├── CITATION.cff ├── docs ├── _figures │ └── empty.txt ├── _build │ ├── html │ │ ├── _static │ │ │ ├── empty.txt │ │ │ ├── file.png │ │ │ ├── minus.png │ │ │ ├── plus.png │ │ │ ├── favicon.png │ │ │ ├── quadrant_logo.png │ │ │ ├── quadrant_thumbnail.png │ │ │ ├── css │ │ │ │ ├── fonts │ │ │ │ │ ├── lato-bold.woff │ │ │ │ │ ├── lato-bold.woff2 │ │ │ │ │ ├── lato-normal.woff │ │ │ │ │ ├── lato-normal.woff2 │ │ │ │ │ ├── Roboto-Slab-Bold.woff │ │ │ │ │ ├── Roboto-Slab-Bold.woff2 │ │ │ │ │ ├── lato-bold-italic.woff │ │ │ │ │ ├── lato-bold-italic.woff2 │ │ │ │ │ ├── Roboto-Slab-Regular.woff │ │ │ │ │ ├── Roboto-Slab-Regular.woff2 │ │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ │ ├── fontawesome-webfont.woff2 │ │ │ │ │ ├── lato-normal-italic.woff │ │ │ │ │ └── lato-normal-italic.woff2 │ │ │ │ └── badge_only.css │ │ │ ├── documentation_options.js │ │ │ ├── js │ │ │ │ ├── badge_only.js │ │ │ │ ├── html5shiv.min.js │ │ │ │ ├── html5shiv-printshiv.min.js │ │ │ │ └── theme.js │ │ │ ├── pygments.css │ │ │ ├── doctools.js │ │ │ └── language_data.js │ │ ├── objects.inv │ │ ├── _images │ │ │ └── quadrant_logo.png │ │ ├── .buildinfo │ │ ├── _sources │ │ │ ├── docs_firststep.rst.txt │ │ │ ├── docs_install.rst.txt │ │ │ ├── docs_functions.rst.txt │ │ │ └── index.rst.txt │ │ ├── searchindex.js │ │ ├── search.html │ │ ├── py-modindex.html │ │ ├── docs_install.html │ │ ├── docs_firststep.html │ │ ├── index.html │ │ └── genindex.html │ └── doctrees │ │ ├── index.doctree │ │ ├── environment.pickle │ │ ├── docs_install.doctree │ │ ├── docs_firststep.doctree │ │ └── docs_functions.doctree ├── requirements.txt ├── _static │ ├── favicon.png │ ├── quadrant_logo.png │ └── quadrant_thumbnail.png ├── Makefile ├── make.bat ├── docs_firststep.rst ├── docs_install.rst ├── docs_functions.rst ├── index.rst └── conf.py ├── source ├── __pycache__ │ ├── accel.cpython-38.pyc │ ├── atmos.cpython-38.pyc │ ├── deputy.cpython-38.pyc │ ├── forces.cpython-38.pyc │ ├── anomaly.cpython-38.pyc │ ├── dcmrotx.cpython-38.pyc │ ├── dcmrotz.cpython-38.pyc │ ├── feedback.cpython-38.pyc │ ├── rotation.cpython-38.pyc │ ├── atmosphere.cpython-38.pyc │ ├── attitudes.cpython-38.pyc │ ├── formation.cpython-38.pyc │ ├── integrate.cpython-38.pyc │ ├── keplerian.cpython-38.pyc │ ├── propagate.cpython-38.pyc │ ├── spacecraft.cpython-38.pyc │ └── targeting.cpython-38.pyc ├── integrate.py ├── forces.py ├── rotation.py ├── atmosphere.py ├── formation.py ├── anomaly.py ├── feedback.py ├── deputy.py └── targeting.py ├── scenarios └── pointing_policy │ ├── Raw Data │ ├── processed.xlsx │ ├── raw_policy4A_forward_d2.txt │ ├── raw_policy4B_forward_d3.txt │ ├── histogram.csv │ └── raw_policy3_lookahead.txt │ ├── ephemeris.csv │ ├── plot_relative_orbits.py │ ├── plot_demo_results.py │ ├── policy1_random.py │ └── policy2a_greedy_angle.py ├── .readthedocs.yaml ├── LICENSE ├── pyproject.toml └── README.rst /CITATION.cff: -------------------------------------------------------------------------------- 1 | empty -------------------------------------------------------------------------------- /docs/_figures/empty.txt: -------------------------------------------------------------------------------- 1 | hello! -------------------------------------------------------------------------------- /docs/_build/html/_static/empty.txt: -------------------------------------------------------------------------------- 1 | hello! -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | matplotlib>=3.3.4 2 | numpy>=1.20.1 3 | -------------------------------------------------------------------------------- /docs/_static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_static/favicon.png -------------------------------------------------------------------------------- /docs/_build/html/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/objects.inv -------------------------------------------------------------------------------- /docs/_static/quadrant_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_static/quadrant_logo.png -------------------------------------------------------------------------------- /docs/_build/doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/doctrees/index.doctree -------------------------------------------------------------------------------- /docs/_build/html/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/file.png -------------------------------------------------------------------------------- /docs/_build/html/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/minus.png -------------------------------------------------------------------------------- /docs/_build/html/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/plus.png -------------------------------------------------------------------------------- /docs/_static/quadrant_thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_static/quadrant_thumbnail.png -------------------------------------------------------------------------------- /docs/_build/html/_static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/favicon.png -------------------------------------------------------------------------------- /docs/_build/doctrees/environment.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/doctrees/environment.pickle -------------------------------------------------------------------------------- /source/__pycache__/accel.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/source/__pycache__/accel.cpython-38.pyc -------------------------------------------------------------------------------- /source/__pycache__/atmos.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/source/__pycache__/atmos.cpython-38.pyc -------------------------------------------------------------------------------- /source/__pycache__/deputy.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/source/__pycache__/deputy.cpython-38.pyc -------------------------------------------------------------------------------- /source/__pycache__/forces.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/source/__pycache__/forces.cpython-38.pyc -------------------------------------------------------------------------------- /docs/_build/doctrees/docs_install.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/doctrees/docs_install.doctree -------------------------------------------------------------------------------- /docs/_build/html/_images/quadrant_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_images/quadrant_logo.png -------------------------------------------------------------------------------- /docs/_build/html/_static/quadrant_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/quadrant_logo.png -------------------------------------------------------------------------------- /source/__pycache__/anomaly.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/source/__pycache__/anomaly.cpython-38.pyc -------------------------------------------------------------------------------- /source/__pycache__/dcmrotx.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/source/__pycache__/dcmrotx.cpython-38.pyc -------------------------------------------------------------------------------- /source/__pycache__/dcmrotz.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/source/__pycache__/dcmrotz.cpython-38.pyc -------------------------------------------------------------------------------- /source/__pycache__/feedback.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/source/__pycache__/feedback.cpython-38.pyc -------------------------------------------------------------------------------- /source/__pycache__/rotation.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/source/__pycache__/rotation.cpython-38.pyc -------------------------------------------------------------------------------- /docs/_build/doctrees/docs_firststep.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/doctrees/docs_firststep.doctree -------------------------------------------------------------------------------- /docs/_build/doctrees/docs_functions.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/doctrees/docs_functions.doctree -------------------------------------------------------------------------------- /source/__pycache__/atmosphere.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/source/__pycache__/atmosphere.cpython-38.pyc -------------------------------------------------------------------------------- /source/__pycache__/attitudes.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/source/__pycache__/attitudes.cpython-38.pyc -------------------------------------------------------------------------------- /source/__pycache__/formation.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/source/__pycache__/formation.cpython-38.pyc -------------------------------------------------------------------------------- /source/__pycache__/integrate.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/source/__pycache__/integrate.cpython-38.pyc -------------------------------------------------------------------------------- /source/__pycache__/keplerian.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/source/__pycache__/keplerian.cpython-38.pyc -------------------------------------------------------------------------------- /source/__pycache__/propagate.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/source/__pycache__/propagate.cpython-38.pyc -------------------------------------------------------------------------------- /source/__pycache__/spacecraft.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/source/__pycache__/spacecraft.cpython-38.pyc -------------------------------------------------------------------------------- /source/__pycache__/targeting.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/source/__pycache__/targeting.cpython-38.pyc -------------------------------------------------------------------------------- /docs/_build/html/_static/quadrant_thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/quadrant_thumbnail.png -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/lato-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/css/fonts/lato-bold.woff -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/lato-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/css/fonts/lato-bold.woff2 -------------------------------------------------------------------------------- /scenarios/pointing_policy/Raw Data/processed.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/scenarios/pointing_policy/Raw Data/processed.xlsx -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/lato-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/css/fonts/lato-normal.woff -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/lato-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/css/fonts/lato-normal.woff2 -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/lato-bold-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/css/fonts/lato-bold-italic.woff -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/lato-bold-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/css/fonts/lato-bold-italic.woff2 -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/css/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/css/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/css/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/css/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/lato-normal-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/css/fonts/lato-normal-italic.woff -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/lato-normal-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammmlow/quadrant/HEAD/docs/_build/html/_static/css/fonts/lato-normal-italic.woff2 -------------------------------------------------------------------------------- /docs/_build/html/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 9d629f76aff6b989fb131be6260f980b 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # File: .readthedocs.yaml 2 | 3 | version: 2 4 | 5 | # Configuration of the Python environment to be used 6 | python: 7 | install: 8 | - requirements: docs/requirements.txt 9 | - method: pip 10 | path: . 11 | 12 | # Configuration for Sphinx documentation 13 | sphinx: 14 | configuration: docs/conf.py 15 | fail_on_warning: true 16 | -------------------------------------------------------------------------------- /docs/_build/html/_static/documentation_options.js: -------------------------------------------------------------------------------- 1 | var DOCUMENTATION_OPTIONS = { 2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), 3 | VERSION: '0.0', 4 | LANGUAGE: 'None', 5 | COLLAPSE_INDEX: false, 6 | BUILDER: 'html', 7 | FILE_SUFFIX: '.html', 8 | LINK_SUFFIX: '.html', 9 | HAS_SOURCE: true, 10 | SOURCELINK_SUFFIX: '.txt', 11 | NAVIGATION_WITH_KEYS: false 12 | }; -------------------------------------------------------------------------------- /scenarios/pointing_policy/Raw Data/raw_policy4A_forward_d2.txt: -------------------------------------------------------------------------------- 1 | 2752.0 2 | 2854.0 3 | 2865.0 4 | 2819.0 5 | 4038.0 6 | 4849.0 7 | 3289.0 8 | 2843.0 9 | 2802.0 10 | 4457.0 11 | 2684.0 12 | 2505.0 13 | 2703.0 14 | 4193.0 15 | 2883.0 16 | 3509.0 17 | 3948.0 18 | 2634.0 19 | 2693.0 20 | 2846.0 21 | 2837.0 22 | 2795.0 23 | 2923.0 24 | 3430.0 25 | 3554.0 26 | 4366.0 27 | 2878.0 28 | 4456.0 29 | 4053.0 30 | 2811.0 31 | 3243.0 32 | 3551.0 33 | 2901.0 34 | 2718.0 35 | 3924.0 36 | 2580.0 37 | 3122.0 38 | 3524.0 39 | 2881.0 40 | 3070.0 41 | 3034.0 42 | 4108.0 43 | 2809.0 44 | 2785.0 45 | 2841.0 46 | 2546.0 47 | 4120.0 48 | 2827.0 49 | 2849.0 50 | 2666.0 51 | -------------------------------------------------------------------------------- /scenarios/pointing_policy/Raw Data/raw_policy4B_forward_d3.txt: -------------------------------------------------------------------------------- 1 | 4369.0 2 | 4270.0 3 | 3295.0 4 | 2940.0 5 | 3824.0 6 | 2708.0 7 | 4138.0 8 | 2795.0 9 | 4138.0 10 | 2696.0 11 | 2710.0 12 | 3057.0 13 | 3990.0 14 | 2888.0 15 | 3309.0 16 | 2828.0 17 | 3809.0 18 | 2806.0 19 | 2814.0 20 | 3605.0 21 | 4502.0 22 | 2959.0 23 | 2528.0 24 | 2740.0 25 | 2839.0 26 | 2872.0 27 | 2697.0 28 | 2658.0 29 | 2559.0 30 | 3788.0 31 | 2999.0 32 | 2699.0 33 | 2891.0 34 | 2906.0 35 | 2406.0 36 | 3104.0 37 | 3569.0 38 | 4055.0 39 | 2821.0 40 | 2834.0 41 | 2823.0 42 | 2585.0 43 | 2585.0 44 | 3974.0 45 | 2687.0 46 | 2384.0 47 | 2815.0 48 | 3008.0 49 | 3366.0 50 | 2539.0 51 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /scenarios/pointing_policy/ephemeris.csv: -------------------------------------------------------------------------------- 1 | Header, a, e, i, w, R, M 2 | Chief, 6978.140000, 0.000000, 60.000000, 90.000000, 90.000000, 45.000000 3 | Deputy1, 6978.140000, 0.000717, 60.000000, 0.000000, 90.094810, 134.952386 4 | Deputy2, 6978.140000, 0.000573, 60.065686, -90.000000, 90.000000, -135.002843 5 | Deputy3, 6978.140000, 0.000430, 60.000000, -180.000000, 89.943114, -44.973084 6 | Deputy4, 6978.140000, 0.000287, 59.967157, 90.000000, 90.000000, 45.001402 7 | Deputy5, 6978.140000, 0.000717, 60.000000, 0.000000, 89.905190, 135.050629 8 | Deputy6, 6978.140000, 0.000573, 60.065686, 90.000000, 90.000000, 45.002792 9 | Deputy7, 6978.140000, 0.000430, 60.000000, 180.000000, 90.056886, -45.027010 10 | Deputy8, 6978.140000, 0.000287, 59.967157, -90.000000, 90.000000, -134.997308 -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/_build/html/_static/js/badge_only.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t,r){}}); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Samuel Low 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/docs_firststep.rst: -------------------------------------------------------------------------------- 1 | .. 2 | ############################################################################ 3 | ############################################################################ 4 | ## ## 5 | ## ___ _ _ __ ____ ____ __ _ _ _____ ## 6 | ## / _ \| | | | / \ | _ \| __ \ / \ | \ | |_ _| ## 7 | ## ( |_| ) |_| |/ /\ \| |_| | -/ // /\ \| \| | | | ## 8 | ## \_ /|_____| /--\ |____/|_|\_\ /--\ |_\___| |_| ## 9 | ## \/ v 0.0 ## 10 | ## ## 11 | ############################################################################ 12 | ############################################################################ 13 | 14 | .. image:: /_static/quadrant_logo.png 15 | 16 | | 17 | 18 | First steps 19 | =========== 20 | 21 | Lorem ipsum. 22 | 23 | .. note:: **Lorem ipsum:** Lorem ipsum. 24 | copy, datetime, decimal, math, os, pathlib, tkinter, urllib, warnings 25 | -------------------------------------------------------------------------------- /docs/docs_install.rst: -------------------------------------------------------------------------------- 1 | .. 2 | ############################################################################ 3 | ############################################################################ 4 | ## ## 5 | ## ___ _ _ __ ____ ____ __ _ _ _____ ## 6 | ## / _ \| | | | / \ | _ \| __ \ / \ | \ | |_ _| ## 7 | ## ( |_| ) |_| |/ /\ \| |_| | -/ // /\ \| \| | | | ## 8 | ## \_ /|_____| /--\ |____/|_|\_\ /--\ |_\___| |_| ## 9 | ## \/ v 0.0 ## 10 | ## ## 11 | ############################################################################ 12 | ############################################################################ 13 | 14 | .. image:: /_static/quadrant_logo.png 15 | 16 | | 17 | 18 | Installation 19 | ============ 20 | 21 | Lorem ipsum. 22 | 23 | .. note:: **Lorem ipsum:** Lorem ipsum. 24 | copy, datetime, decimal, math, os, pathlib, tkinter, urllib, warnings 25 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/docs_firststep.rst.txt: -------------------------------------------------------------------------------- 1 | .. 2 | ############################################################################ 3 | ############################################################################ 4 | ## ## 5 | ## ___ _ _ __ ____ ____ __ _ _ _____ ## 6 | ## / _ \| | | | / \ | _ \| __ \ / \ | \ | |_ _| ## 7 | ## ( |_| ) |_| |/ /\ \| |_| | -/ // /\ \| \| | | | ## 8 | ## \_ /|_____| /--\ |____/|_|\_\ /--\ |_\___| |_| ## 9 | ## \/ v 0.0 ## 10 | ## ## 11 | ############################################################################ 12 | ############################################################################ 13 | 14 | .. image:: /_static/quadrant_logo.png 15 | 16 | | 17 | 18 | First steps 19 | =========== 20 | 21 | Lorem ipsum. 22 | 23 | .. note:: **Lorem ipsum:** Lorem ipsum. 24 | copy, datetime, decimal, math, os, pathlib, tkinter, urllib, warnings 25 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/docs_install.rst.txt: -------------------------------------------------------------------------------- 1 | .. 2 | ############################################################################ 3 | ############################################################################ 4 | ## ## 5 | ## ___ _ _ __ ____ ____ __ _ _ _____ ## 6 | ## / _ \| | | | / \ | _ \| __ \ / \ | \ | |_ _| ## 7 | ## ( |_| ) |_| |/ /\ \| |_| | -/ // /\ \| \| | | | ## 8 | ## \_ /|_____| /--\ |____/|_|\_\ /--\ |_\___| |_| ## 9 | ## \/ v 0.0 ## 10 | ## ## 11 | ############################################################################ 12 | ############################################################################ 13 | 14 | .. image:: /_static/quadrant_logo.png 15 | 16 | | 17 | 18 | Installation 19 | ============ 20 | 21 | Lorem ipsum. 22 | 23 | .. note:: **Lorem ipsum:** Lorem ipsum. 24 | copy, datetime, decimal, math, os, pathlib, tkinter, urllib, warnings 25 | -------------------------------------------------------------------------------- /scenarios/pointing_policy/Raw Data/histogram.csv: -------------------------------------------------------------------------------- 1 | 500,0,0,0,0,0,0 2 | 750,0,0,0,0,0,0 3 | 1000,0,0,0,0,0,0 4 | 1250,0,0,0,0,0,0 5 | 1500,0,0,0,0,0,0 6 | 1750,0,0,0,0,0,0 7 | 2000,0,0,0,1,0,0 8 | 2250,0,0,0,0,0,0 9 | 2500,0,0,1,2,0,40 10 | 2750,0,1,10,60,180,260 11 | 3000,4,3,40,254,400,320 12 | 3250,10,13,55,205,80,60 13 | 3500,14,22,40,76,40,60 14 | 3750,21,17,27,55,80,40 15 | 4000,18,25,33,45,40,100 16 | 4250,14,24,44,78,100,60 17 | 4500,23,22,41,51,60,40 18 | 4750,21,16,24,53,0,20 19 | 5000,14,30,33,35,20,0 20 | 5250,27,28,46,28,0,0 21 | 5500,35,36,46,16,0,0 22 | 5750,28,30,45,11,0,0 23 | 6000,37,48,40,2,0,0 24 | 6250,38,37,21,2,0,0 25 | 6500,32,32,23,2,0,0 26 | 6750,41,32,34,0,0,0 27 | 7000,37,36,35,2,0,0 28 | 7250,49,50,37,1,0,0 29 | 7500,46,51,29,2,0,0 30 | 7750,44,43,24,10,0,0 31 | 8000,43,31,30,6,0,0 32 | 8250,49,40,33,3,0,0 33 | 8500,33,37,19,0,0,0 34 | 8750,42,31,18,0,0,0 35 | 9000,41,28,21,0,0,0 36 | 9250,23,26,19,0,0,0 37 | 9500,18,21,8,0,0,0 38 | 9750,23,23,15,0,0,0 39 | 10000,16,19,8,0,0,0 40 | 10250,21,18,16,0,0,0 41 | 10500,21,15,12,0,0,0 42 | 10750,19,12,16,0,0,0 43 | 11000,14,12,5,0,0,0 44 | 11250,9,11,8,0,0,0 45 | 11500,4,15,4,0,0,0 46 | 11750,9,5,4,0,0,0 47 | 12000,5,5,5,0,0,0 48 | 12250,8,5,4,0,0,0 49 | 12500,8,4,5,0,0,0 50 | 12750,7,4,3,0,0,0 51 | 13000,4,8,3,0,0,0 52 | 13250,8,7,2,0,0,0 53 | 13500,5,3,4,0,0,0 54 | 13750,2,1,1,0,0,0 55 | 14000,3,6,0,0,0,0 56 | 14250,0,2,2,0,0,0 57 | 14500,0,3,2,0,0,0 58 | 14750,0,2,0,0,0,0 59 | 15000,1,0,0,0,0,0 60 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "quadrant" 3 | readme = "README.md" 4 | requires-python = ">=3.6" 5 | license = {file = "LICENSE"} 6 | authors = [ 7 | {name = "Samuel Y. W. Low", email = "sammmlow@gmail.com"} 8 | ] 9 | 10 | keywords = [ 11 | "pid", 12 | "aero", 13 | "adcs", 14 | "gyro", 15 | "astro", 16 | "orbits", 17 | "forces", 18 | "wheels", 19 | "moments", 20 | "control", 21 | "reaction", 22 | "Lyapunov", 23 | "attitude", 24 | "pointing", 25 | "aerospace", 26 | "satellite", 27 | "spacecraft", 28 | "engineering", 29 | "astrodynamics" 30 | ] 31 | 32 | classifiers = [ 33 | "License :: OSI Approved :: MIT License", 34 | "Development Status :: 4 - Beta", 35 | "Intended Audience :: Education", 36 | "Intended Audience :: Science/Research", 37 | "License :: OSI Approved :: MIT License", 38 | "Operating System :: OS Independent", 39 | "Programming Language :: Python", 40 | "Programming Language :: Python :: 3", 41 | "Programming Language :: Python :: 3.5", 42 | "Programming Language :: Python :: 3.6", 43 | "Programming Language :: Python :: 3.7", 44 | "Programming Language :: Python :: 3.8", 45 | "Programming Language :: Python :: 3.9", 46 | "Programming Language :: Python :: 3.10", 47 | "Programming Language :: Python :: Implementation :: CPython", 48 | "Topic :: Scientific/Engineering", 49 | "Topic :: Scientific/Engineering :: Physics", 50 | "Topic :: Scientific/Engineering :: Navigation", 51 | "Topic :: Scientific/Engineering :: Astronomy" 52 | ] 53 | 54 | dependencies = [ 55 | "numpy", 56 | "matplotlib >=2.0" 57 | ] -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://raw.githubusercontent.com/sammmlow/quadrant/main/docs/_static/quadrant_logo.png 2 | 3 | .. |docs| image:: https://img.shields.io/badge/docs-latest-brightgreen.svg?style=flat-square 4 | :target: https://readthedocs.org/ 5 | 6 | .. |license| image:: https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square 7 | :target: https://github.com/sammmlow/quadrant/blob/main/LICENSE 8 | 9 | .. |orcid| image:: https://img.shields.io/badge/ID-0000--0002--1911--701X-a6ce39.svg 10 | :target: https://orcid.org/0000-0002-1911-701X/ 11 | 12 | .. |linkedin| image:: https://img.shields.io/badge/LinkedIn-sammmlow-blue.svg 13 | :target: https://www.linkedin.com/in/sammmlow 14 | 15 | :Project: QUADRANT 16 | :Github: https://github.com/sammmlow/quadrant 17 | :Documents: https://quadrant.readthedocs.io/en/latest/ 18 | :Version: 0.0 (Unreleased) 19 | 20 | |docs| |license| 21 | 22 | :Author: Samuel Y. W. Low 23 | 24 | |linkedin| |orcid| 25 | 26 | QUADRANT 27 | ------ 28 | 29 | A Python application that allows for attitude control simulations, for formation flying satellites. 30 | 31 | **Key (Intended) Features** 32 | 33 | Three different attitude coordinates 34 | 35 | - Quarternions 36 | - Classical Rodrigues Parameters 37 | - Modified Rodrigues Parameters 38 | 39 | Four different control scenarios 40 | 41 | - Nadir Pointing 42 | - Sun Pointing 43 | - Inter-satellite Pointing 44 | - Regulation Mode (De-Tumbling) 45 | 46 | **STILL WORK IN PROGRESS AND NOT STABLE FOR RELEASE YET** 47 | 48 | 49 | 50 | Contact 51 | ------- 52 | 53 | If you have any queries feel free to reach out to me at: 54 | 55 | sammmlow@gmail.com 56 | 57 | |linkedin| |orcid| 58 | 59 | *Last Modified on 17-04-2021* -------------------------------------------------------------------------------- /docs/docs_functions.rst: -------------------------------------------------------------------------------- 1 | .. 2 | ############################################################################ 3 | ############################################################################ 4 | ## ## 5 | ## ___ _ _ __ ____ ____ __ _ _ _____ ## 6 | ## / _ \| | | | / \ | _ \| __ \ / \ | \ | |_ _| ## 7 | ## ( |_| ) |_| |/ /\ \| |_| | -/ // /\ \| \| | | | ## 8 | ## \_ /|_____| /--\ |____/|_|\_\ /--\ |_\___| |_| ## 9 | ## \/ v 0.0 ## 10 | ## ## 11 | ############################################################################ 12 | ############################################################################ 13 | 14 | .. image:: /_static/quadrant_logo.png 15 | 16 | | 17 | 18 | Function Reference 19 | ================== 20 | 21 | The order of functions in this API reference goes according to the chronological order of which they are called in the native LEOGPS processing work flow (see previous page). Functions that are currently not in use in the current native work flow are listed at the end of this page. 22 | 23 | #### 24 | 25 | spacecraft.py 26 | ------------- 27 | 28 | Spacecraft object 29 | 30 | .. automodule:: spacecraft 31 | :members: 32 | 33 | #### 34 | 35 | attitudes.py 36 | ------------ 37 | 38 | Attitude file containing attitude classes (quaternions, classical rodrigues parameters and modified rodrigues parameters). 39 | 40 | .. automodule:: attitudes 41 | :members: QTR, CRP, MRP 42 | 43 | #### 44 | 45 | This API reference was automatically generated using Sphinx' Autodoc feature, using the `NumPy docstring format `_, and last updated on 11th September 2021. 46 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/docs_functions.rst.txt: -------------------------------------------------------------------------------- 1 | .. 2 | ############################################################################ 3 | ############################################################################ 4 | ## ## 5 | ## ___ _ _ __ ____ ____ __ _ _ _____ ## 6 | ## / _ \| | | | / \ | _ \| __ \ / \ | \ | |_ _| ## 7 | ## ( |_| ) |_| |/ /\ \| |_| | -/ // /\ \| \| | | | ## 8 | ## \_ /|_____| /--\ |____/|_|\_\ /--\ |_\___| |_| ## 9 | ## \/ v 0.0 ## 10 | ## ## 11 | ############################################################################ 12 | ############################################################################ 13 | 14 | .. image:: /_static/quadrant_logo.png 15 | 16 | | 17 | 18 | Function Reference 19 | ================== 20 | 21 | The order of functions in this API reference goes according to the chronological order of which they are called in the native LEOGPS processing work flow (see previous page). Functions that are currently not in use in the current native work flow are listed at the end of this page. 22 | 23 | #### 24 | 25 | spacecraft.py 26 | ------------- 27 | 28 | Spacecraft object 29 | 30 | .. automodule:: spacecraft 31 | :members: 32 | 33 | #### 34 | 35 | attitudes.py 36 | ------------ 37 | 38 | Attitude file containing attitude classes (quaternions, classical rodrigues parameters and modified rodrigues parameters). 39 | 40 | .. automodule:: attitudes 41 | :members: QTR, CRP, MRP 42 | 43 | #### 44 | 45 | This API reference was automatically generated using Sphinx' Autodoc feature, using the `NumPy docstring format `_, and last updated on 11th September 2021. 46 | -------------------------------------------------------------------------------- /source/integrate.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Dec 17 14:36:49 2021 4 | 5 | @author: sammm 6 | """ 7 | 8 | import numpy as np 9 | 10 | from source import forces 11 | 12 | def RK4( sc, dt ): 13 | '''Orbit propagator for one step, using Runge-Kutta 4th Order (3/8 Rule) 14 | 15 | Parameters 16 | ---------- 17 | dt : integer 18 | Time step size (s) 19 | pos : numpy.ndarray 20 | Inertial frame position vector (1x3) of the spacecraft (km) 21 | vel : numpy.ndarray 22 | Inertial frame velocity vector (1x3) of the spacecraft (km/s) 23 | Cd : float 24 | Drag coefficient of the spacecraft 25 | Ar : float 26 | Drag area of the spacecraft (m^2) 27 | Ms : float 28 | Mass of the spacecraft (kg) 29 | fJ : bool 30 | Flag to toggle J2 perturbation (True to toggle on) 31 | fD : bool 32 | Flag to toggle atmospheric drag (True to toggle on) 33 | 34 | Returns 35 | ------- 36 | posf : numpy.ndarray 37 | Final position vector (1x3) of the spacecraft (km) 38 | velf : numpy.ndarray 39 | Final velocity vector (1x3) of the spacecraft (km/s) 40 | 41 | ''' 42 | 43 | c = 1.0/3.0 44 | pos, vel = np.array(sc.states[:3]), np.array(sc.states[3:]) 45 | 46 | # K1 47 | k1p = vel 48 | k1v = forces.forces( pos, vel, sc ) 49 | 50 | # K2 51 | k2p = vel + dt * (c*k1v) 52 | k2v = forces.forces( pos + dt*(c*k1p), vel + dt*(c*k1v), sc ) 53 | 54 | # K3 55 | k3p = vel + dt * (k2v-c*k1v) 56 | k3v = forces.forces( pos + dt*(k2p-c*k1p), vel + dt*(k2v-c*k1v), sc ) 57 | 58 | # K4 59 | k4p = vel + dt * (k1v-k2v+k3v) 60 | k4v = forces.forces( pos + dt*(k1p-k2p+k3p), vel + dt*(k1v-k2v+k3v), sc ) 61 | 62 | # Simpson's Rule variant to RK4 update step 63 | posf = pos + (dt/8) * (k1p + 3*k2p + 3*k3p + k4p) 64 | velf = vel + (dt/8) * (k1v + 3*k2v + 3*k3v + k4v) 65 | 66 | sc.states = list(posf) + list(velf) 67 | 68 | return sc 69 | -------------------------------------------------------------------------------- /source/forces.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Nov 6 22:08:12 2021 4 | 5 | @author: sammm 6 | """ 7 | 8 | import numpy as np 9 | from source import atmosphere 10 | 11 | def forces( pos, vel, sc ): 12 | '''Computation of the total inertial acceleration as a 1x3 vector, from 13 | Earth's gravity; optionally the J2 perturbation force, and drag force via 14 | the US Standard Atmosphere 1976. 15 | 16 | Parameters 17 | ---------- 18 | sc : numpy.ndarray 19 | xxx 20 | 21 | Returns 22 | ------- 23 | acceleration : numpy.ndarray 24 | Inertial frame acceleration vector (1x3) of the spacecraft (km/s^2) 25 | 26 | ''' 27 | 28 | # Retrieve all parameters from the spacecraft. 29 | GM = sc.GM 30 | Cd = sc.Cd 31 | 32 | # Define all constants 33 | RE = 6378.140 # Earth equatorial radius (km) 34 | GM = 398600.4418 # G * Earth Mass (km**3/s**2) 35 | J2 = 1.0826267e-3 # J2 constant 36 | 37 | # Get the radial distance of the satellite. 38 | R = np.linalg.norm( pos ) # km 39 | V = np.linalg.norm( vel ) # km/s 40 | 41 | # Initialise the acceleration vector. 42 | acceleration = np.zeros(3) 43 | 44 | # Compute the two-body gravitational force by Earth. 45 | if sc.forces['Earth Twobody'] == True: 46 | acceleration += ( -1 * GM * pos ) / ( R**3 ) 47 | 48 | # Include the additional J2 acceleration vector if necessary. 49 | if sc.forces['Earth Oblate J2'] == True: 50 | R_J2 = 1.5 * J2 * GM * ((RE**2)/(R**5)) 51 | zRatio = (pos[2]/R)**2 52 | oblate_x = R_J2 * pos[0] * (5 * zRatio-1) 53 | oblate_y = R_J2 * pos[1] * (5 * zRatio-1) 54 | oblate_z = R_J2 * pos[2] * (5 * zRatio-3) 55 | acceleration += np.array([oblate_x, oblate_y, oblate_z]) 56 | 57 | # Include the additional drag acceleration if necessary. 58 | if sc.forces['Earth Atmos Drag'] == True: 59 | areaMassRatio = sc.area / sc.mass # m**2/kg 60 | dragDensity = atmosphere.density( (R - RE) ) # kg/m**3 61 | dragAccel = 0.5 * Cd * dragDensity * areaMassRatio * ((V*1000)**2) 62 | acceleration -= dragAccel * ( vel / V ) / 1000 63 | 64 | # Acceleration vector is in km/s**2 65 | return acceleration 66 | -------------------------------------------------------------------------------- /docs/_build/html/searchindex.js: -------------------------------------------------------------------------------- 1 | Search.setIndex({docnames:["docs_firststep","docs_functions","docs_install","index"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":3,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":3,"sphinx.domains.rst":2,"sphinx.domains.std":2,sphinx:56},filenames:["docs_firststep.rst","docs_functions.rst","docs_install.rst","index.rst"],objects:{"":{attitudes:[1,0,0,"-"],spacecraft:[1,0,0,"-"]},"spacecraft.Spacecraft":{Cd:[1,2,1,""],GM:[1,2,1,""],area:[1,2,1,""],elements:[1,2,1,""],mass:[1,2,1,""],name:[1,2,1,""],states:[1,2,1,""]},attitudes:{QTR:[1,1,1,""]},spacecraft:{Spacecraft:[1,1,1,""]}},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","attribute","Python attribute"]},objtypes:{"0":"py:module","1":"py:class","2":"py:attribute"},terms:{"0":[1,3],"1":1,"11th":1,"16":1,"18":1,"1x4":1,"2":1,"2021":1,"23":1,"3":1,"36":1,"3x3":1,"6":1,"class":1,"default":1,"float":1,"function":3,A:1,By:3,For:3,The:[1,3],accord:1,agent:3,an:3,angular:1,api:1,ar:1,area:1,arrai:1,attbn:1,attbr:1,attitud:3,attribut:1,author:[1,3],autodoc:1,automat:1,below:3,bug:3,call:1,can:1,cartesian:1,cd:1,chronolog:1,classic:1,coeffici:1,collabor:3,com:3,constant:1,contain:1,control:3,copi:[0,2],cosin:1,creat:1,current:1,custom:1,datetim:[0,2],dcm:1,decim:[0,2],descript:1,dimensionless:1,direct:1,docstr:1,document:3,drag:1,e:1,either:1,element:1,en:3,end:1,epoch:1,featur:1,file:1,first:3,flow:1,forc:1,format:1,gener:1,github:3,gm:1,gmail:3,goe:1,gravit:1,http:3,i:1,initialis:1,instal:3,io:3,ipsum:[0,2],issu:3,keplerian:1,kg:1,km:1,last:1,latest:3,leogp:1,librari:3,licens:3,list:1,lorem:[0,2],low:3,m:1,mass:1,math:[0,2],me:3,mit:3,modifi:1,multi:3,name:1,nativ:1,none:1,nov:1,numpi:1,object:1,ohmbn:1,ohmbr:1,option:1,order:1,os:[0,2],out:3,page:1,paramet:1,pathlib:[0,2],planet:1,previou:1,process:1,product:1,progress:3,project:3,px:1,pz:1,qtr:1,quaternion:1,r:1,rais:3,reach:3,readthedoc:3,repositori:3,rodrigu:1,s:1,sammm:1,sammmlow:3,samuel:3,see:1,septemb:1,simul:3,spacecraft:3,sphinx:1,state:1,step:3,str:1,thei:1,thi:1,tkinter:[0,2],tree:3,trial:3,tue:1,type:1,under:3,updat:1,urllib:[0,2],us:1,veloc:1,version:3,vx:1,vy:1,vz:1,w:[1,3],wa:1,warn:[0,2],which:1,work:[1,3],written:3,y:3},titles:["First steps","Function Reference","Installation","QUADRANT"],titleterms:{"function":1,advanc:3,attitud:1,first:0,get:3,instal:2,py:1,quadrant:3,refer:[1,3],spacecraft:1,start:3,step:0}}) -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. 2 | ############################################################################ 3 | ############################################################################ 4 | ## ## 5 | ## ___ _ _ __ ____ ____ __ _ _ _____ ## 6 | ## / _ \| | | | / \ | _ \| __ \ / \ | \ | |_ _| ## 7 | ## ( |_| ) |_| |/ /\ \| |_| | -/ // /\ \| \| | | | ## 8 | ## \_ /|_____| /--\ |____/|_|\_\ /--\ |_\___| |_| ## 9 | ## \/ v 0.0 ## 10 | ## ## 11 | ############################################################################ 12 | ############################################################################ 13 | 14 | .. |docs| image:: https://img.shields.io/badge/docs-latest-brightgreen.svg?style=flat-square 15 | :target: https://quadrant.readthedocs.io/en/latest/ 16 | 17 | .. |license| image:: https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square 18 | :target: https://github.com/sammmlow/quadrant/blob/master/LICENSE 19 | 20 | .. |orcid| image:: https://img.shields.io/badge/ID-0000--0002--1911--701X-a6ce39.svg 21 | :target: https://orcid.org/0000-0002-1911-701X/ 22 | 23 | .. |linkedin| image:: https://img.shields.io/badge/LinkedIn-sammmlow-blue.svg 24 | :target: https://www.linkedin.com/in/sammmlow 25 | 26 | .. image:: /_static/quadrant_logo.png 27 | 28 | | 29 | 30 | :Github: https://github.com/sammmlow/quadrant 31 | :Documents: https://quadrant.readthedocs.io/en/latest/ 32 | :Version: 0.0 (Trial) 33 | :Author: Samuel Y. W. Low 34 | 35 | |docs| |license| |linkedin| |orcid| 36 | 37 | QUADRANT 38 | ======== 39 | 40 | QUADRANT is an attitude control simulation library for multi-agent spacecraft, work in progress. 41 | 42 | Documentation tree below. 43 | 44 | .. toctree:: 45 | :maxdepth: 1 46 | :caption: Getting Started 47 | 48 | docs_install.rst 49 | docs_firststep.rst 50 | 51 | .. toctree:: 52 | :maxdepth: 1 53 | :caption: Advanced References 54 | 55 | docs_functions.rst 56 | 57 | .. toctree:: 58 | :maxdepth: 1 59 | :caption: Extras 60 | 61 | For bugs, raise the issues in the `GitHub repository `_. For collaborations, reach out to me (sammmlow@gmail.com). The project is licensed under the MIT license. 62 | 63 | *Written By: Samuel Y. W. Low* 64 | 65 | |linkedin| |orcid| 66 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/index.rst.txt: -------------------------------------------------------------------------------- 1 | .. 2 | ############################################################################ 3 | ############################################################################ 4 | ## ## 5 | ## ___ _ _ __ ____ ____ __ _ _ _____ ## 6 | ## / _ \| | | | / \ | _ \| __ \ / \ | \ | |_ _| ## 7 | ## ( |_| ) |_| |/ /\ \| |_| | -/ // /\ \| \| | | | ## 8 | ## \_ /|_____| /--\ |____/|_|\_\ /--\ |_\___| |_| ## 9 | ## \/ v 0.0 ## 10 | ## ## 11 | ############################################################################ 12 | ############################################################################ 13 | 14 | .. |docs| image:: https://img.shields.io/badge/docs-latest-brightgreen.svg?style=flat-square 15 | :target: https://quadrant.readthedocs.io/en/latest/ 16 | 17 | .. |license| image:: https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square 18 | :target: https://github.com/sammmlow/quadrant/blob/master/LICENSE 19 | 20 | .. |orcid| image:: https://img.shields.io/badge/ID-0000--0002--1911--701X-a6ce39.svg 21 | :target: https://orcid.org/0000-0002-1911-701X/ 22 | 23 | .. |linkedin| image:: https://img.shields.io/badge/LinkedIn-sammmlow-blue.svg 24 | :target: https://www.linkedin.com/in/sammmlow 25 | 26 | .. image:: /_static/quadrant_logo.png 27 | 28 | | 29 | 30 | :Github: https://github.com/sammmlow/quadrant 31 | :Documents: https://quadrant.readthedocs.io/en/latest/ 32 | :Version: 0.0 (Trial) 33 | :Author: Samuel Y. W. Low 34 | 35 | |docs| |license| |linkedin| |orcid| 36 | 37 | QUADRANT 38 | ======== 39 | 40 | QUADRANT is an attitude control simulation library for multi-agent spacecraft, work in progress. 41 | 42 | Documentation tree below. 43 | 44 | .. toctree:: 45 | :maxdepth: 1 46 | :caption: Getting Started 47 | 48 | docs_install.rst 49 | docs_firststep.rst 50 | 51 | .. toctree:: 52 | :maxdepth: 1 53 | :caption: Advanced References 54 | 55 | docs_functions.rst 56 | 57 | .. toctree:: 58 | :maxdepth: 1 59 | :caption: Extras 60 | 61 | For bugs, raise the issues in the `GitHub repository `_. For collaborations, reach out to me (sammmlow@gmail.com). The project is licensed under the MIT license. 62 | 63 | *Written By: Samuel Y. W. Low* 64 | 65 | |linkedin| |orcid| 66 | -------------------------------------------------------------------------------- /docs/_build/html/_static/js/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); -------------------------------------------------------------------------------- /docs/_build/html/_static/css/badge_only.css: -------------------------------------------------------------------------------- 1 | .fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} -------------------------------------------------------------------------------- /source/rotation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ############################################################################### 4 | ############################################################################### 5 | ## ## 6 | ## ___ _ _ __ ____ ____ __ _ _ _____ ## 7 | ## / _ \| | | | / \ | _ \| __ \ / \ | \ | |_ _| ## 8 | ## ( |_| ) |_| |/ /\ \| |_| | -/ // /\ \| \| | | | ## 9 | ## \_ /|_____| /--\ |____/|_|\_\ /--\ |_\___| |_| ## 10 | ## \/ v 0.0 ## 11 | ## ## 12 | ## FILE DESCRIPTION: ## 13 | ## ## 14 | ## Passive Direction Cosine Matrix Tool Box ## 15 | ## ## 16 | ## Written by Samuel Y. W. Low. ## 17 | ## First created 20-May-2021 12:50 PM (+8 GMT) ## 18 | ## Last modified 20-May-2021 12:50 PM (+8 GMT) ## 19 | ## ## 20 | ############################################################################### 21 | ############################################################################### 22 | 23 | import math 24 | import numpy as np 25 | 26 | ############################################################################### 27 | ############################################################################### 28 | 29 | def dcmX(t): 30 | '''Generate the direction cosine matrix for an X-axis rotation of angle t. 31 | 32 | Parameters 33 | ---------- 34 | t : float 35 | Angle theta (t) is the scalar angle (in radians). 36 | 37 | Returns 38 | ------- 39 | dcm : numpy.ndarray 40 | Numpy 3x3 direction cosine matrix. 41 | 42 | ''' 43 | 44 | dcm = np.array([[ 1.0, 0.0, 0.0 ], 45 | [ 0.0, math.cos(t), math.sin(t) ], 46 | [ 0.0, -1*math.sin(t), math.cos(t) ]]) 47 | 48 | return dcm 49 | 50 | 51 | 52 | def dcmY(t): 53 | '''Generate the direction cosine matrix for an Y-axis rotation of angle t. 54 | 55 | Parameters 56 | ---------- 57 | t : float 58 | Angle theta (t) is the scalar angle (in radians). 59 | 60 | Returns 61 | ------- 62 | dcm : numpy.ndarray 63 | Numpy 3x3 direction cosine matrix. 64 | 65 | ''' 66 | 67 | dcm = np.array([[ math.cos(t), 0.0, -1*math.sin(t) ], 68 | [ 0.0, 1.0, 0.0 ], 69 | [ math.sin(t), 0.0, math.cos(t) ]]) 70 | 71 | return dcm 72 | 73 | 74 | 75 | def dcmZ(t): 76 | '''Generate the direction cosine matrix for an Z-axis rotation of angle t. 77 | 78 | Parameters 79 | ---------- 80 | t : float 81 | Angle theta (t) is the scalar angle (in radians). 82 | 83 | Returns 84 | ------- 85 | dcm : numpy.ndarray 86 | Numpy 3x3 direction cosine matrix. 87 | 88 | ''' 89 | 90 | dcm = np.array([[ math.cos(t), math.sin(t), 0.0 ], 91 | [ -1*math.sin(t), math.cos(t), 0.0 ], 92 | [ 0.0, 0.0, 1.0 ]]) 93 | 94 | return dcm -------------------------------------------------------------------------------- /source/atmosphere.py: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | ############################################################################### 3 | ## ## 4 | ## _____ ___ ___ ___ _____ __ __ ## 5 | ## | _ | _ \| _ \|_ _||_ _| | \/ | ## 6 | ## | |_| | <| _ < | | | | _ | \ / | ## 7 | ## |_____|_|\_|___/|___| |_| |_| |_|\/|_| ## 8 | ## v 1.1 ## 9 | ## ## 10 | ## FILE DESCRIPTION: ## 11 | ## ## 12 | ## Atmospheric density model based on the U.S. Standard Atmosphere 1976. ## 13 | ## Returns the atmospheric density (km/m^3) given an altitude input (km). ## 14 | ## Valid only for altitudes between 86km to 1000km. ## 15 | ## ## 16 | ## Written by Samuel Y. W. Low. ## 17 | ## First created 24-May-2017 11:27 AM (+8 GMT) ## 18 | ## Last modified 19-Sep-2021 22:27 PM (-7 GMT) ## 19 | ## ## 20 | ############################################################################### 21 | ############################################################################### 22 | 23 | import math 24 | import bisect 25 | 26 | # Create the atmospheric density coefficients table 27 | D_coeff = [[ 0.0000000000, 0.0000000000, 0.0000000000,-0.1523325, 0.202941], 28 | [ 0.0000000000,-3.322622e-06, 9.111460e-04,-0.2609971, 5.944694], 29 | [ 0.0000000000, 2.873405e-05,-0.008492037, 0.6541179, -23.62010], 30 | [-1.240774e-05, 0.005162063, -0.8048342, 55.55996, -1443.338], 31 | [ 0.0000000000,-8.854164e-05, 0.03373254, -4.390837, 176.5294], 32 | [ 3.661771e-07,-2.154344e-04, 0.04809214, -4.884744, 172.3597], 33 | [ 1.906032e-08,-1.527799e-05, 0.004724294, -0.6992340, 20.50921], 34 | [ 1.199282e-09,-1.451051e-06, 6.910474e-04,-0.1736220, -5.321644], 35 | [ 1.140564e-10,-2.130756e-07, 1.570762e-04,-0.07029296,-12.89844], 36 | [ 8.105631e-12,-2.358417e-09,-2.635110E-06,-0.01562608,-20.02246], 37 | [-3.701195e-12,-8.608611e-09, 5.118829e-05,-0.06600998,-6.137674]] 38 | 39 | def density(R): 40 | '''Atmospheric density model based on the U.S. Standard Atmosphere 1976. 41 | Returns the atmospheric density (km/m^3) given an altitude input (km). 42 | Valid only for altitudes between 86km to 1000km. 43 | 44 | Parameters 45 | ---------- 46 | R : float 47 | Radial altitude from the surface of the Earth (km) 48 | 49 | Returns 50 | ------- 51 | density : float 52 | Atmospheric density (kg/m^3) 53 | 54 | ''' 55 | 56 | # Altitude i-th data points in Ra[i] for US Standard Atmosphere 1976 57 | Ra = [86, 91, 100, 110, 120, 150, 200, 300, 500, 750, 1000] 58 | Ri = bisect.bisect(Ra, R) 59 | co = D_coeff[Ri] 60 | 61 | # Compute the exponent term. 62 | AtmosExp = co[0]*(R**4) + co[1]*(R**3) + co[2]*(R**2) + co[3]*(R) + co[4] 63 | 64 | # Return the atmospheric density (kg/m^3) 65 | return math.exp(AtmosExp) -------------------------------------------------------------------------------- /scenarios/pointing_policy/plot_relative_orbits.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ############################################################################### 4 | ############################################################################### 5 | ## ## 6 | ## ___ _ _ __ ____ ____ __ _ _ _____ ## 7 | ## / _ \| | | | / \ | _ \| __ \ / \ | \ | |_ _| ## 8 | ## ( |_| ) |_| |/ /\ \| |_| | -/ // /\ \| \| | | | ## 9 | ## \_ /|_____| /--\ |____/|_|\_\ /--\ |_\___| |_| ## 10 | ## \/ v 0.0 ## 11 | ## ## 12 | ## Optimal Pointing Sequences in Spacecraft Formation Flying ## 13 | ## using Online Planning with Resource Constraints. ## 14 | ## ## 15 | ## Plotting code for relative orbits in demonstration scenario. ## 16 | ## ## 17 | ## Written by Samuel Y. W. Low. ## 18 | ## Advised by Professor Mykel J. Kochenderfer ## 19 | ## First created 23-Nov-2021 23:01 PM (-8 GMT) ## 20 | ## Last modified 15-Mar-2022 13:05 AM (-8 GMT) ## 21 | ## ## 22 | ############################################################################### 23 | ############################################################################### 24 | 25 | # Import global libraries. 26 | import numpy as np 27 | import os, csv 28 | import matplotlib.pyplot as plt 29 | from copy import deepcopy 30 | 31 | # Move the current directory up until the Quadrant root. 32 | while os.getcwd().split("\\")[-1] != "quadrant": 33 | os.chdir("..") 34 | 35 | # Import source libraries. 36 | from source import spacecraft, attitudes, targeting, feedback, deputy 37 | 38 | # Retrieve the ephemeris 39 | sDs = [] 40 | scenario_path = os.getcwd() + '\\scenarios\\pointing_policy' 41 | with open( scenario_path + '\\ephemeris.csv' ) as ephemeris: 42 | ephemeris_csv = csv.reader( ephemeris, delimiter=',' ) 43 | for row in ephemeris_csv: 44 | if 'Header' in row: 45 | continue 46 | else: 47 | a,e,i = float(row[1]), float(row[2]), float(row[3]) 48 | w,R,M = float(row[4]), float(row[5]), float(row[6]) 49 | if 'Chief' in row[0]: # Grab the chief SC parameters. 50 | sC = spacecraft.Spacecraft( elements = [a,e,i,w,R,M] ) 51 | if 'Deputy' in row[0]: # Grab the deputy SC parameters. 52 | sD = spacecraft.Spacecraft(elements=[a,e,i,w,R,M]) 53 | sDs.append( sD ) 54 | 55 | # Plot the trajectory for one orbit. 56 | fig = plt.figure() 57 | ax = fig.add_subplot(projection='3d') 58 | for sDi in sDs: 59 | sCi = deepcopy( sC ) 60 | posn_array = [] 61 | for t in range(7200): 62 | hill = sCi.get_hill_frame() 63 | posn = hill @ (np.array(sCi.states[:3])-np.array(sDi.states[:3])) 64 | posn_array.append( posn ) 65 | sCi.propagate_orbit(1) 66 | sDi.propagate_orbit(1) 67 | if t == 0: 68 | ax.scatter(posn[0],posn[1],posn[2]) 69 | posn_array = np.array( posn_array ) 70 | ax.plot(posn_array[:,0],posn_array[:,1],posn_array[:,2]) 71 | ax.set_xlabel('Cross-Track [km]') 72 | ax.set_ylabel('In-Track [km]') 73 | ax.set_zlabel('Radial [km]') 74 | ax.scatter(0,0,0,'k') -------------------------------------------------------------------------------- /source/formation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ############################################################################### 4 | ############################################################################### 5 | ## ## 6 | ## ___ _ _ __ ____ ____ __ _ _ _____ ## 7 | ## / _ \| | | | / \ | _ \| __ \ / \ | \ | |_ _| ## 8 | ## ( |_| ) |_| |/ /\ \| |_| | -/ // /\ \| \| | | | ## 9 | ## \_ /|_____| /--\ |____/|_|\_\ /--\ |_\___| |_| ## 10 | ## \/ v 0.0 ## 11 | ## ## 12 | ## FILE DESCRIPTION: ## 13 | ## ## 14 | ## This file contains the relative orbit propagation function. It takes ## 15 | ## the 06x chief and deputy orbit Keplerian elements (in km and radians) ## 16 | ## as input, and outputs the Hill-frame position and velocity vectors as ## 17 | ## two sets of Nx3 NumPy matrices, where N = total number of samples. ## 18 | ## ## 19 | ## Written by Samuel Y. W. Low. ## 20 | ## First created 16-May-2021 15:56 AM (+8 GMT) ## 21 | ## Last modified 15-Mar-2022 14:03 PM (-8 GMT) ## 22 | ## ## 23 | ############################################################################### 24 | ############################################################################### 25 | 26 | import numpy as np 27 | 28 | class Formation(): 29 | 30 | def __init__(self, sC, sD): 31 | '''Initialise a formation object, defined by a chief and a deputy. 32 | 33 | Parameters 34 | ---------- 35 | sC : Spacecraft() 36 | Chief spacecraft, as an instance of the Spacecraft() class from 37 | the `spacecraft.py` module. 38 | sD : Spacecraft() 39 | Deputy spacecraft, as an instance of the Spacecraft() class from 40 | the `spacecraft.py` module. 41 | 42 | ''' 43 | 44 | try: 45 | self.sC = sC # Chief 46 | self.sD = sD # Deputy 47 | self.update_ROE( sC, sD ) 48 | self.update_RTN( sC, sD ) 49 | 50 | except AttributeError: 51 | print("AttributeError: Check if constructor has 02x Spacecraft()?") 52 | except TypeError: 53 | print("TypeError: Inputs must be instances of Spacecraft()!") 54 | except: 55 | print("Unknown error occurred. Printing constructor args:") 56 | print(sC, sD) 57 | 58 | # Convert Keplerian orbit elements to relative orbit elements exactly. 59 | def update_ROE(self, sC, sD): 60 | self.sC = sC # Chief 61 | self.sD = sD # Deputy 62 | self.da = (self.sD.a - self.sC.a) / self.sC.a 63 | self.dL = (self.sD.M - self.sC.M) + (self.sD.w - self.sC.w) 64 | self.dL = (self.sD.R - self.sC.R) * np.cos(self.sC.i) + self.dL 65 | self.ix = (self.sD.i - self.sC.i) 66 | self.iy = (self.sD.R - self.sC.R) * np.sin(self.sC.i) 67 | self.ex = (self.sD.e * np.cos(self.sD.w)) 68 | self.ex = (self.ex - (self.sC.e * np.cos(self.sC.w))) 69 | self.ey = (self.sD.e * np.sin(self.sD.w)) 70 | self.ey = (self.ey - (self.sC.e * np.sin(self.sC.w))) 71 | 72 | # Convert inertial frame coordinates to RTN frame coordinates exactly. 73 | def update_RTN(self, sC, sD): 74 | self.sC = sC # Chief 75 | self.sD = sD # Deputy 76 | pC = np.array([ self.sC.px, self.sC.py, self.sC.pz ]) 77 | pD = np.array([ self.sD.px, self.sD.py, self.sD.pz ]) 78 | self.get_hill_dcm() # Updates self.hill_dcm 79 | self.RTN = self.hill_dcm @ ( pD - pC ) 80 | -------------------------------------------------------------------------------- /docs/_build/html/_static/js/html5shiv-printshiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.3-pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | ############################################################################### 3 | ## ## 4 | ## ___ _ _ __ ____ ____ __ _ _ _____ ## 5 | ## / _ \| | | | / \ | _ \| __ \ / \ | \ | |_ _| ## 6 | ## ( |_| ) |_| |/ /\ \| |_| | -/ // /\ \| \| | | | ## 7 | ## \_ /|_____| /--\ |____/|_|\_\ /--\ |_\___| |_| ## 8 | ## \/ v 0.0 ## 9 | ## ## 10 | ## Written by Samuel Y. W. Low. ## 11 | ## Last modified 15-March-2022. ## 12 | ## Website: https://github.com/sammmlow/quadrant ## 13 | ## Documentation: https://quadrant.readthedocs.io/en/latest/ ## 14 | ## ## 15 | ############################################################################### 16 | ############################################################################### 17 | 18 | # Configuration file for the Sphinx documentation builder. 19 | # 20 | # This file only contains a selection of the most common options. For a full 21 | # list see the documentation: 22 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 23 | 24 | # -- Path setup -------------------------------------------------------------- 25 | 26 | # If extensions (or modules to document with autodoc) are in another directory, 27 | # add these directories to sys.path here. If the directory is relative to the 28 | # documentation root, use os.path.abspath to make it absolute, like shown here. 29 | 30 | import os 31 | import sys 32 | sys.path.insert(0, os.path.abspath('..')) 33 | sys.path.insert(0, os.path.abspath('../source')) 34 | 35 | import sphinx_rtd_theme 36 | 37 | html_theme = "sphinx_rtd_theme" 38 | # -- Project information ----------------------------------------------------- 39 | 40 | project = 'QUADRANT' 41 | copyright = '2022, Samuel Y. W. Low' 42 | author = 'Samuel Y. W. Low' 43 | 44 | # The full version, including alpha/beta/rc tags 45 | release = '0.0' 46 | 47 | # -- General configuration --------------------------------------------------- 48 | 49 | # Add any Sphinx extension module names here, as strings. They can be 50 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 51 | # ones. 52 | extensions = ["sphinx_rtd_theme", 53 | "sphinx.ext.autodoc", 54 | "sphinx.ext.coverage", 55 | "sphinx.ext.napoleon"] 56 | 57 | # Add any paths that contain templates here, relative to this directory. 58 | templates_path = ['_templates'] 59 | 60 | # List of patterns, relative to source directory, that match files and 61 | # directories to ignore when looking for source files. 62 | # This pattern also affects html_static_path and html_extra_path. 63 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 64 | 65 | 66 | # -- Options for HTML output ------------------------------------------------- 67 | 68 | # The theme to use for HTML and HTML Help pages. See the documentation for 69 | # a list of builtin themes. 70 | # 71 | # html_theme = 'alabaster' 72 | 73 | # Add any paths that contain custom static files (such as style sheets) here, 74 | # relative to this directory. They are copied after the builtin static files, 75 | # so a file named "default.css" will overwrite the builtin "default.css". 76 | html_static_path = ['_static','_figures'] 77 | 78 | html_theme_options = { 79 | 'analytics_id': 'UA-XXXXXXX-1', # Provided by Google in your dashboard 80 | 'analytics_anonymize_ip': False, 81 | 'logo_only': False, 82 | 'display_version': True, 83 | 'prev_next_buttons_location': 'bottom', 84 | 'style_external_links': False, 85 | 'vcs_pageview_mode': '', 86 | 'style_nav_header_background': '#222A35', 87 | # Toc options 88 | 'collapse_navigation': True, 89 | 'sticky_navigation': True, 90 | 'navigation_depth': 4, 91 | 'includehidden': True, 92 | 'titles_only': False 93 | } 94 | 95 | # Get the direct path 96 | dir_path = os.path.dirname(os.path.realpath(__file__)) 97 | 98 | # Add the HTML logo that displays on the top-left panel. 99 | html_logo = dir_path + '/_static/favicon.png' 100 | 101 | # Add the HTML logo. 102 | html_favicon = dir_path + '/_static/favicon.png' -------------------------------------------------------------------------------- /docs/_build/html/_static/js/theme.js: -------------------------------------------------------------------------------- 1 | !function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t 2 | 3 | 4 | 5 | 6 | Search — QUADRANT 0.0 documentation 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 55 | 56 |
60 | 61 |
62 |
63 |
64 |
    65 |
  • »
  • 66 |
  • Search
  • 67 |
  • 68 |
  • 69 |
70 |
71 |
72 |
73 |
74 | 75 | 82 | 83 | 84 |
85 | 86 |
87 | 88 |
89 |
90 |
91 | 92 |
93 | 94 |
95 |

© Copyright 2022, Samuel Y. W. Low.

96 |
97 | 98 | Built with Sphinx using a 99 | theme 100 | provided by Read the Docs. 101 | 102 | 103 |
104 |
105 |
106 |
107 |
108 | 113 | 114 | 115 | 124 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /scenarios/pointing_policy/plot_demo_results.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ############################################################################### 4 | ############################################################################### 5 | ## ## 6 | ## ___ _ _ __ ____ ____ __ _ _ _____ ## 7 | ## / _ \| | | | / \ | _ \| __ \ / \ | \ | |_ _| ## 8 | ## ( |_| ) |_| |/ /\ \| |_| | -/ // /\ \| \| | | | ## 9 | ## \_ /|_____| /--\ |____/|_|\_\ /--\ |_\___| |_| ## 10 | ## \/ v 0.0 ## 11 | ## ## 12 | ## Optimal Pointing Sequences in Spacecraft Formation Flying ## 13 | ## using Online Planning with Resource Constraints. ## 14 | ## ## 15 | ## Plotting code for single scenario results. Demonstration scenario ## 16 | ## results were obtained by running policy scripts for trials = 1. ## 17 | ## ## 18 | ## Written by Samuel Y. W. Low. ## 19 | ## Advised by Professor Mykel J. Kochenderfer ## 20 | ## First created 23-Nov-2021. Last modified 04-Apr-2022. ## 21 | ## ## 22 | ############################################################################### 23 | ############################################################################### 24 | 25 | # Import global libraries. 26 | import numpy as np 27 | import matplotlib.pyplot as plt 28 | plt.close('all') 29 | 30 | # Recorded results for random search in deterministic scenario. 31 | p1_nodes = ['9', '3', 'S', '6', '8', '7', '5', '2', 'S', '4' ] 32 | p1_times = [1938,4844,6154,7522,7818,8090,8384,10388,11565,11841] 33 | 34 | # Recorded results for greedy (angle) search in deterministic scenario. 35 | p2_nodes = ['5', 'S', '3', '9', 'S', '6', 'S', '7', '2', 'S', '4', '8' ] 36 | p2_times = [2214,3010,3279,5144,5894,7522,8178,8458,10388,11161,11438,11560] 37 | 38 | # Recorded results for greedy (angle rate) search in deterministic scenario. 39 | p3_nodes = ['7','9','8','6', '5', '3', 'S', '4','2' ] 40 | p3_times = [294,591,882,1187,1580,2219,2986,4554,4848] 41 | 42 | # Recorded results for one step lookahead in deterministic scenario. 43 | p4_nodes = ['7','6','4','2', '8', '9', 'S', 'S', '3', '5' ] 44 | p4_times = [294,585,840,1142,1624,2038,2786,2788,3060,4847] 45 | 46 | # Recorded results for forward search (depth 2) in deterministic scenario. 47 | p5_nodes = ['7','6','4','2', '8', 'S', '3', '5', 'S', '9' ] 48 | p5_times = [294,585,840,1142,1624,2289,2565,4847,5689,5961] 49 | 50 | # Recorded results for forward search (depth 3) in deterministic scenario. 51 | p6_nodes = ['7','6','4','2', '8', 'S', '3', '5', 'S', '9' ] 52 | p6_times = [294,585,840,1142,1624,2289,2565,4847,5689,5961] 53 | 54 | # Put them all in a nested list. 55 | all_nodes = [p1_nodes, p2_nodes, p3_nodes, p4_nodes, p5_nodes, p6_nodes] 56 | all_times = [p1_times, p2_times, p3_times, p4_times, p5_times, p6_times] 57 | 58 | # Relative run times for each script for 1 trial. 59 | runtimes = np.array([8.4, 8.4, 8.4, 24, 97, 870]) 60 | print('Relative run-times were:', runtimes/min(runtimes)) 61 | 62 | # Create the figure subplot object. 63 | plt.rcdefaults() 64 | fig, ax = plt.subplots() 65 | ax.xaxis.grid(True,linestyle='dashed') 66 | ax.set_axisbelow(True) 67 | cmap = plt.cm.viridis 68 | policies_names = ('Random', 'Greedy (Angle)', 'Greedy (Rates)', 69 | 'Lookahead', 'Fwd Search (d=2)', 'Fwd Search (d=3)') 70 | policies_number = np.arange( len(policies_names) ) 71 | for i in range(len(all_nodes)): 72 | nodes, times = all_nodes[i], all_times[i] 73 | plt.gca().set_prop_cycle( None ) 74 | for j in range(len(nodes)+1): 75 | c = cmap( j / np.max(len(nodes)+1)) 76 | ax.barh( i, width=times[-1*j], height=0.4, color=c ) 77 | ax.scatter( times[-1*j], i, c='white' ) 78 | if nodes[-1*j] != 'S': 79 | ax.annotate( nodes[-1*j], (times[-1*j], i+0.45)) 80 | else: 81 | ax.annotate( nodes[-1*j], (times[-1*j], i+0.45), color='red') 82 | 83 | ax.set_yticks( policies_number ) 84 | ax.set_yticklabels( policies_names ) 85 | ax.invert_yaxis() # labels read top-to-bottom 86 | ax.set_xlabel('Pointing mission execution time (seconds)') 87 | ax.set_title('Pointing execution time (s) for each policy in demo scenario.') 88 | plt.show() 89 | 90 | # # Example data 91 | # people = ('Tom', 'Dick', 'Harry', 'Slim', 'Jim') 92 | # y_pos = np.arange(len(people)) 93 | # performance = 3 + 10 * np.random.rand(len(people)) 94 | # error = np.random.rand(len(people)) 95 | 96 | # ax.barh(y_pos, performance, xerr=error, align='center') 97 | # ax.set_yticks(y_pos) 98 | # ax.set_yticklabels(people) 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /docs/_build/html/py-modindex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Python Module Index — QUADRANT 0.0 documentation 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | 31 |
32 | 59 | 60 |
64 | 65 |
66 |
67 |
68 |
    69 |
  • »
  • 70 |
  • Python Module Index
  • 71 |
  • 72 |
  • 73 |
74 |
75 |
76 |
77 |
78 | 79 | 80 |

Python Module Index

81 | 82 |
83 | a | 84 | s 85 |
86 | 87 | 88 | 89 | 91 | 92 | 93 | 96 | 97 | 99 | 100 | 101 | 104 |
 
90 | a
94 | attitudes 95 |
 
98 | s
102 | spacecraft 103 |
105 | 106 | 107 |
108 |
109 |
110 | 111 |
112 | 113 |
114 |

© Copyright 2022, Samuel Y. W. Low.

115 |
116 | 117 | Built with Sphinx using a 118 | theme 119 | provided by Read the Docs. 120 | 121 | 122 |
123 |
124 |
125 |
126 |
127 | 132 | 133 | 134 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /source/anomaly.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ############################################################################### 4 | ############################################################################### 5 | ## ## 6 | ## ___ _ _ _ ____ _____ ____ ____ ## 7 | ## / _ \| | | | | | __|_ _| ___| __ \ ## 8 | ## ( |_| ) |__| |_| |__ | | | | __| -/ / ## 9 | ## \_ /|____|_____|____| |_| |____|_|\_\ ## 10 | ## \/ v 0.0 ## 11 | ## ## 12 | ## FILE DESCRIPTION: ## 13 | ## ## 14 | ## This file contains all the anomaly conversion scripts, from the mean ## 15 | ## anomaly to the true anomaly and vice versa. ## 16 | ## ## 17 | ## Written by Samuel Y. W. Low. ## 18 | ## First created 20-May-2021 10:17 PM (+8 GMT) ## 19 | ## Last modified 20-May-2021 10:17 PM (+8 GMT) ## 20 | ## ## 21 | ############################################################################### 22 | ############################################################################### 23 | 24 | import numpy as np 25 | 26 | ############################################################################### 27 | ############################################################################### 28 | 29 | def M2E(M,e): 30 | '''Mean anomaly to eccentric anomaly conversion via Keplers Equation (rad). 31 | 32 | Parameters 33 | ---------- 34 | M : float 35 | Mean Anomaly (rad) 36 | e : float 37 | Eccentricity (unit-less) 38 | 39 | Returns 40 | ------- 41 | E2 : float 42 | Eccentric anomaly (rad) 43 | 44 | ''' 45 | 46 | E1 = M # Initialise eccentric anomaly 47 | ei = e # Initialise the float eccentricity 48 | residual = 1.0 # Initialise convergence residual 49 | 50 | while residual >= 0.000001: 51 | 52 | fn = E1 - (ei*np.sin(E1)) - M 53 | fd = 1 - (ei*np.cos(E1)) 54 | E2 = E1 - (fn/fd) 55 | residual = abs(E2-E1) # Compute residual 56 | E1 = E2 # Update the eccentric anomaly 57 | 58 | return E2 59 | 60 | ############################################################################### 61 | ############################################################################### 62 | 63 | def M2V(M,e): 64 | '''Mean anomaly to true anomaly conversion via Keplers Equation (rad). 65 | 66 | Parameters 67 | ---------- 68 | M : float 69 | Mean Anomaly (rad) 70 | e : float 71 | Eccentricity (unit-less) 72 | 73 | Returns 74 | ------- 75 | nu : float 76 | True anomaly (rad) 77 | 78 | ''' 79 | 80 | # First, let us solve for the eccentric anomaly. 81 | eccAnom = M2E(M,e) 82 | 83 | # With the eccentric anomaly, we can solve for position and velocity 84 | # in the perifocal (PQW) frame, using the polar equation for an ellipse. 85 | pos_X = np.cos(eccAnom) - e 86 | pos_Y = np.sqrt( 1 - e**2 ) * np.sin(eccAnom) 87 | 88 | # Finally, let us compute the true anomaly. 89 | nu = np.arctan2( pos_Y, pos_X ) 90 | 91 | return nu 92 | 93 | ############################################################################### 94 | ############################################################################### 95 | 96 | def V2E(nu,e): 97 | '''True anomaly to eccentric anomaly conversion (rad). 98 | 99 | Parameters 100 | ---------- 101 | nu : float 102 | True anomaly (rad) 103 | e : float 104 | Eccentricity (unit-less) 105 | 106 | Returns 107 | ------- 108 | E : float 109 | Eccentric anomaly (rad) 110 | 111 | ''' 112 | 113 | E = 2 * np.arctan( np.sqrt( ( 1 - e ) / ( 1 + e ) ) * np.tan( nu / 2 ) ) 114 | return E 115 | 116 | ############################################################################### 117 | ############################################################################### 118 | 119 | def E2M(E,e): 120 | '''Eccentric anomaly to mean anomaly conversion (rad). Note that this uses 121 | the original Keplers equation M = E - e*sin(E). 122 | 123 | Parameters 124 | ---------- 125 | E : float 126 | Eccentric anomaly (rad) 127 | e : float 128 | Eccentricity (unit-less) 129 | 130 | Returns 131 | ------- 132 | M : float 133 | Mean Anomaly (rad) 134 | 135 | ''' 136 | 137 | M = E - e * np.sin(E) 138 | return M 139 | 140 | ############################################################################### 141 | ############################################################################### 142 | 143 | def V2M(nu,e): 144 | '''True anomaly to mean anomaly conversion (rad). 145 | 146 | Parameters 147 | ---------- 148 | nu : float 149 | True anomaly (rad) 150 | e : float 151 | Eccentricity (unit-less) 152 | 153 | Returns 154 | ------- 155 | M : float 156 | Mean Anomaly (rad) 157 | 158 | ''' 159 | 160 | return E2M( V2E(nu,e), e ) 161 | -------------------------------------------------------------------------------- /docs/_build/html/docs_install.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Installation — QUADRANT 0.0 documentation 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 55 | 56 |
60 | 61 |
62 |
63 |
64 | 71 |
72 |
73 |
74 |
75 | 76 | _images/quadrant_logo.png 77 |
78 |

79 |
80 |
81 |

Installation

82 |

Lorem ipsum.

83 |
84 |

Note

85 |

Lorem ipsum: Lorem ipsum. 86 | copy, datetime, decimal, math, os, pathlib, tkinter, urllib, warnings

87 |
88 |
89 | 90 | 91 |
92 |
93 |
97 | 98 |
99 | 100 |
101 |

© Copyright 2022, Samuel Y. W. Low.

102 |
103 | 104 | Built with Sphinx using a 105 | theme 106 | provided by Read the Docs. 107 | 108 | 109 |
110 |
111 |
112 |
113 |
114 | 119 | 120 | 121 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /docs/_build/html/docs_firststep.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | First steps — QUADRANT 0.0 documentation 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 55 | 56 |
60 | 61 |
62 |
63 |
64 | 71 |
72 |
73 |
74 |
75 | 76 | _images/quadrant_logo.png 77 |
78 |

79 |
80 |
81 |

First steps

82 |

Lorem ipsum.

83 |
84 |

Note

85 |

Lorem ipsum: Lorem ipsum. 86 | copy, datetime, decimal, math, os, pathlib, tkinter, urllib, warnings

87 |
88 |
89 | 90 | 91 |
92 |
93 |
97 | 98 |
99 | 100 |
101 |

© Copyright 2022, Samuel Y. W. Low.

102 |
103 | 104 | Built with Sphinx using a 105 | theme 106 | provided by Read the Docs. 107 | 108 | 109 |
110 |
111 |
112 |
113 |
114 | 119 | 120 | 121 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /source/feedback.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Nov 23 23:25:33 2021 4 | 5 | @author: sammm 6 | """ 7 | import numpy as np 8 | from source import attitudes 9 | 10 | def control( sC, Kp, Ki, Kd, dcmRN, ohmRN ): 11 | 12 | # ------------------------------------------------------------------------ 13 | # MEMO ON SIMULATING SPACECRAFT ATTITUDES AND EXECUTION OF CONTROL LAW 14 | # Written by Samuel Low (07-01-2020), DSO National Laboratories. 15 | # ------------------------------------------------------------------------ 16 | # 17 | # The inputs of this function are: 18 | # --> t ... ... Current time in the dynamics loop 19 | # --> dt ... ... Time step in the dynamics loop 20 | # --> ct ... ... Time step in the controls software loop 21 | # --> Kp ... ... Feedback control proportional gain 22 | # --> Ki ... ... Feedback control integral gain 23 | # --> Kd ... ... Feedback control derivative gain 24 | # --> ctrl_mode ... ... Control law choice (integer) 25 | # --> I ... ... Spacecraft 3x3 inertia matrix 26 | # --> qBN ... ... Attitude error in inertial-to-body frame [BN] 27 | # --> wBN ... ... Angular velocity vector (rad/s) inertial-to-reference 28 | # --> ohmRN ... ... Angular velocity vector (rad/s) inertial-to-reference 29 | # --> dcmRN ... ... Direction cosine matrix inertial-to-reference [RN] 30 | # --> intgErr ... Accumulated qBR error integrated over time 31 | # --> torq ... Control torque vector from previous loop (Nm) 32 | # 33 | # The outputs of this function are the attitude errors and angular rates: 34 | # --> qBR ... ... Reference-to-Body attitude 35 | # --> wBR ... ... Reference-to-Body angular velocity (rad/s) 36 | # --> qBN ... ... Inertial-to-Body attitude 37 | # --> wBN ... ... Inertial-to-Body angular velocity (rad/s) 38 | # --> qBN_DOT ... Inertial-to-Body attitude rate 39 | # --> intgErr ... Reference-to-Body attitude integration error 40 | # --> torque ... Control torque response (Nm) in current feedback 41 | # 42 | # ------------------------------------------------------------------------ 43 | # 44 | # To understand the feedback control logic in this function, which is to 45 | # be embedded in the numerical integrator loop, let us work backwards. 46 | # 47 | # In general for tracking, the reference-to-body outputs qBR and wBR 48 | # are to be updated in the feedback. This means qDotBR is required, which 49 | # can be computed using qBR and wBR using the differential kinematic 50 | # equation at that instant. wBR can be obtained via vector addition (as 51 | # seen in the B-frame) of wBN and -ohmRN. Target ohmRN can be computed in the 52 | # "targeting.py" library, before calling this control function. wBN must 53 | # be updated in each loop, and thus requires the 54 | # computation of wDotBN for the update. wDotBN can be computed by 55 | # providing inputs wBN, the satellite inertia I, and the control law 56 | # U, into Euler's rotational equation of motion. If omegaDot_RN is needed, 57 | # the numerical first order gradient will suffice as an estimate. 58 | # 59 | # Take note that ohmRN and omegaDot_RN must be seen in the B-frame! 60 | # 61 | # Keep in mind also that if we are working with MRPs, we should switch to 62 | # the appropriate shadow and non-shadow set values by checking if the MRP 63 | # norm exceeds 1. This is not necessary with CRPs, since CRP attitude 64 | # coordinates are unique (i.e., the shadow set = non-shadow set). In the 65 | # case of quarternions, it is necessary to check that the Beta-0 term is 66 | # greater than or equal to 0, in order to ensure that the short rotation 67 | # is called, instead of the long rotation (think: the -359 degree case). 68 | # 69 | # Now, we understand what are the ingredients we need to solve for qBR 70 | # in each loop, we can update the respective derivatives, retrieve the 71 | # appropriate reference attitudes, repeat again in the next loop. 72 | # 73 | # -------------------------------------------------------------------- 74 | 75 | # First check: Ensure that the quarternions always have norm = 1. 76 | # Although quarternions are represented as NumPy arrays, you cannot 77 | # perform vector addition as quarternions are not like vectors where the 78 | # law of commutativity and the law of parallelograms is adhered to. Thus, 79 | # in order to perform vector operations on them, you must call the 80 | # attribute `q` to draw out the quarternion as a NumPy array. 81 | 82 | # If spacecraft attitude coordinates are in quaternions... 83 | if sC.attBN.strID() == 'QTR' and sC.attBR.strID() == 'QTR': 84 | 85 | # Check if BN quaternion describes the short or long rotation. 86 | if sC.attBN[0] < 0.0: 87 | sC.attBN.qtr = -1 * sC.attBN.qtr 88 | 89 | # Re-normalise BN quaternions if norm is not 1. 90 | if np.dot( sC.attBN.qtr, sC.attBN.qtr ) != 1.0: 91 | sC.attBN.normalise() 92 | 93 | # Initialise reference-to-body and reference-to-inertial attitudes. 94 | dcmBN = sC.attBN.dcm 95 | dcmBR = dcmBN @ dcmRN.T 96 | sC.attBR = attitudes.QTR( dcm = dcmBR ) 97 | 98 | # Check if BR quaternion describes the short or long rotation. 99 | if sC.attBR[0] < 0.0: 100 | sC.attBR.qtr = -1 * sC.attBR.qtr 101 | 102 | # Re-normalise BR quaternions if norm is not 1. 103 | if np.dot( sC.attBR.qtr, sC.attBR.qtr ) != 1.0: 104 | sC.attBR.normalise() 105 | 106 | # The original ohmRN is seen in the N-frame. Convert to B-frame. 107 | ohmRN = dcmBN @ ohmRN 108 | 109 | # Update angular velocity of body-to-reference (in body frame) 110 | sC.ohmBR = sC.ohmBN - ohmRN 111 | 112 | # Compute the feedback control torque and apply it on spacecraft. 113 | # Note that this only changes the attribute of the torque on the 114 | # spacecraft, to meaningfully propagate it, the user should run 115 | # the "propagate_attitude_stepwise( dt, torque )" method of the SC. 116 | torque = ( -1 * Kp * sC.attBR.qtr[1:] ) 117 | torque -= ( Kd * sC.ohmBR ) 118 | torque -= ( Ki * Kp * sC.attIntgErr ) 119 | sC.torque = torque 120 | return sC 121 | 122 | else: 123 | return False 124 | 125 | -------------------------------------------------------------------------------- /source/deputy.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ############################################################################### 4 | ############################################################################### 5 | ## ## 6 | ## ___ _ _ _ ____ _____ ____ ____ ## 7 | ## / _ \| | | | | | __|_ _| ___| __ \ ## 8 | ## ( |_| ) |__| |_| |__ | | | | __| -/ / ## 9 | ## \_ /|____|_____|____| |_| |____|_|\_\ ## 10 | ## \/ v 0.0 ## 11 | ## ## 12 | ## FILE DESCRIPTION: ## 13 | ## ## 14 | ## This file contains the function that resolves for the Keplerian ## 15 | ## elements of the deputy spacecraft. It takes in formation geometry ## 16 | ## parameters (radial, in-track, cross-track, or RIC), the argument of ## 17 | ## latitude crossing (rad), the argument of relative pericenter (rad), ## 18 | ## the six chief orbit Keplerian elements (in km and rad) as inputs. ## 19 | ## It outputs the six Keplerian orbit elements of the deputy spacecraft ## 20 | ## that satisfies the formation geometrical requirements. ## 21 | ## ## 22 | ## KEY REFERENCES: ## 23 | ## ## 24 | ## [1] Clohessy, W. H., Wiltshire, R. S. (1960). Terminal guidance ## 25 | ## system for satellite rendezvous. Journal of the Aerospace ## 26 | ## Sciences, 27(9), 653-658. doi:10.2514/8.8704 ## 27 | ## ## 28 | ## [2] D'Amico, S., &; Montenbruck, O. (2006). Proximity operations of ## 29 | ## formation-flying spacecraft using an eccentricity/inclination ## 30 | ## vector separation. Journal of Guidance, Control, and Dynamics, ## 31 | ## 29(3), 554-563. doi:10.2514/1.15114 ## 32 | ## ## 33 | ## [3] D'Amico, S. (2010) Autonomous Formation Flying in Low Earth ## 34 | ## Orbit. PhD Dissertation, TU Delft. ## 35 | ## ## 36 | ## Written by Samuel Y. W. Low. ## 37 | ## First created 20-May-2021 18:00 PM (+8 GMT) ## 38 | ## Last modified 20-May-2021 20:09 PM (+8 GMT) ## 39 | ## ## 40 | ############################################################################### 41 | ############################################################################### 42 | 43 | import numpy as np 44 | from source import anomaly 45 | 46 | def deputy(aC, eC, iC, wC, RC, MC, fR, fI, fO, fC, fPhi, fTht): 47 | ''' 48 | 49 | Parameters 50 | ---------- 51 | td : int 52 | Propagation Duration (s) 53 | ts : int 54 | Propagation Timestep (s) 55 | aC : float 56 | Chief Orbit Semi-Major Axis (km) 57 | eC : float 58 | Chief Orbit Eccentricity (0 to 1) 59 | iC : float 60 | Chief Orbit Inclination (deg) 61 | wC : float 62 | Chief Orbit Arg. of Perigee (deg) 63 | RC : float 64 | Chief Orbit Right Ascension (deg) 65 | MC : float 66 | Chief Orbit Mean Anomaly (deg) 67 | fR : float 68 | Formation Radial Amplitude (km) 69 | fI : float 70 | Formation In-Track Amplitude (km) 71 | fO : float 72 | Formation In-Track Offset (km) 73 | fC : float 74 | Formation Cross-Track Amplitude (km) 75 | fPhi : float 76 | Argument of Relative Pericenter (rad) 77 | fTht : float 78 | Argument of Latitude Crossing (rad) 79 | 80 | Returns 81 | ------- 82 | aD : float 83 | Deputy Orbit Semi-Major Axis (km) 84 | eD : float 85 | Deputy Orbit Eccentricity (0 to 1) 86 | iD : float 87 | Deputy Orbit Inclination (rad) 88 | wD : float 89 | Deputy Orbit Arg. of Perigee (rad) 90 | RD : float 91 | Deputy Orbit Right Ascension (rad) 92 | MD : float 93 | Deputy Orbit Mean Anomaly (rad) 94 | 95 | ''' 96 | 97 | # Convert all angles from degrees to radians. 98 | iC = np.deg2rad( iC ) 99 | wC = np.deg2rad( wC ) 100 | RC = np.deg2rad( RC ) 101 | MC = np.deg2rad( MC ) 102 | fPhi = np.deg2rad( fPhi ) 103 | fTht = np.deg2rad( fTht ) 104 | 105 | # First, the Hill-Clohessy-Wiltshire equations are predicated on the key 106 | # assumption that the chief and deputy share the same semi-major axis. 107 | aD = aC 108 | 109 | # Second, from the radial or in-track separation, we can derive the deputy 110 | # satellite's eccentricity and argument of perigee. 111 | de = fR / aC 112 | de_vect = np.array([ de * np.cos(fPhi), de * np.sin(fPhi) ]) 113 | eC_vect = np.array([ eC * np.cos( wC ), eC * np.sin( wC ) ]) 114 | eD_vect = eC_vect + de_vect 115 | eD = np.linalg.norm( eD_vect ) 116 | wD = np.arctan2( eD_vect[1], eD_vect[0] ) 117 | 118 | # Third, the relative inclination vector components allow us to derive the 119 | # deputy inclination and right ascension of the ascending node. 120 | di = np.sin( fC / aC ) 121 | di_vect = np.array([ di * np.cos(fTht), di * np.sin(fTht) ]) 122 | iD = iC + di_vect[0] 123 | RD = RC + ( di_vect[1] / np.sin(iC) ) 124 | 125 | # Fourth, we need to determine the argument of latitudes of the chief 126 | # (where argument of latitude = true anomaly + argument of perigee) 127 | nuC = anomaly.M2V( MC, eC ) 128 | 129 | # Finally, we need to ascertain the mean anomaly of the deputy. For a 130 | # bounded and centered formation geometry, it is expected that the mean 131 | # argument of latitude of the chief must be equal to the deputy. However, 132 | # if the user has specified a non-zero offset in the variable fO, then 133 | # this offset must be applied to the deputy satellite formation center. 134 | offset = fO / aD 135 | nuD = nuC + wC - wD + ( ( RD - RC ) * np.cos(iC) ) + offset 136 | MD = anomaly.V2M( nuD, eD ) 137 | 138 | # Finally, re-convert all angular output into degrees. 139 | iD = np.rad2deg( iD ) 140 | wD = np.rad2deg( wD ) 141 | RD = np.rad2deg( RD ) 142 | MD = np.rad2deg( MD ) 143 | 144 | return aD, eD, iD, wD, RD, MD 145 | -------------------------------------------------------------------------------- /source/targeting.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ############################################################################### 4 | ############################################################################### 5 | ## ## 6 | ## ___ _ _ __ ____ ____ __ _ _ _____ ## 7 | ## / _ \| | | | / \ | _ \| __ \ / \ | \ | |_ _| ## 8 | ## ( |_| ) |_| |/ /\ \| |_| | -/ // /\ \| \| | | | ## 9 | ## \_ /|_____| /--\ |____/|_|\_\ /--\ |_\___| |_| ## 10 | ## \/ v 0.0 ## 11 | ## ## 12 | ## Returns reference DCM and OHM of the target, for attitude reference. ## 13 | ## ## 14 | ## Written by Samuel Y. W. Low. ## 15 | ## First created 23-Nov-2021 23:01 PM (-8 GMT) ## 16 | ## Last modified 15-Mar-2022 13:05 AM (-8 GMT) ## 17 | ## ## 18 | ############################################################################### 19 | ############################################################################### 20 | 21 | import numpy as np 22 | from numpy.linalg import norm as norm 23 | from source import anomaly 24 | from source import rotation 25 | 26 | 27 | 28 | ############################################################################### 29 | ############################################################################### 30 | ### ### 31 | ### Pointing DCM & OHM reference as a deputy satellite in the formation. ### 32 | ### ### 33 | ############################################################################### 34 | ############################################################################### 35 | 36 | 37 | 38 | ############################################################################### 39 | ############################################################################### 40 | ### ### 41 | ### Pointing DCM & OHM reference as the Sun, defined to ### 42 | ### be in the inertial frame Y-axis direction. ### 43 | ### ### 44 | ############################################################################### 45 | ############################################################################### 46 | 47 | def reference_sun(): 48 | 49 | # For the sun-pointing mode, we have defined the sun to always be 50 | # located when the main spacecraft points in the n2 unit direction 51 | # vector (and where n3 is the direction vector pointing out of the 52 | # ecliptic, and n1 x n2 = n3). The dcmSN is defined below. 53 | 54 | # Inputs: None. Since the rotation from inertial to sun-pointing is a 55 | # fixed rotation (not time-varying), external inputs are not needed. 56 | 57 | # Output: Reference attitude in 3x3 DCM and reference angular velocity 58 | # All outputs taken with respect to the inertial frame (i.e., RN frame). 59 | 60 | # Sun-pointing from inertial frame is fixed by definition. 61 | dcmSN = np.array([ [-1,0,0], [0,0,1], [0,1,0] ]) 62 | ohmSN = [ 0.0, 0.0, 0.0 ] 63 | 64 | return dcmSN, ohmSN 65 | 66 | ############################################################################### 67 | ############################################################################### 68 | ### ### 69 | ### Pointing towards Earth in Nadir direction. ### 70 | ### ### 71 | ############################################################################### 72 | ############################################################################### 73 | 74 | def reference_hill( sC ): 75 | p = np.array([ sC.px, sC.py, sC.pz ]) 76 | v = np.array([ sC.vx, sC.vy, sC.vz ]) 77 | dcmHN = sC.get_hill_frame() 78 | ohmHN = np.cross(p,v) / np.dot(p,p) # N-frame coordinates 79 | return dcmHN, ohmHN 80 | 81 | def reference_nadir( sC ): 82 | 83 | # For the nadir-pointing mode, the nadir is always fixed in definition 84 | # with respect to the hill frame of the main spacecraft. Thus, we need 85 | # to compute the hill frame first, and then rotate hill to nadir. The 86 | # hill frame needs the S/C position and velocity in the inertial frame. 87 | # Note, the angular velocity OHM_RN = OHM_HN, because the nadir pointing 88 | # frame is actually a fixed rotation from the standard hill frame. 89 | 90 | dcmHN, ohmHN = reference_hill( sC ) 91 | dcmRH = np.array([ [-1,0,0 ], [0,1,0 ], [0,0,-1] ]) 92 | dcmRN = dcmRH @ dcmHN 93 | ohmRN = ohmHN 94 | return dcmRN, ohmRN 95 | 96 | def reference_deputy( dt, sC, sD ): 97 | 98 | # For the inertial to inter-satellite communications frame, denoted as 99 | # CN, we need to first resolve the three axes of the pointing frame by 100 | # determining the relative position vectors from the main to the 101 | # secondary spacecraft. The antenna of the main spacecraft is assumed 102 | # to be pointing in the positive b1 axis (body-frame) for now. 103 | 104 | # Current relative states 105 | posCi = np.array([ sC.px, sC.py, sC.pz ]) 106 | posDi = np.array([ sD.px, sD.py, sD.pz ]) 107 | posCDi = posDi - posCi 108 | posXi = np.cross( posCDi, [0,0,1] ) 109 | 110 | # Back-propagate the ephemeris by one time step. 111 | sC.propagate_orbit( -1*dt ) 112 | sD.propagate_orbit( -1*dt ) 113 | 114 | # Previous relative states 115 | posCf = np.array([ sC.px, sC.py, sC.pz ]) 116 | posDf = np.array([ sD.px, sD.py, sD.pz ]) 117 | posCDf = posDf - posCf 118 | posXf = np.cross( posCDf, [0,0,1] ) 119 | 120 | # Compute current inertial to communications pointing frame 121 | dcmCN_X = posCDi / norm( posCDi ) 122 | dcmCN_Y = posXi / norm( posXi ) 123 | dcmCN_Z = np.cross( dcmCN_X, dcmCN_Y ) 124 | dcmCN = np.array([ dcmCN_X, dcmCN_Y, dcmCN_Z ]) 125 | 126 | # Compute previous step inertial to communications pointing frame 127 | dcmCN_Xf = posCDf / norm( posCDf ) 128 | dcmCN_Yf = posXf / norm( posXf ) 129 | dcmCN_Zf = np.cross( dcmCN_Xf, dcmCN_Yf ) 130 | dcmCNf = np.array([ dcmCN_Xf, dcmCN_Yf, dcmCN_Zf ]) 131 | 132 | # Estimate the rate of change of the DCM numerically. 133 | dcmCN_dot = (dcmCN - dcmCNf) / dt 134 | 135 | # Finally, we need to compute the angular velocity of relative 136 | # pointing frame, as seen in the inertial frame. 137 | ohmCN_tilde = -1 * dcmCN.T @ dcmCN_dot 138 | ohmCN = [ ohmCN_tilde[2,1], ohmCN_tilde[0,2], ohmCN_tilde[1,0] ] 139 | 140 | # Forward propagate the ephemeris by one time step. 141 | sC.propagate_orbit( dt ) 142 | sD.propagate_orbit( dt ) 143 | 144 | return dcmCN, ohmCN -------------------------------------------------------------------------------- /docs/_build/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | QUADRANT — QUADRANT 0.0 documentation 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 54 | 55 |
59 | 60 |
61 |
62 |
63 | 70 |
71 |
72 |
73 |
74 | 75 | _images/quadrant_logo.png 76 |
77 |

78 |
79 |
80 |
Github
81 |

https://github.com/sammmlow/quadrant

82 |
83 |
Documents
84 |

https://quadrant.readthedocs.io/en/latest/

85 |
86 |
Version
87 |

0.0 (Trial)

88 |
89 |
Author
90 |

Samuel Y. W. Low

91 |
92 |
93 |

docs license linkedin orcid

94 |
95 |

QUADRANT

96 |

QUADRANT is an attitude control simulation library for multi-agent spacecraft, work in progress.

97 |

Documentation tree below.

98 |
99 |

Getting Started

100 | 104 |
105 |
106 |

Advanced References

107 | 110 |
111 |
112 |
113 |

For bugs, raise the issues in the GitHub repository. For collaborations, reach out to me (sammmlow@gmail.com). The project is licensed under the MIT license.

114 |

Written By: Samuel Y. W. Low

115 |

linkedin orcid

116 |
117 | 118 | 119 |
120 |
121 |
124 | 125 |
126 | 127 |
128 |

© Copyright 2022, Samuel Y. W. Low.

129 |
130 | 131 | Built with Sphinx using a 132 | theme 133 | provided by Read the Docs. 134 | 135 | 136 |
137 |
138 |
139 |
140 |
141 | 146 | 147 | 148 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /docs/_build/html/genindex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Index — QUADRANT 0.0 documentation 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 52 | 53 |
57 | 58 |
59 |
60 |
61 |
    62 |
  • »
  • 63 |
  • Index
  • 64 |
  • 65 |
  • 66 |
67 |
68 |
69 |
70 |
71 | 72 | 73 |

Index

74 | 75 |
76 | A 77 | | C 78 | | E 79 | | G 80 | | M 81 | | N 82 | | Q 83 | | S 84 | 85 |
86 |

A

87 | 88 | 92 | 101 |
    93 |
  • 94 | attitudes 95 | 96 |
  • 100 |
102 | 103 |

C

104 | 105 | 109 |
110 | 111 |

E

112 | 113 | 117 |
118 | 119 |

G

120 | 121 | 125 |
126 | 127 |

M

128 | 129 | 142 |
143 | 144 |

N

145 | 146 | 150 |
151 | 152 |

Q

153 | 154 | 158 |
159 | 160 |

S

161 | 162 | 171 | 177 |
    163 |
  • 164 | spacecraft 165 | 166 |
  • 170 |
178 | 179 | 180 | 181 |
182 |
183 |
184 | 185 |
186 | 187 |
188 |

© Copyright 2022, Samuel Y. W. Low.

189 |
190 | 191 | Built with Sphinx using a 192 | theme 193 | provided by Read the Docs. 194 | 195 | 196 |
197 |
198 |
199 |
200 |
201 | 206 | 207 | 208 | 217 | 218 | 219 | -------------------------------------------------------------------------------- /docs/_build/html/_static/doctools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * doctools.js 3 | * ~~~~~~~~~~~ 4 | * 5 | * Sphinx JavaScript utilities for all documentation. 6 | * 7 | * :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | /** 13 | * select a different prefix for underscore 14 | */ 15 | $u = _.noConflict(); 16 | 17 | /** 18 | * make the code below compatible with browsers without 19 | * an installed firebug like debugger 20 | if (!window.console || !console.firebug) { 21 | var names = ["log", "debug", "info", "warn", "error", "assert", "dir", 22 | "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", 23 | "profile", "profileEnd"]; 24 | window.console = {}; 25 | for (var i = 0; i < names.length; ++i) 26 | window.console[names[i]] = function() {}; 27 | } 28 | */ 29 | 30 | /** 31 | * small helper function to urldecode strings 32 | * 33 | * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL 34 | */ 35 | jQuery.urldecode = function(x) { 36 | if (!x) { 37 | return x 38 | } 39 | return decodeURIComponent(x.replace(/\+/g, ' ')); 40 | }; 41 | 42 | /** 43 | * small helper function to urlencode strings 44 | */ 45 | jQuery.urlencode = encodeURIComponent; 46 | 47 | /** 48 | * This function returns the parsed url parameters of the 49 | * current request. Multiple values per key are supported, 50 | * it will always return arrays of strings for the value parts. 51 | */ 52 | jQuery.getQueryParameters = function(s) { 53 | if (typeof s === 'undefined') 54 | s = document.location.search; 55 | var parts = s.substr(s.indexOf('?') + 1).split('&'); 56 | var result = {}; 57 | for (var i = 0; i < parts.length; i++) { 58 | var tmp = parts[i].split('=', 2); 59 | var key = jQuery.urldecode(tmp[0]); 60 | var value = jQuery.urldecode(tmp[1]); 61 | if (key in result) 62 | result[key].push(value); 63 | else 64 | result[key] = [value]; 65 | } 66 | return result; 67 | }; 68 | 69 | /** 70 | * highlight a given string on a jquery object by wrapping it in 71 | * span elements with the given class name. 72 | */ 73 | jQuery.fn.highlightText = function(text, className) { 74 | function highlight(node, addItems) { 75 | if (node.nodeType === 3) { 76 | var val = node.nodeValue; 77 | var pos = val.toLowerCase().indexOf(text); 78 | if (pos >= 0 && 79 | !jQuery(node.parentNode).hasClass(className) && 80 | !jQuery(node.parentNode).hasClass("nohighlight")) { 81 | var span; 82 | var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); 83 | if (isInSVG) { 84 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); 85 | } else { 86 | span = document.createElement("span"); 87 | span.className = className; 88 | } 89 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 90 | node.parentNode.insertBefore(span, node.parentNode.insertBefore( 91 | document.createTextNode(val.substr(pos + text.length)), 92 | node.nextSibling)); 93 | node.nodeValue = val.substr(0, pos); 94 | if (isInSVG) { 95 | var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); 96 | var bbox = node.parentElement.getBBox(); 97 | rect.x.baseVal.value = bbox.x; 98 | rect.y.baseVal.value = bbox.y; 99 | rect.width.baseVal.value = bbox.width; 100 | rect.height.baseVal.value = bbox.height; 101 | rect.setAttribute('class', className); 102 | addItems.push({ 103 | "parent": node.parentNode, 104 | "target": rect}); 105 | } 106 | } 107 | } 108 | else if (!jQuery(node).is("button, select, textarea")) { 109 | jQuery.each(node.childNodes, function() { 110 | highlight(this, addItems); 111 | }); 112 | } 113 | } 114 | var addItems = []; 115 | var result = this.each(function() { 116 | highlight(this, addItems); 117 | }); 118 | for (var i = 0; i < addItems.length; ++i) { 119 | jQuery(addItems[i].parent).before(addItems[i].target); 120 | } 121 | return result; 122 | }; 123 | 124 | /* 125 | * backward compatibility for jQuery.browser 126 | * This will be supported until firefox bug is fixed. 127 | */ 128 | if (!jQuery.browser) { 129 | jQuery.uaMatch = function(ua) { 130 | ua = ua.toLowerCase(); 131 | 132 | var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || 133 | /(webkit)[ \/]([\w.]+)/.exec(ua) || 134 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || 135 | /(msie) ([\w.]+)/.exec(ua) || 136 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || 137 | []; 138 | 139 | return { 140 | browser: match[ 1 ] || "", 141 | version: match[ 2 ] || "0" 142 | }; 143 | }; 144 | jQuery.browser = {}; 145 | jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; 146 | } 147 | 148 | /** 149 | * Small JavaScript module for the documentation. 150 | */ 151 | var Documentation = { 152 | 153 | init : function() { 154 | this.fixFirefoxAnchorBug(); 155 | this.highlightSearchWords(); 156 | this.initIndexTable(); 157 | if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) { 158 | this.initOnKeyListeners(); 159 | } 160 | }, 161 | 162 | /** 163 | * i18n support 164 | */ 165 | TRANSLATIONS : {}, 166 | PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, 167 | LOCALE : 'unknown', 168 | 169 | // gettext and ngettext don't access this so that the functions 170 | // can safely bound to a different name (_ = Documentation.gettext) 171 | gettext : function(string) { 172 | var translated = Documentation.TRANSLATIONS[string]; 173 | if (typeof translated === 'undefined') 174 | return string; 175 | return (typeof translated === 'string') ? translated : translated[0]; 176 | }, 177 | 178 | ngettext : function(singular, plural, n) { 179 | var translated = Documentation.TRANSLATIONS[singular]; 180 | if (typeof translated === 'undefined') 181 | return (n == 1) ? singular : plural; 182 | return translated[Documentation.PLURALEXPR(n)]; 183 | }, 184 | 185 | addTranslations : function(catalog) { 186 | for (var key in catalog.messages) 187 | this.TRANSLATIONS[key] = catalog.messages[key]; 188 | this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); 189 | this.LOCALE = catalog.locale; 190 | }, 191 | 192 | /** 193 | * add context elements like header anchor links 194 | */ 195 | addContextElements : function() { 196 | $('div[id] > :header:first').each(function() { 197 | $('\u00B6'). 198 | attr('href', '#' + this.id). 199 | attr('title', _('Permalink to this headline')). 200 | appendTo(this); 201 | }); 202 | $('dt[id]').each(function() { 203 | $('\u00B6'). 204 | attr('href', '#' + this.id). 205 | attr('title', _('Permalink to this definition')). 206 | appendTo(this); 207 | }); 208 | }, 209 | 210 | /** 211 | * workaround a firefox stupidity 212 | * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 213 | */ 214 | fixFirefoxAnchorBug : function() { 215 | if (document.location.hash && $.browser.mozilla) 216 | window.setTimeout(function() { 217 | document.location.href += ''; 218 | }, 10); 219 | }, 220 | 221 | /** 222 | * highlight the search words provided in the url in the text 223 | */ 224 | highlightSearchWords : function() { 225 | var params = $.getQueryParameters(); 226 | var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; 227 | if (terms.length) { 228 | var body = $('div.body'); 229 | if (!body.length) { 230 | body = $('body'); 231 | } 232 | window.setTimeout(function() { 233 | $.each(terms, function() { 234 | body.highlightText(this.toLowerCase(), 'highlighted'); 235 | }); 236 | }, 10); 237 | $('') 239 | .appendTo($('#searchbox')); 240 | } 241 | }, 242 | 243 | /** 244 | * init the domain index toggle buttons 245 | */ 246 | initIndexTable : function() { 247 | var togglers = $('img.toggler').click(function() { 248 | var src = $(this).attr('src'); 249 | var idnum = $(this).attr('id').substr(7); 250 | $('tr.cg-' + idnum).toggle(); 251 | if (src.substr(-9) === 'minus.png') 252 | $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); 253 | else 254 | $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); 255 | }).css('display', ''); 256 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { 257 | togglers.click(); 258 | } 259 | }, 260 | 261 | /** 262 | * helper function to hide the search marks again 263 | */ 264 | hideSearchWords : function() { 265 | $('#searchbox .highlight-link').fadeOut(300); 266 | $('span.highlighted').removeClass('highlighted'); 267 | }, 268 | 269 | /** 270 | * make the url absolute 271 | */ 272 | makeURL : function(relativeURL) { 273 | return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; 274 | }, 275 | 276 | /** 277 | * get the current relative url 278 | */ 279 | getCurrentURL : function() { 280 | var path = document.location.pathname; 281 | var parts = path.split(/\//); 282 | $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { 283 | if (this === '..') 284 | parts.pop(); 285 | }); 286 | var url = parts.join('/'); 287 | return path.substring(url.lastIndexOf('/') + 1, path.length - 1); 288 | }, 289 | 290 | initOnKeyListeners: function() { 291 | $(document).keydown(function(event) { 292 | var activeElementType = document.activeElement.tagName; 293 | // don't navigate when in search box, textarea, dropdown or button 294 | if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT' 295 | && activeElementType !== 'BUTTON' && !event.altKey && !event.ctrlKey && !event.metaKey 296 | && !event.shiftKey) { 297 | switch (event.keyCode) { 298 | case 37: // left 299 | var prevHref = $('link[rel="prev"]').prop('href'); 300 | if (prevHref) { 301 | window.location.href = prevHref; 302 | return false; 303 | } 304 | case 39: // right 305 | var nextHref = $('link[rel="next"]').prop('href'); 306 | if (nextHref) { 307 | window.location.href = nextHref; 308 | return false; 309 | } 310 | } 311 | } 312 | }); 313 | } 314 | }; 315 | 316 | // quick alias for translations 317 | _ = Documentation.gettext; 318 | 319 | $(document).ready(function() { 320 | Documentation.init(); 321 | }); 322 | -------------------------------------------------------------------------------- /docs/_build/html/_static/language_data.js: -------------------------------------------------------------------------------- 1 | /* 2 | * language_data.js 3 | * ~~~~~~~~~~~~~~~~ 4 | * 5 | * This script contains the language-specific data used by searchtools.js, 6 | * namely the list of stopwords, stemmer, scorer and splitter. 7 | * 8 | * :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. 9 | * :license: BSD, see LICENSE for details. 10 | * 11 | */ 12 | 13 | var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"]; 14 | 15 | 16 | /* Non-minified version is copied as a separate JS file, is available */ 17 | 18 | /** 19 | * Porter Stemmer 20 | */ 21 | var Stemmer = function() { 22 | 23 | var step2list = { 24 | ational: 'ate', 25 | tional: 'tion', 26 | enci: 'ence', 27 | anci: 'ance', 28 | izer: 'ize', 29 | bli: 'ble', 30 | alli: 'al', 31 | entli: 'ent', 32 | eli: 'e', 33 | ousli: 'ous', 34 | ization: 'ize', 35 | ation: 'ate', 36 | ator: 'ate', 37 | alism: 'al', 38 | iveness: 'ive', 39 | fulness: 'ful', 40 | ousness: 'ous', 41 | aliti: 'al', 42 | iviti: 'ive', 43 | biliti: 'ble', 44 | logi: 'log' 45 | }; 46 | 47 | var step3list = { 48 | icate: 'ic', 49 | ative: '', 50 | alize: 'al', 51 | iciti: 'ic', 52 | ical: 'ic', 53 | ful: '', 54 | ness: '' 55 | }; 56 | 57 | var c = "[^aeiou]"; // consonant 58 | var v = "[aeiouy]"; // vowel 59 | var C = c + "[^aeiouy]*"; // consonant sequence 60 | var V = v + "[aeiou]*"; // vowel sequence 61 | 62 | var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 63 | var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 64 | var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 65 | var s_v = "^(" + C + ")?" + v; // vowel in stem 66 | 67 | this.stemWord = function (w) { 68 | var stem; 69 | var suffix; 70 | var firstch; 71 | var origword = w; 72 | 73 | if (w.length < 3) 74 | return w; 75 | 76 | var re; 77 | var re2; 78 | var re3; 79 | var re4; 80 | 81 | firstch = w.substr(0,1); 82 | if (firstch == "y") 83 | w = firstch.toUpperCase() + w.substr(1); 84 | 85 | // Step 1a 86 | re = /^(.+?)(ss|i)es$/; 87 | re2 = /^(.+?)([^s])s$/; 88 | 89 | if (re.test(w)) 90 | w = w.replace(re,"$1$2"); 91 | else if (re2.test(w)) 92 | w = w.replace(re2,"$1$2"); 93 | 94 | // Step 1b 95 | re = /^(.+?)eed$/; 96 | re2 = /^(.+?)(ed|ing)$/; 97 | if (re.test(w)) { 98 | var fp = re.exec(w); 99 | re = new RegExp(mgr0); 100 | if (re.test(fp[1])) { 101 | re = /.$/; 102 | w = w.replace(re,""); 103 | } 104 | } 105 | else if (re2.test(w)) { 106 | var fp = re2.exec(w); 107 | stem = fp[1]; 108 | re2 = new RegExp(s_v); 109 | if (re2.test(stem)) { 110 | w = stem; 111 | re2 = /(at|bl|iz)$/; 112 | re3 = new RegExp("([^aeiouylsz])\\1$"); 113 | re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 114 | if (re2.test(w)) 115 | w = w + "e"; 116 | else if (re3.test(w)) { 117 | re = /.$/; 118 | w = w.replace(re,""); 119 | } 120 | else if (re4.test(w)) 121 | w = w + "e"; 122 | } 123 | } 124 | 125 | // Step 1c 126 | re = /^(.+?)y$/; 127 | if (re.test(w)) { 128 | var fp = re.exec(w); 129 | stem = fp[1]; 130 | re = new RegExp(s_v); 131 | if (re.test(stem)) 132 | w = stem + "i"; 133 | } 134 | 135 | // Step 2 136 | re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; 137 | if (re.test(w)) { 138 | var fp = re.exec(w); 139 | stem = fp[1]; 140 | suffix = fp[2]; 141 | re = new RegExp(mgr0); 142 | if (re.test(stem)) 143 | w = stem + step2list[suffix]; 144 | } 145 | 146 | // Step 3 147 | re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; 148 | if (re.test(w)) { 149 | var fp = re.exec(w); 150 | stem = fp[1]; 151 | suffix = fp[2]; 152 | re = new RegExp(mgr0); 153 | if (re.test(stem)) 154 | w = stem + step3list[suffix]; 155 | } 156 | 157 | // Step 4 158 | re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; 159 | re2 = /^(.+?)(s|t)(ion)$/; 160 | if (re.test(w)) { 161 | var fp = re.exec(w); 162 | stem = fp[1]; 163 | re = new RegExp(mgr1); 164 | if (re.test(stem)) 165 | w = stem; 166 | } 167 | else if (re2.test(w)) { 168 | var fp = re2.exec(w); 169 | stem = fp[1] + fp[2]; 170 | re2 = new RegExp(mgr1); 171 | if (re2.test(stem)) 172 | w = stem; 173 | } 174 | 175 | // Step 5 176 | re = /^(.+?)e$/; 177 | if (re.test(w)) { 178 | var fp = re.exec(w); 179 | stem = fp[1]; 180 | re = new RegExp(mgr1); 181 | re2 = new RegExp(meq1); 182 | re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 183 | if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) 184 | w = stem; 185 | } 186 | re = /ll$/; 187 | re2 = new RegExp(mgr1); 188 | if (re.test(w) && re2.test(w)) { 189 | re = /.$/; 190 | w = w.replace(re,""); 191 | } 192 | 193 | // and turn initial Y back to y 194 | if (firstch == "y") 195 | w = firstch.toLowerCase() + w.substr(1); 196 | return w; 197 | } 198 | } 199 | 200 | 201 | 202 | 203 | var splitChars = (function() { 204 | var result = {}; 205 | var singles = [96, 180, 187, 191, 215, 247, 749, 885, 903, 907, 909, 930, 1014, 1648, 206 | 1748, 1809, 2416, 2473, 2481, 2526, 2601, 2609, 2612, 2615, 2653, 2702, 207 | 2706, 2729, 2737, 2740, 2857, 2865, 2868, 2910, 2928, 2948, 2961, 2971, 208 | 2973, 3085, 3089, 3113, 3124, 3213, 3217, 3241, 3252, 3295, 3341, 3345, 209 | 3369, 3506, 3516, 3633, 3715, 3721, 3736, 3744, 3748, 3750, 3756, 3761, 210 | 3781, 3912, 4239, 4347, 4681, 4695, 4697, 4745, 4785, 4799, 4801, 4823, 211 | 4881, 5760, 5901, 5997, 6313, 7405, 8024, 8026, 8028, 8030, 8117, 8125, 212 | 8133, 8181, 8468, 8485, 8487, 8489, 8494, 8527, 11311, 11359, 11687, 11695, 213 | 11703, 11711, 11719, 11727, 11735, 12448, 12539, 43010, 43014, 43019, 43587, 214 | 43696, 43713, 64286, 64297, 64311, 64317, 64319, 64322, 64325, 65141]; 215 | var i, j, start, end; 216 | for (i = 0; i < singles.length; i++) { 217 | result[singles[i]] = true; 218 | } 219 | var ranges = [[0, 47], [58, 64], [91, 94], [123, 169], [171, 177], [182, 184], [706, 709], 220 | [722, 735], [741, 747], [751, 879], [888, 889], [894, 901], [1154, 1161], 221 | [1318, 1328], [1367, 1368], [1370, 1376], [1416, 1487], [1515, 1519], [1523, 1568], 222 | [1611, 1631], [1642, 1645], [1750, 1764], [1767, 1773], [1789, 1790], [1792, 1807], 223 | [1840, 1868], [1958, 1968], [1970, 1983], [2027, 2035], [2038, 2041], [2043, 2047], 224 | [2070, 2073], [2075, 2083], [2085, 2087], [2089, 2307], [2362, 2364], [2366, 2383], 225 | [2385, 2391], [2402, 2405], [2419, 2424], [2432, 2436], [2445, 2446], [2449, 2450], 226 | [2483, 2485], [2490, 2492], [2494, 2509], [2511, 2523], [2530, 2533], [2546, 2547], 227 | [2554, 2564], [2571, 2574], [2577, 2578], [2618, 2648], [2655, 2661], [2672, 2673], 228 | [2677, 2692], [2746, 2748], [2750, 2767], [2769, 2783], [2786, 2789], [2800, 2820], 229 | [2829, 2830], [2833, 2834], [2874, 2876], [2878, 2907], [2914, 2917], [2930, 2946], 230 | [2955, 2957], [2966, 2968], [2976, 2978], [2981, 2983], [2987, 2989], [3002, 3023], 231 | [3025, 3045], [3059, 3076], [3130, 3132], [3134, 3159], [3162, 3167], [3170, 3173], 232 | [3184, 3191], [3199, 3204], [3258, 3260], [3262, 3293], [3298, 3301], [3312, 3332], 233 | [3386, 3388], [3390, 3423], [3426, 3429], [3446, 3449], [3456, 3460], [3479, 3481], 234 | [3518, 3519], [3527, 3584], [3636, 3647], [3655, 3663], [3674, 3712], [3717, 3718], 235 | [3723, 3724], [3726, 3731], [3752, 3753], [3764, 3772], [3774, 3775], [3783, 3791], 236 | [3802, 3803], [3806, 3839], [3841, 3871], [3892, 3903], [3949, 3975], [3980, 4095], 237 | [4139, 4158], [4170, 4175], [4182, 4185], [4190, 4192], [4194, 4196], [4199, 4205], 238 | [4209, 4212], [4226, 4237], [4250, 4255], [4294, 4303], [4349, 4351], [4686, 4687], 239 | [4702, 4703], [4750, 4751], [4790, 4791], [4806, 4807], [4886, 4887], [4955, 4968], 240 | [4989, 4991], [5008, 5023], [5109, 5120], [5741, 5742], [5787, 5791], [5867, 5869], 241 | [5873, 5887], [5906, 5919], [5938, 5951], [5970, 5983], [6001, 6015], [6068, 6102], 242 | [6104, 6107], [6109, 6111], [6122, 6127], [6138, 6159], [6170, 6175], [6264, 6271], 243 | [6315, 6319], [6390, 6399], [6429, 6469], [6510, 6511], [6517, 6527], [6572, 6592], 244 | [6600, 6607], [6619, 6655], [6679, 6687], [6741, 6783], [6794, 6799], [6810, 6822], 245 | [6824, 6916], [6964, 6980], [6988, 6991], [7002, 7042], [7073, 7085], [7098, 7167], 246 | [7204, 7231], [7242, 7244], [7294, 7400], [7410, 7423], [7616, 7679], [7958, 7959], 247 | [7966, 7967], [8006, 8007], [8014, 8015], [8062, 8063], [8127, 8129], [8141, 8143], 248 | [8148, 8149], [8156, 8159], [8173, 8177], [8189, 8303], [8306, 8307], [8314, 8318], 249 | [8330, 8335], [8341, 8449], [8451, 8454], [8456, 8457], [8470, 8472], [8478, 8483], 250 | [8506, 8507], [8512, 8516], [8522, 8525], [8586, 9311], [9372, 9449], [9472, 10101], 251 | [10132, 11263], [11493, 11498], [11503, 11516], [11518, 11519], [11558, 11567], 252 | [11622, 11630], [11632, 11647], [11671, 11679], [11743, 11822], [11824, 12292], 253 | [12296, 12320], [12330, 12336], [12342, 12343], [12349, 12352], [12439, 12444], 254 | [12544, 12548], [12590, 12592], [12687, 12689], [12694, 12703], [12728, 12783], 255 | [12800, 12831], [12842, 12880], [12896, 12927], [12938, 12976], [12992, 13311], 256 | [19894, 19967], [40908, 40959], [42125, 42191], [42238, 42239], [42509, 42511], 257 | [42540, 42559], [42592, 42593], [42607, 42622], [42648, 42655], [42736, 42774], 258 | [42784, 42785], [42889, 42890], [42893, 43002], [43043, 43055], [43062, 43071], 259 | [43124, 43137], [43188, 43215], [43226, 43249], [43256, 43258], [43260, 43263], 260 | [43302, 43311], [43335, 43359], [43389, 43395], [43443, 43470], [43482, 43519], 261 | [43561, 43583], [43596, 43599], [43610, 43615], [43639, 43641], [43643, 43647], 262 | [43698, 43700], [43703, 43704], [43710, 43711], [43715, 43738], [43742, 43967], 263 | [44003, 44015], [44026, 44031], [55204, 55215], [55239, 55242], [55292, 55295], 264 | [57344, 63743], [64046, 64047], [64110, 64111], [64218, 64255], [64263, 64274], 265 | [64280, 64284], [64434, 64466], [64830, 64847], [64912, 64913], [64968, 65007], 266 | [65020, 65135], [65277, 65295], [65306, 65312], [65339, 65344], [65371, 65381], 267 | [65471, 65473], [65480, 65481], [65488, 65489], [65496, 65497]]; 268 | for (i = 0; i < ranges.length; i++) { 269 | start = ranges[i][0]; 270 | end = ranges[i][1]; 271 | for (j = start; j <= end; j++) { 272 | result[j] = true; 273 | } 274 | } 275 | return result; 276 | })(); 277 | 278 | function splitQuery(query) { 279 | var result = []; 280 | var start = -1; 281 | for (var i = 0; i < query.length; i++) { 282 | if (splitChars[query.charCodeAt(i)]) { 283 | if (start !== -1) { 284 | result.push(query.slice(start, i)); 285 | start = -1; 286 | } 287 | } else if (start === -1) { 288 | start = i; 289 | } 290 | } 291 | if (start !== -1) { 292 | result.push(query.slice(start)); 293 | } 294 | return result; 295 | } 296 | 297 | 298 | -------------------------------------------------------------------------------- /scenarios/pointing_policy/policy1_random.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ############################################################################### 4 | ############################################################################### 5 | ## ## 6 | ## ___ _ _ __ ____ ____ __ _ _ _____ ## 7 | ## / _ \| | | | / \ | _ \| __ \ / \ | \ | |_ _| ## 8 | ## ( |_| ) |_| |/ /\ \| |_| | -/ // /\ \| \| | | | ## 9 | ## \_ /|_____| /--\ |____/|_|\_\ /--\ |_\___| |_| ## 10 | ## \/ v 0.0 ## 11 | ## ## 12 | ## Scenario: Optimal Pointing Sequences in Spacecraft Formation Flying ## 13 | ## using Online Planning with Resource Constraints ## 14 | ## ## 15 | ## Written by Samuel Y. W. Low. ## 16 | ## Advised by Professor Mykel J. Kochenderfer ## 17 | ## First created 23-Nov-2021 23:01 PM (-8 GMT) ## 18 | ## Last modified 15-Mar-2022 13:05 AM (-8 GMT) ## 19 | ## ## 20 | ############################################################################### 21 | ############################################################################### 22 | 23 | # Import global libraries. 24 | import numpy as np 25 | import os, csv, math 26 | from copy import deepcopy 27 | import matplotlib.pyplot as plt 28 | 29 | # Move the current directory up until the Quadrant root. 30 | while os.getcwd().split("\\")[-1] != "quadrant": 31 | os.chdir("..") 32 | 33 | # Import source libraries. 34 | from source import spacecraft, attitudes, targeting, feedback, deputy 35 | 36 | # Initialise the number of Monte Carlo trials. If the number of trials is 37 | # set > 1, then the orbital elements of the deputies will be randomized 38 | # about the elements given in the ephemeris file. If trials = 1, then the 39 | # exact ephemeris elements will be used. 40 | trials = 1 41 | 42 | # Initialise dynamic and control time step. 43 | dt, ct = 1.0, 1.0 44 | 45 | # Initialise reinforcement learning hyper-parameters. 46 | γ = 0.75 # Gamma: Discount factor in Bellman update ( < 1.0) 47 | λ = 2.0 # Lambda: Power charging urgency constant (float) 48 | μ = 0.00017783 # Mu: Soft max precision parameter for rewards (float) 49 | 50 | # Initialise the resource parameters. 51 | P = 1.0 # Power level, must be between 0 and 1 52 | P_drain = 0.0005 # Power drain rate per second, between 0 and 1 53 | P_charge = 0.0025 # Power charge rate per second, between 0 and 1 54 | 55 | # Initialise attitude control gains 56 | Kp, Ki, Kd = 0.016, 0.0, 0.4 # Initial (Lyapunov) control gains 57 | 58 | 59 | ############################################################################### 60 | ############################################################################### 61 | 62 | 63 | # Define the reward for sun pointing. 64 | def reward_sun( P, λ, μ, C ): 65 | # P = power level of chief spacecraft, between 0 and 1. 66 | # λ = power charging priority hyper-parameter. 67 | # μ = soft-max hyper-parameter to tune reward hardness. 68 | # C = expected duration of sun pointing plus charging. 69 | α = ( 1 - max(0,P) ) ** λ 70 | return α / math.exp(μ*C) 71 | 72 | 73 | # Define the reward for deputy pointing. 74 | def reward_deputy( P, λ, μ, δ ): 75 | # P = power level of chief spacecraft, between 0 and 1. 76 | # λ = power charging priority hyper-parameter. 77 | # μ = soft-max hyper-parameter to tune reward hardness. 78 | # δ = individual expected slew time to a particular deputy. 79 | if P <= 0.01: 80 | return -1.0 81 | else: 82 | α = ( 1 - max(0,P) ) ** λ 83 | return (1-α) / math.exp(μ*δ) 84 | 85 | 86 | # Define the plotting of rewards. 87 | def plot_rewards(): 88 | plt.close('all') 89 | p_axis = np.arange( 0.02, 1.0, 0.01 ) 90 | t_axis = np.arange( 0.0, 1500.0, 10.0 ) 91 | p_grid, t_grid = np.meshgrid( p_axis, t_axis ) 92 | Z_sun_grid = np.zeros( np.shape(p_grid) ) 93 | Z_sat_grid = np.zeros( np.shape(p_grid) ) 94 | for i in range( len(p_axis) ): 95 | for j in range( len(t_axis) ): 96 | p, t = p_axis[i], t_axis[j] 97 | Z_sun_grid[j][i] = reward_sun( p, λ, μ, t ) 98 | Z_sat_grid[j][i] = reward_deputy( p, λ, μ, t ) 99 | plt.figure(1) 100 | plt.title('Reward distribution for a sun-pointing state.') 101 | plt.contourf(p_grid, t_grid, Z_sun_grid, levels=50) 102 | plt.colorbar() 103 | plt.xlabel('State: Current power level prior to state (0 to 1)') 104 | plt.ylabel('Action: Charging and attitude control duration [sec]') 105 | plt.figure(2) 106 | plt.title('Reward distribution for a deputy-pointing state.') 107 | plt.contourf(p_grid, t_grid, Z_sat_grid, levels=50) 108 | plt.colorbar() 109 | plt.xlabel('State: Current power level prior to state (0 to 1)') 110 | plt.ylabel('Action: Attitude control duration [sec]') 111 | 112 | 113 | # Define the transition probability between states. 114 | def transition( Sf, Si, P, λ ): 115 | # Sf = final state 116 | # Si = initial state 117 | # P = power level of chief spacecraft, between 0 and 1. 118 | # λ = power charging priority constant (hyper-parameter) 119 | if P <= 0.0: 120 | α = 1.0 121 | else: 122 | α = (1-P)**λ 123 | if Sf == 'Sun Pointing' and Si == 'Sun Pointing': 124 | return 0.0 125 | elif Sf == 'Sun Pointing' and Si != 'Sun Pointing': 126 | return α 127 | elif Sf != 'Sun Pointing' and Si != 'Sun Pointing': 128 | return 1-α 129 | else: 130 | return 1.0 131 | 132 | 133 | # Define the transition matrix used in value iteration. 134 | def transition_matrix( P, λ, n ): 135 | # P = power level of chief spacecraft, between 0 and 1. 136 | # λ = power charging priority constant (hyper-parameter) 137 | # n = number of deputy spacecraft in action space. 138 | if P <= 0.0: 139 | α = 1.0 140 | else: 141 | α = (1-P)**λ 142 | transition_matrix = (1-α) * np.eye(n+1) 143 | transition_matrix[0 ,0] = 1.0 144 | transition_matrix[1:,0] = α 145 | return transition_matrix 146 | 147 | 148 | # Define a function to perform a Bellman update. 149 | def bellman( Sf, Si, P, λ, R, γ, U1 ): 150 | # R = vector of rewards for each action. 151 | # γ = discount factor in Bellman equation. 152 | # T = transition matrix. 153 | # U1 = utility from previous iteration. 154 | T = transition( Sf, Si, P, λ ) 155 | U2 = R + γ * T * U1 156 | return U2 157 | 158 | 159 | # Define a function hallucinating slew duration to a deputy. This function 160 | # will also propagate orbits of all deputies in the simulation. 161 | def point_deputy( dt, P, sCi, sDi, sDs ): 162 | δ = 0.0 163 | while True: 164 | dcmRN, ohmRN = targeting.reference_deputy( dt, sCi, sDi ) 165 | sCi = feedback.control( sCi, Kp, Ki, Kd, dcmRN, ohmRN ) 166 | sCi.propagate_orbit( dt ) 167 | sCi.propagate_attitude( dt, sCi.torque ) 168 | for sDii in sDs: 169 | sDii.propagate_orbit( dt ) # All spacecraft must move! 170 | δ += dt 171 | if sum( np.abs( sCi.attBR[1:] ) ) < 0.001: 172 | if abs( sCi.attBR[0] ) > 0.999: 173 | break # Break loop once attitude converges to 0.1% error. 174 | P -= δ * P_drain 175 | return P, δ # Return power level and duration. 176 | 177 | 178 | # Define a function hallucinating slew and charge duration to the sun. This 179 | # function will also propagate orbits of all deputies in the simulation. 180 | def point_sun( dt, P, sCi, sDs ): 181 | δ = 0.0 182 | while True: # Attitude maneuver to the sun 183 | dcmRN, ohmRN = targeting.reference_sun() 184 | sCi = feedback.control( sCi, Kp, Ki, Kd, dcmRN, ohmRN ) 185 | sCi.propagate_orbit( dt ) 186 | sCi.propagate_attitude( dt, sCi.torque ) 187 | for sDii in sDs: 188 | sDii.propagate_orbit( dt ) # All spacecraft must move! 189 | δ += dt 190 | P -= dt * P_drain 191 | if sum( np.abs( sCi.attBR[1:] ) ) < 0.001: 192 | if abs( sCi.attBR[0] ) > 0.999: 193 | break # Break loop once attitude converges to 0.1% error. 194 | while P < 1.0: # Begin charging process 195 | dcmRN, ohmRN = targeting.reference_sun() 196 | sCi = feedback.control( sCi, Kp, Ki, Kd, dcmRN, ohmRN ) 197 | sCi.propagate_orbit( dt ) 198 | sCi.propagate_attitude( dt, sCi.torque ) 199 | for sDii in sDs: 200 | sDii.propagate_orbit( dt ) # All spacecraft must move! 201 | δ += dt 202 | P += dt * P_charge 203 | return 1.0, δ # Return power level and duration. 204 | 205 | 206 | ############################################################################### 207 | ############################################################################### 208 | 209 | 210 | if __name__ == '__main__': 211 | 212 | mission_time = [] 213 | print('Random search for shortest path. \n') 214 | 215 | for trial in range(trials): 216 | 217 | # First, re-initialize all spacecraft. 218 | P, sDs = 1.0, [] 219 | 220 | # Import the ephemeris of all spacecraft from the CSV file. 221 | name_index = 0 222 | scenario_path = os.getcwd() + '\\scenarios\\pointing_policy' 223 | with open( scenario_path + '\\ephemeris.csv' ) as ephemeris: 224 | ephemeris_csv = csv.reader( ephemeris, delimiter=',' ) 225 | for row in ephemeris_csv: 226 | if 'Header' in row: 227 | continue 228 | else: 229 | a,e,i = float(row[1]), float(row[2]), float(row[3]) 230 | w,R,M = float(row[4]), float(row[5]), float(row[6]) 231 | if 'Chief' in row[0]: # Grab the chief SC parameters. 232 | sC = spacecraft.Spacecraft( elements = [a,e,i,w,R,M] ) 233 | sC.name = spacecraft.names[ name_index ] 234 | if 'Deputy' in row[0]: # Grab the deputy SC parameters. 235 | fR = 4*np.random.random()+1 # Random ∈ [1,5 ] km. 236 | fI = 2*fR # Random ∈ [2,10] km. 237 | fO = 0.0 # No in-track offset. 238 | fC = 4*np.random.random()+1 # Random ∈ [1,5 ] km. 239 | fP = np.random.random() * 360.0 - 180.0 240 | fT = np.random.random() * 360.0 - 180.0 241 | if trials == 1: 242 | sD = spacecraft.Spacecraft(elements=[a,e,i,w,R,M]) 243 | else: 244 | a,e,i,w,R,M = deputy.deputy( a, e, i, w, R, M, 245 | fR,fI,fO,fC,fP,fT ) 246 | sD = spacecraft.Spacecraft(elements=[a,e,i,w,R,M]) 247 | sD.name = str(name_index+1) 248 | sDs.append( sD ) 249 | name_index += 1 250 | 251 | # Initialise the action space. 252 | actions = ['Sun Pointing'] + sDs 253 | 254 | # Begin the solution for optimal pointing sequence. 255 | Af, δf, Tf = 'Initial', 0.0, 1 256 | while len(actions) > 1: 257 | if trials == 1: 258 | print('Epoch',Tf,'with current duration',δf,'and power',P) 259 | if P > 0.2: 260 | Af_index = np.random.randint( 0, len(actions)-1 ) 261 | Af = actions[ Af_index + 1 ] # Random deputy pointing 262 | P, δi = point_deputy( dt, P, sC, Af, sDs ) 263 | actions.remove( Af ) 264 | else: 265 | Af = actions[ 0 ] # Sun-pointing 266 | P, δi = point_sun( dt, P, sC, sDs ) 267 | δf = δf + δi 268 | if trials == 1: 269 | print('Completed! Duration',δf,'action',Af,'power',P,'\n') 270 | Tf = Tf + 1 271 | 272 | # Record the mission execution time. 273 | mission_time.append( δf ) 274 | if trials == 1: 275 | print('Completed the demonstration scenario!') 276 | else: 277 | print('Completed trial',trial) 278 | 279 | # Record the mission execution time in a file. 280 | f = open('raw_policy1_random.txt','w') 281 | for time in mission_time: 282 | f.write(str(time)+'\n') 283 | f.close() 284 | -------------------------------------------------------------------------------- /scenarios/pointing_policy/policy2a_greedy_angle.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ############################################################################### 4 | ############################################################################### 5 | ## ## 6 | ## ___ _ _ __ ____ ____ __ _ _ _____ ## 7 | ## / _ \| | | | / \ | _ \| __ \ / \ | \ | |_ _| ## 8 | ## ( |_| ) |_| |/ /\ \| |_| | -/ // /\ \| \| | | | ## 9 | ## \_ /|_____| /--\ |____/|_|\_\ /--\ |_\___| |_| ## 10 | ## \/ v 0.0 ## 11 | ## ## 12 | ## Scenario: Optimal Pointing Sequences in Spacecraft Formation Flying ## 13 | ## using Online Planning with Resource Constraints ## 14 | ## ## 15 | ## Written by Samuel Y. W. Low. ## 16 | ## Advised by Professor Mykel J. Kochenderfer ## 17 | ## First created 23-Nov-2021 23:01 PM (-8 GMT) ## 18 | ## Last modified 15-Mar-2022 13:05 AM (-8 GMT) ## 19 | ## ## 20 | ############################################################################### 21 | ############################################################################### 22 | 23 | # Import global libraries. 24 | import numpy as np 25 | import os, csv, math 26 | from copy import deepcopy 27 | import matplotlib.pyplot as plt 28 | 29 | # Move the current directory up until the Quadrant root. 30 | while os.getcwd().split("\\")[-1] != "quadrant": 31 | os.chdir("..") 32 | 33 | # Import source libraries. 34 | from source import spacecraft, attitudes, targeting, feedback, deputy 35 | 36 | # Initialise the number of Monte Carlo trials. If the number of trials is 37 | # set > 1, then the orbital elements of the deputies will be randomized 38 | # about the elements given in the ephemeris file. If trials = 1, then the 39 | # exact ephemeris elements will be used. 40 | trials = 1 41 | 42 | # Initialise dynamic and control time step. 43 | dt, ct = 1.0, 1.0 44 | 45 | # Initialise reinforcement learning hyper-parameters. 46 | γ = 0.75 # Gamma: Discount factor in Bellman update ( < 1.0) 47 | λ = 2.0 # Lambda: Power charging urgency constant (float) 48 | μ = 0.00017783 # Mu: Soft max precision parameter for rewards (float) 49 | 50 | # Initialise the resource parameters. 51 | P = 1.0 # Power level, must be between 0 and 1 52 | P_drain = 0.0005 # Power drain rate per second, between 0 and 1 53 | P_charge = 0.0025 # Power charge rate per second, between 0 and 1 54 | 55 | # Initialise attitude control gains 56 | Kp, Ki, Kd = 0.016, 0.0, 0.4 # Initial (Lyapunov) control gains 57 | 58 | 59 | ############################################################################### 60 | ############################################################################### 61 | 62 | 63 | # Define the reward for sun pointing. 64 | def reward_sun( P, λ, μ, C ): 65 | # P = power level of chief spacecraft, between 0 and 1. 66 | # λ = power charging priority hyper-parameter. 67 | # μ = soft-max hyper-parameter to tune reward hardness. 68 | # C = expected duration of sun pointing plus charging. 69 | α = ( 1 - max(0,P) ) ** λ 70 | return α / math.exp(μ*C) 71 | 72 | 73 | # Define the reward for deputy pointing. 74 | def reward_deputy( P, λ, μ, δ ): 75 | # P = power level of chief spacecraft, between 0 and 1. 76 | # λ = power charging priority hyper-parameter. 77 | # μ = soft-max hyper-parameter to tune reward hardness. 78 | # δ = individual expected slew time to a particular deputy. 79 | if P <= 0.01: 80 | return -1.0 81 | else: 82 | α = ( 1 - max(0,P) ) ** λ 83 | return (1-α) / math.exp(μ*δ) 84 | 85 | 86 | # Define the plotting of rewards. 87 | def plot_rewards(): 88 | plt.close('all') 89 | p_axis = np.arange( 0.02, 1.0, 0.01 ) 90 | t_axis = np.arange( 0.0, 1500.0, 10.0 ) 91 | p_grid, t_grid = np.meshgrid( p_axis, t_axis ) 92 | Z_sun_grid = np.zeros( np.shape(p_grid) ) 93 | Z_sat_grid = np.zeros( np.shape(p_grid) ) 94 | for i in range( len(p_axis) ): 95 | for j in range( len(t_axis) ): 96 | p, t = p_axis[i], t_axis[j] 97 | Z_sun_grid[j][i] = reward_sun( p, λ, μ, t ) 98 | Z_sat_grid[j][i] = reward_deputy( p, λ, μ, t ) 99 | plt.figure(1) 100 | plt.title('Reward distribution for a sun-pointing state.') 101 | plt.contourf(p_grid, t_grid, Z_sun_grid, levels=50) 102 | plt.colorbar() 103 | plt.xlabel('State: Current power level prior to state (0 to 1)') 104 | plt.ylabel('Action: Charging and attitude control duration [sec]') 105 | plt.figure(2) 106 | plt.title('Reward distribution for a deputy-pointing state.') 107 | plt.contourf(p_grid, t_grid, Z_sat_grid, levels=50) 108 | plt.colorbar() 109 | plt.xlabel('State: Current power level prior to state (0 to 1)') 110 | plt.ylabel('Action: Attitude control duration [sec]') 111 | 112 | 113 | # Define the transition probability between states. 114 | def transition( Sf, Si, P, λ ): 115 | # Sf = final state 116 | # Si = initial state 117 | # P = power level of chief spacecraft, between 0 and 1. 118 | # λ = power charging priority constant (hyper-parameter) 119 | if P <= 0.0: 120 | α = 1.0 121 | else: 122 | α = (1-P)**λ 123 | if Sf == 'Sun Pointing' and Si == 'Sun Pointing': 124 | return 0.0 125 | elif Sf == 'Sun Pointing' and Si != 'Sun Pointing': 126 | return α 127 | elif Sf != 'Sun Pointing' and Si != 'Sun Pointing': 128 | return 1-α 129 | else: 130 | return 1.0 131 | 132 | 133 | # Define the transition matrix used in value iteration. 134 | def transition_matrix( P, λ, n ): 135 | # P = power level of chief spacecraft, between 0 and 1. 136 | # λ = power charging priority constant (hyper-parameter) 137 | # n = number of deputy spacecraft in action space. 138 | if P <= 0.0: 139 | α = 1.0 140 | else: 141 | α = (1-P)**λ 142 | transition_matrix = (1-α) * np.eye(n+1) 143 | transition_matrix[0 ,0] = 1.0 144 | transition_matrix[1:,0] = α 145 | return transition_matrix 146 | 147 | 148 | # Define a function to perform a Bellman update. 149 | def bellman( Sf, Si, P, λ, R, γ, U1 ): 150 | # R = vector of rewards for each action. 151 | # γ = discount factor in Bellman equation. 152 | # T = transition matrix. 153 | # U1 = utility from previous iteration. 154 | T = transition( Sf, Si, P, λ ) 155 | U2 = R + γ * T * U1 156 | return U2 157 | 158 | 159 | # Define a function hallucinating slew duration to a deputy. This function 160 | # will also propagate orbits of all deputies in the simulation. 161 | def point_deputy( dt, P, sCi, sDi, sDs ): 162 | δ = 0.0 163 | while True: 164 | dcmRN, ohmRN = targeting.reference_deputy( dt, sCi, sDi ) 165 | sCi = feedback.control( sCi, Kp, Ki, Kd, dcmRN, ohmRN ) 166 | sCi.propagate_orbit( dt ) 167 | sCi.propagate_attitude( dt, sCi.torque ) 168 | for sDii in sDs: 169 | sDii.propagate_orbit( dt ) # All spacecraft must move! 170 | δ += dt 171 | if sum( np.abs( sCi.attBR[1:] ) ) < 0.001: 172 | if abs( sCi.attBR[0] ) > 0.999: 173 | break # Break loop once attitude converges to 0.1% error. 174 | P -= δ * P_drain 175 | return P, δ # Return power level and duration. 176 | 177 | 178 | # Define a function hallucinating slew and charge duration to the sun. This 179 | # function will also propagate orbits of all deputies in the simulation. 180 | def point_sun( dt, P, sCi, sDs ): 181 | δ = 0.0 182 | while True: # Attitude maneuver to the sun 183 | dcmRN, ohmRN = targeting.reference_sun() 184 | sCi = feedback.control( sCi, Kp, Ki, Kd, dcmRN, ohmRN ) 185 | sCi.propagate_orbit( dt ) 186 | sCi.propagate_attitude( dt, sCi.torque ) 187 | for sDii in sDs: 188 | sDii.propagate_orbit( dt ) # All spacecraft must move! 189 | δ += dt 190 | P -= dt * P_drain 191 | if sum( np.abs( sCi.attBR[1:] ) ) < 0.001: 192 | if abs( sCi.attBR[0] ) > 0.999: 193 | break # Break loop once attitude converges to 0.1% error. 194 | while P < 1.0: # Begin charging process 195 | dcmRN, ohmRN = targeting.reference_sun() 196 | sCi = feedback.control( sCi, Kp, Ki, Kd, dcmRN, ohmRN ) 197 | sCi.propagate_orbit( dt ) 198 | sCi.propagate_attitude( dt, sCi.torque ) 199 | for sDii in sDs: 200 | sDii.propagate_orbit( dt ) # All spacecraft must move! 201 | δ += dt 202 | P += dt * P_charge 203 | return 1.0, δ # Return power level and duration. 204 | 205 | 206 | ############################################################################### 207 | ############################################################################### 208 | 209 | 210 | # Run the main pointing simulation via greedy algorithm below. 211 | if __name__ == '__main__': 212 | 213 | mission_time = [] 214 | print('Greedy search for shortest path based on angles. \n') 215 | 216 | for trial in range(trials): 217 | 218 | # First, re-initialize all spacecraft. 219 | P, sDs = 1.0, [] 220 | 221 | # Import the ephemeris of all spacecraft from the CSV file. 222 | name_index = 0 223 | scenario_path = os.getcwd() + '\\scenarios\\pointing_policy' 224 | with open( scenario_path + '\\ephemeris.csv' ) as ephemeris: 225 | ephemeris_csv = csv.reader( ephemeris, delimiter=',' ) 226 | for row in ephemeris_csv: 227 | if 'Header' in row: 228 | continue 229 | else: 230 | a,e,i = float(row[1]), float(row[2]), float(row[3]) 231 | w,R,M = float(row[4]), float(row[5]), float(row[6]) 232 | if 'Chief' in row[0]: # Grab the chief SC parameters. 233 | sC = spacecraft.Spacecraft( elements = [a,e,i,w,R,M] ) 234 | sC.name = spacecraft.names[ name_index ] 235 | if 'Deputy' in row[0]: # Grab the deputy SC parameters. 236 | fR = 4*np.random.random()+1 # Random ∈ [1,5 ] km. 237 | fI = 2*fR # Random ∈ [2,10] km. 238 | fO = 0.0 # No in-track offset. 239 | fC = 4*np.random.random()+1 # Random ∈ [1,5 ] km. 240 | fP = np.random.random() * 360.0 - 180.0 241 | fT = np.random.random() * 360.0 - 180.0 242 | if trials == 1: 243 | sD = spacecraft.Spacecraft(elements=[a,e,i,w,R,M]) 244 | else: 245 | a,e,i,w,R,M = deputy.deputy( a, e, i, w, R, M, 246 | fR,fI,fO,fC,fP,fT ) 247 | sD = spacecraft.Spacecraft(elements=[a,e,i,w,R,M]) 248 | sD.name = str(name_index+1) 249 | sDs.append( sD ) 250 | name_index += 1 251 | 252 | # Initialise the action space. 253 | actions = ['Sun Pointing'] + sDs 254 | 255 | # Begin the solution for optimal pointing sequence. 256 | Af, δf, Tf = 'Initial', 0.0, 1 257 | while len(actions) > 1: 258 | if trials == 1: 259 | print('Epoch',Tf,'with current duration',δf,'and power',P) 260 | if P > 0.2: 261 | deputy_best, angle_best = None, 360.0 262 | for sDi in actions[1:]: # For every deputy, pick the closest. 263 | sCc = deepcopy(sC) 264 | dcmRN, ohmRN = targeting.reference_deputy( dt, sCc, sDi ) 265 | sCc = feedback.control( sCc, Kp, Ki, Kd, dcmRN, ohmRN ) 266 | angle = sCc.attBR.get_angle() # Get angular distance. 267 | if abs(angle) < abs(angle_best): 268 | angle_best = angle 269 | deputy_best = sDi 270 | Af = deputy_best 271 | P, δi = point_deputy( dt, P, sC, Af, sDs ) 272 | actions.remove( Af ) 273 | else: 274 | Af = actions[ 0 ] # Sun-pointing 275 | P, δi = point_sun( dt, P, sC, sDs ) 276 | δf = δf + δi 277 | if trials == 1: 278 | print('Completed! Duration',δf,'action',Af,'power',P,'\n') 279 | Tf = Tf + 1 280 | 281 | # Record the mission execution time. 282 | mission_time.append( δf ) 283 | if trials == 1: 284 | print('Completed the demonstration scenario!') 285 | else: 286 | print('Completed trial',trial) 287 | 288 | # Record the mission execution time in a file. 289 | f = open('raw_policy2_greedy.txt','w') 290 | for time in mission_time: 291 | f.write(str(time)+'\n') 292 | f.close() -------------------------------------------------------------------------------- /scenarios/pointing_policy/Raw Data/raw_policy3_lookahead.txt: -------------------------------------------------------------------------------- 1 | 2991.0 2 | 2913.0 3 | 3128.0 4 | 4295.0 5 | 5148.0 6 | 2807.0 7 | 5174.0 8 | 2744.0 9 | 3063.0 10 | 4424.0 11 | 3053.0 12 | 3298.0 13 | 2854.0 14 | 3234.0 15 | 3824.0 16 | 2957.0 17 | 5399.0 18 | 7957.0 19 | 3895.0 20 | 3907.0 21 | 3115.0 22 | 2914.0 23 | 3129.0 24 | 3730.0 25 | 3022.0 26 | 2852.0 27 | 2842.0 28 | 2843.0 29 | 3068.0 30 | 3067.0 31 | 2893.0 32 | 2946.0 33 | 3042.0 34 | 2788.0 35 | 3852.0 36 | 2833.0 37 | 2671.0 38 | 2695.0 39 | 2844.0 40 | 3409.0 41 | 5479.0 42 | 3376.0 43 | 3523.0 44 | 3608.0 45 | 3149.0 46 | 3428.0 47 | 2946.0 48 | 2551.0 49 | 3105.0 50 | 4026.0 51 | 3583.0 52 | 2810.0 53 | 4071.0 54 | 2789.0 55 | 2983.0 56 | 2683.0 57 | 2853.0 58 | 3884.0 59 | 4829.0 60 | 4390.0 61 | 3200.0 62 | 2895.0 63 | 3169.0 64 | 3186.0 65 | 3170.0 66 | 2709.0 67 | 3057.0 68 | 4207.0 69 | 4725.0 70 | 7736.0 71 | 2886.0 72 | 5038.0 73 | 5582.0 74 | 2703.0 75 | 2944.0 76 | 3034.0 77 | 3406.0 78 | 3439.0 79 | 4928.0 80 | 3739.0 81 | 3344.0 82 | 3152.0 83 | 3818.0 84 | 2969.0 85 | 2916.0 86 | 4897.0 87 | 2866.0 88 | 2716.0 89 | 3352.0 90 | 3088.0 91 | 3357.0 92 | 2916.0 93 | 4123.0 94 | 3029.0 95 | 3140.0 96 | 3211.0 97 | 3049.0 98 | 2901.0 99 | 3098.0 100 | 2962.0 101 | 3147.0 102 | 2855.0 103 | 2875.0 104 | 2921.0 105 | 2676.0 106 | 2853.0 107 | 3607.0 108 | 3037.0 109 | 3954.0 110 | 7901.0 111 | 3317.0 112 | 3448.0 113 | 2873.0 114 | 4071.0 115 | 2598.0 116 | 3755.0 117 | 3322.0 118 | 3018.0 119 | 4105.0 120 | 6877.0 121 | 3562.0 122 | 3167.0 123 | 4555.0 124 | 2841.0 125 | 3175.0 126 | 2984.0 127 | 5592.0 128 | 3438.0 129 | 4174.0 130 | 4246.0 131 | 3255.0 132 | 3219.0 133 | 5099.0 134 | 3331.0 135 | 3151.0 136 | 2984.0 137 | 3136.0 138 | 4114.0 139 | 4548.0 140 | 3215.0 141 | 7300.0 142 | 3528.0 143 | 4192.0 144 | 2857.0 145 | 2809.0 146 | 2836.0 147 | 4137.0 148 | 5116.0 149 | 3280.0 150 | 4433.0 151 | 3005.0 152 | 2999.0 153 | 2853.0 154 | 4271.0 155 | 5221.0 156 | 2642.0 157 | 2723.0 158 | 3155.0 159 | 2913.0 160 | 3671.0 161 | 4614.0 162 | 2921.0 163 | 3174.0 164 | 4036.0 165 | 3795.0 166 | 4091.0 167 | 3843.0 168 | 4630.0 169 | 2872.0 170 | 4263.0 171 | 2686.0 172 | 4707.0 173 | 3030.0 174 | 4922.0 175 | 3177.0 176 | 3297.0 177 | 4928.0 178 | 3600.0 179 | 3365.0 180 | 3192.0 181 | 2824.0 182 | 4664.0 183 | 3237.0 184 | 5372.0 185 | 3725.0 186 | 3483.0 187 | 2995.0 188 | 4366.0 189 | 4595.0 190 | 3320.0 191 | 4209.0 192 | 6190.0 193 | 2786.0 194 | 3654.0 195 | 3215.0 196 | 2835.0 197 | 3198.0 198 | 2879.0 199 | 3774.0 200 | 7556.0 201 | 2922.0 202 | 2536.0 203 | 4926.0 204 | 3439.0 205 | 4424.0 206 | 3008.0 207 | 4611.0 208 | 2814.0 209 | 3114.0 210 | 2970.0 211 | 2921.0 212 | 3014.0 213 | 2865.0 214 | 4861.0 215 | 2928.0 216 | 7128.0 217 | 4004.0 218 | 2686.0 219 | 4316.0 220 | 3526.0 221 | 2884.0 222 | 3206.0 223 | 3039.0 224 | 4503.0 225 | 3230.0 226 | 4569.0 227 | 5067.0 228 | 3939.0 229 | 3400.0 230 | 3115.0 231 | 5187.0 232 | 2833.0 233 | 3107.0 234 | 3046.0 235 | 3947.0 236 | 3710.0 237 | 3146.0 238 | 4813.0 239 | 3114.0 240 | 2814.0 241 | 2986.0 242 | 4125.0 243 | 2863.0 244 | 3119.0 245 | 2826.0 246 | 4799.0 247 | 2694.0 248 | 2805.0 249 | 4440.0 250 | 4952.0 251 | 2716.0 252 | 3019.0 253 | 3084.0 254 | 4588.0 255 | 4044.0 256 | 3324.0 257 | 3419.0 258 | 3104.0 259 | 2819.0 260 | 3045.0 261 | 4554.0 262 | 3191.0 263 | 2941.0 264 | 4852.0 265 | 3828.0 266 | 2896.0 267 | 2634.0 268 | 3264.0 269 | 4244.0 270 | 3994.0 271 | 2923.0 272 | 3681.0 273 | 2815.0 274 | 3017.0 275 | 3086.0 276 | 2826.0 277 | 3157.0 278 | 3644.0 279 | 3614.0 280 | 2836.0 281 | 3981.0 282 | 2779.0 283 | 4766.0 284 | 3191.0 285 | 7746.0 286 | 5528.0 287 | 4583.0 288 | 3355.0 289 | 2988.0 290 | 5406.0 291 | 3867.0 292 | 4623.0 293 | 2867.0 294 | 2696.0 295 | 2907.0 296 | 3239.0 297 | 4586.0 298 | 3118.0 299 | 3053.0 300 | 5695.0 301 | 5550.0 302 | 4379.0 303 | 4889.0 304 | 4636.0 305 | 3432.0 306 | 3128.0 307 | 3308.0 308 | 3008.0 309 | 3177.0 310 | 4788.0 311 | 2922.0 312 | 3153.0 313 | 3000.0 314 | 2948.0 315 | 3240.0 316 | 3085.0 317 | 4389.0 318 | 4839.0 319 | 4777.0 320 | 5447.0 321 | 2973.0 322 | 4035.0 323 | 2894.0 324 | 2891.0 325 | 3069.0 326 | 2944.0 327 | 2872.0 328 | 2716.0 329 | 3994.0 330 | 3262.0 331 | 2989.0 332 | 3021.0 333 | 2906.0 334 | 2992.0 335 | 4428.0 336 | 4172.0 337 | 3406.0 338 | 4492.0 339 | 4722.0 340 | 4345.0 341 | 3056.0 342 | 2852.0 343 | 3382.0 344 | 3260.0 345 | 2915.0 346 | 4640.0 347 | 4041.0 348 | 4399.0 349 | 2906.0 350 | 3519.0 351 | 3140.0 352 | 3137.0 353 | 3215.0 354 | 2998.0 355 | 4389.0 356 | 4148.0 357 | 2920.0 358 | 2868.0 359 | 2840.0 360 | 4882.0 361 | 2863.0 362 | 4149.0 363 | 2828.0 364 | 3044.0 365 | 4139.0 366 | 2785.0 367 | 2986.0 368 | 8204.0 369 | 3522.0 370 | 2981.0 371 | 2852.0 372 | 4522.0 373 | 4243.0 374 | 4119.0 375 | 3514.0 376 | 5041.0 377 | 3109.0 378 | 2723.0 379 | 2972.0 380 | 2533.0 381 | 5943.0 382 | 2999.0 383 | 3374.0 384 | 4144.0 385 | 3014.0 386 | 3137.0 387 | 3597.0 388 | 3662.0 389 | 4707.0 390 | 3187.0 391 | 3362.0 392 | 2964.0 393 | 2758.0 394 | 3215.0 395 | 2785.0 396 | 2757.0 397 | 3346.0 398 | 2962.0 399 | 3176.0 400 | 2876.0 401 | 3052.0 402 | 3215.0 403 | 3432.0 404 | 2814.0 405 | 3029.0 406 | 4160.0 407 | 3017.0 408 | 4284.0 409 | 2895.0 410 | 4200.0 411 | 5259.0 412 | 4166.0 413 | 7717.0 414 | 3261.0 415 | 3244.0 416 | 2719.0 417 | 2721.0 418 | 3016.0 419 | 3994.0 420 | 3149.0 421 | 3765.0 422 | 4986.0 423 | 4085.0 424 | 3039.0 425 | 4206.0 426 | 4713.0 427 | 5229.0 428 | 3617.0 429 | 5246.0 430 | 2691.0 431 | 4591.0 432 | 2499.0 433 | 4625.0 434 | 4242.0 435 | 2887.0 436 | 3524.0 437 | 3112.0 438 | 3540.0 439 | 3095.0 440 | 5209.0 441 | 3219.0 442 | 2993.0 443 | 4714.0 444 | 3156.0 445 | 3051.0 446 | 2899.0 447 | 4707.0 448 | 2924.0 449 | 4611.0 450 | 5144.0 451 | 3085.0 452 | 2974.0 453 | 3402.0 454 | 4737.0 455 | 4147.0 456 | 4347.0 457 | 4351.0 458 | 3663.0 459 | 4864.0 460 | 3184.0 461 | 4881.0 462 | 2949.0 463 | 3205.0 464 | 3005.0 465 | 2594.0 466 | 3631.0 467 | 4420.0 468 | 3615.0 469 | 7743.0 470 | 4410.0 471 | 2777.0 472 | 2983.0 473 | 2920.0 474 | 3530.0 475 | 2849.0 476 | 5089.0 477 | 2868.0 478 | 2591.0 479 | 3354.0 480 | 3338.0 481 | 2937.0 482 | 3057.0 483 | 4349.0 484 | 2901.0 485 | 3731.0 486 | 3035.0 487 | 4927.0 488 | 3108.0 489 | 4633.0 490 | 5065.0 491 | 3472.0 492 | 3046.0 493 | 2860.0 494 | 2854.0 495 | 2821.0 496 | 3818.0 497 | 3307.0 498 | 2765.0 499 | 2895.0 500 | 2881.0 501 | 3389.0 502 | 2663.0 503 | 2604.0 504 | 2723.0 505 | 3553.0 506 | 6375.0 507 | 2945.0 508 | 5479.0 509 | 2910.0 510 | 3577.0 511 | 2850.0 512 | 2940.0 513 | 4378.0 514 | 5444.0 515 | 2877.0 516 | 2993.0 517 | 2975.0 518 | 2926.0 519 | 4278.0 520 | 4727.0 521 | 4056.0 522 | 4200.0 523 | 4253.0 524 | 2835.0 525 | 3124.0 526 | 2681.0 527 | 2766.0 528 | 2944.0 529 | 5734.0 530 | 3013.0 531 | 3389.0 532 | 3929.0 533 | 3124.0 534 | 3942.0 535 | 2886.0 536 | 5072.0 537 | 3706.0 538 | 2684.0 539 | 3479.0 540 | 5058.0 541 | 4200.0 542 | 4899.0 543 | 7890.0 544 | 3008.0 545 | 3020.0 546 | 3048.0 547 | 2968.0 548 | 2798.0 549 | 3433.0 550 | 4639.0 551 | 3459.0 552 | 2596.0 553 | 4215.0 554 | 2857.0 555 | 7753.0 556 | 2917.0 557 | 4112.0 558 | 2804.0 559 | 5078.0 560 | 2903.0 561 | 3190.0 562 | 4078.0 563 | 2796.0 564 | 3028.0 565 | 3222.0 566 | 3677.0 567 | 4034.0 568 | 3016.0 569 | 2877.0 570 | 2860.0 571 | 3859.0 572 | 4106.0 573 | 4220.0 574 | 3273.0 575 | 4721.0 576 | 4109.0 577 | 3061.0 578 | 2841.0 579 | 4693.0 580 | 7883.0 581 | 2893.0 582 | 2996.0 583 | 3080.0 584 | 5588.0 585 | 3912.0 586 | 3032.0 587 | 6388.0 588 | 5657.0 589 | 2768.0 590 | 4765.0 591 | 3228.0 592 | 2909.0 593 | 3070.0 594 | 2962.0 595 | 3453.0 596 | 3128.0 597 | 4048.0 598 | 5148.0 599 | 3342.0 600 | 4443.0 601 | 2795.0 602 | 4179.0 603 | 3377.0 604 | 3143.0 605 | 2972.0 606 | 5601.0 607 | 5441.0 608 | 7610.0 609 | 4671.0 610 | 3036.0 611 | 4129.0 612 | 3167.0 613 | 2864.0 614 | 4380.0 615 | 2934.0 616 | 3338.0 617 | 3872.0 618 | 3381.0 619 | 3014.0 620 | 2884.0 621 | 4371.0 622 | 2958.0 623 | 5048.0 624 | 3630.0 625 | 5457.0 626 | 2900.0 627 | 7434.0 628 | 3149.0 629 | 2807.0 630 | 3256.0 631 | 2917.0 632 | 4099.0 633 | 4304.0 634 | 3893.0 635 | 3015.0 636 | 3437.0 637 | 3946.0 638 | 5365.0 639 | 4071.0 640 | 4723.0 641 | 3001.0 642 | 4107.0 643 | 5162.0 644 | 3220.0 645 | 4786.0 646 | 3087.0 647 | 2726.0 648 | 3189.0 649 | 3070.0 650 | 5430.0 651 | 5271.0 652 | 3941.0 653 | 2725.0 654 | 4173.0 655 | 3113.0 656 | 2720.0 657 | 3106.0 658 | 3995.0 659 | 2535.0 660 | 2412.0 661 | 2854.0 662 | 4487.0 663 | 4717.0 664 | 2793.0 665 | 4283.0 666 | 4601.0 667 | 3122.0 668 | 2962.0 669 | 2677.0 670 | 2664.0 671 | 3049.0 672 | 3083.0 673 | 2909.0 674 | 3172.0 675 | 2903.0 676 | 7684.0 677 | 2941.0 678 | 3077.0 679 | 4687.0 680 | 4226.0 681 | 7719.0 682 | 5320.0 683 | 3504.0 684 | 4663.0 685 | 4236.0 686 | 7823.0 687 | 3001.0 688 | 4536.0 689 | 2927.0 690 | 4339.0 691 | 4395.0 692 | 2858.0 693 | 2975.0 694 | 2923.0 695 | 2918.0 696 | 2565.0 697 | 2989.0 698 | 3089.0 699 | 2901.0 700 | 2844.0 701 | 3420.0 702 | 2960.0 703 | 2841.0 704 | 4295.0 705 | 3259.0 706 | 3074.0 707 | 3237.0 708 | 2986.0 709 | 4883.0 710 | 2865.0 711 | 2754.0 712 | 3721.0 713 | 2910.0 714 | 2887.0 715 | 3157.0 716 | 3207.0 717 | 2991.0 718 | 3779.0 719 | 3722.0 720 | 4120.0 721 | 4681.0 722 | 3856.0 723 | 2707.0 724 | 3853.0 725 | 2823.0 726 | 4017.0 727 | 3306.0 728 | 2900.0 729 | 4165.0 730 | 3755.0 731 | 3370.0 732 | 3022.0 733 | 3005.0 734 | 3001.0 735 | 2914.0 736 | 4932.0 737 | 4383.0 738 | 3114.0 739 | 2893.0 740 | 3731.0 741 | 3033.0 742 | 2827.0 743 | 2925.0 744 | 4652.0 745 | 4831.0 746 | 3553.0 747 | 4257.0 748 | 2880.0 749 | 2512.0 750 | 4966.0 751 | 3171.0 752 | 2871.0 753 | 3674.0 754 | 2971.0 755 | 3531.0 756 | 2850.0 757 | 5784.0 758 | 2642.0 759 | 3117.0 760 | 8191.0 761 | 2995.0 762 | 4296.0 763 | 4684.0 764 | 3230.0 765 | 4507.0 766 | 2777.0 767 | 3061.0 768 | 3081.0 769 | 3484.0 770 | 4490.0 771 | 2932.0 772 | 2855.0 773 | 4316.0 774 | 2911.0 775 | 3127.0 776 | 6212.0 777 | 2841.0 778 | 2859.0 779 | 3977.0 780 | 5095.0 781 | 2689.0 782 | 3457.0 783 | 3746.0 784 | 4511.0 785 | 2868.0 786 | 4573.0 787 | 2842.0 788 | 3239.0 789 | 3175.0 790 | 2983.0 791 | 2752.0 792 | 4887.0 793 | 2684.0 794 | 2588.0 795 | 2878.0 796 | 7679.0 797 | 3101.0 798 | 2859.0 799 | 2973.0 800 | 3407.0 801 | 2945.0 802 | 2704.0 803 | 2804.0 804 | 3044.0 805 | 2706.0 806 | 3032.0 807 | 3011.0 808 | 3831.0 809 | 3448.0 810 | 3065.0 811 | 3191.0 812 | 2577.0 813 | 3441.0 814 | 2713.0 815 | 2950.0 816 | 4169.0 817 | 2555.0 818 | 3464.0 819 | 3726.0 820 | 2965.0 821 | 2959.0 822 | 2837.0 823 | 2814.0 824 | 3135.0 825 | 4404.0 826 | 3462.0 827 | 3191.0 828 | 4751.0 829 | 2767.0 830 | 3096.0 831 | 2884.0 832 | 4210.0 833 | 4389.0 834 | 4553.0 835 | 3527.0 836 | 2893.0 837 | 4103.0 838 | 4249.0 839 | 5307.0 840 | 2915.0 841 | 3141.0 842 | 2991.0 843 | 3087.0 844 | 4527.0 845 | 2997.0 846 | 3911.0 847 | 4197.0 848 | 4370.0 849 | 2876.0 850 | 4227.0 851 | 2997.0 852 | 3666.0 853 | 4698.0 854 | 2823.0 855 | 2922.0 856 | 3424.0 857 | 2856.0 858 | 4505.0 859 | 2974.0 860 | 2951.0 861 | 4104.0 862 | 5091.0 863 | 3166.0 864 | 2922.0 865 | 4224.0 866 | 4423.0 867 | 3017.0 868 | 2678.0 869 | 3768.0 870 | 2958.0 871 | 4728.0 872 | 3135.0 873 | 2766.0 874 | 3066.0 875 | 2853.0 876 | 6885.0 877 | 3104.0 878 | 3940.0 879 | 4247.0 880 | 1930.0 881 | 4121.0 882 | 3076.0 883 | 2703.0 884 | 3001.0 885 | 3859.0 886 | 3258.0 887 | 3183.0 888 | 2914.0 889 | 2982.0 890 | 4905.0 891 | 5650.0 892 | 4350.0 893 | 3098.0 894 | 5149.0 895 | 2912.0 896 | 4176.0 897 | 2995.0 898 | 2826.0 899 | 3715.0 900 | 5706.0 901 | 3429.0 902 | 3085.0 903 | 3131.0 904 | 2540.0 905 | 2725.0 906 | 3106.0 907 | 3349.0 908 | 4115.0 909 | 2977.0 910 | 4172.0 911 | 4578.0 912 | 3748.0 913 | 3464.0 914 | 4360.0 915 | 4788.0 916 | 4080.0 917 | 3030.0 918 | 3172.0 919 | 4960.0 920 | 4696.0 921 | 4411.0 922 | 3089.0 923 | 3099.0 924 | 4142.0 925 | 2867.0 926 | 3997.0 927 | 3307.0 928 | 2982.0 929 | 5195.0 930 | 4213.0 931 | 3097.0 932 | 3496.0 933 | 4062.0 934 | 4756.0 935 | 2825.0 936 | 2794.0 937 | 3617.0 938 | 4329.0 939 | 3018.0 940 | 3504.0 941 | 3847.0 942 | 8079.0 943 | 2617.0 944 | 7747.0 945 | 3055.0 946 | 2846.0 947 | 2695.0 948 | 3119.0 949 | 4396.0 950 | 3347.0 951 | 2910.0 952 | 3059.0 953 | 4064.0 954 | 3094.0 955 | 2994.0 956 | 5161.0 957 | 4182.0 958 | 3599.0 959 | 5078.0 960 | 3767.0 961 | 3503.0 962 | 5111.0 963 | 2653.0 964 | 3699.0 965 | 2725.0 966 | 4459.0 967 | 4198.0 968 | 2782.0 969 | 3572.0 970 | 3422.0 971 | 3065.0 972 | 2859.0 973 | 4006.0 974 | 3022.0 975 | 3038.0 976 | 4299.0 977 | 3169.0 978 | 4794.0 979 | 3026.0 980 | 2908.0 981 | 3067.0 982 | 2828.0 983 | 2918.0 984 | 3028.0 985 | 2801.0 986 | 2879.0 987 | 3536.0 988 | 3013.0 989 | 4116.0 990 | 2567.0 991 | 3824.0 992 | 5282.0 993 | 3809.0 994 | 2990.0 995 | 3020.0 996 | 2842.0 997 | 3099.0 998 | 3331.0 999 | 3017.0 1000 | 3238.0 1001 | --------------------------------------------------------------------------------