├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ ├── black.yml
│ └── testcase.yml
├── .gitignore
├── Aerodynamic Heating Example.ipynb
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.txt
├── README.md
├── Stats Model Example.ipynb
├── campyros
├── __init__.py
├── aero.py
├── constants.py
├── gui.py
├── heating.py
├── main.py
├── mass.py
├── motor.py
├── plot.py
├── post.py
├── ray_alt.py
├── slosh.py
├── statistical.py
├── tests
│ ├── __init__.py
│ ├── test.json
│ ├── test.py
│ ├── test_stats.json
│ ├── testaero.csv
│ └── testmotor.csv
├── transforms.py
└── wind.py
├── data
├── Martlet4RasAeroII.CSV
├── Notes about RasAero Data.txt
├── Sample_Parachute_Cd.csv
├── aero_heating_example.json
├── martlet4.py
├── pyro.json
├── trajectory.json
└── w0_0_('20210321', '12', '[1]').pkl
├── docs
├── Makefile
├── build
│ ├── doctrees
│ │ ├── campyros.doctree
│ │ ├── campyros.tests.doctree
│ │ ├── environment.pickle
│ │ ├── index.doctree
│ │ └── modules.doctree
│ └── html
│ │ ├── .buildinfo
│ │ ├── _sources
│ │ ├── campyros.rst.txt
│ │ ├── campyros.tests.rst.txt
│ │ ├── index.rst.txt
│ │ └── modules.rst.txt
│ │ ├── _static
│ │ ├── basic.css
│ │ ├── css
│ │ │ ├── badge_only.css
│ │ │ ├── fonts
│ │ │ │ ├── Roboto-Slab-Bold.woff
│ │ │ │ ├── Roboto-Slab-Bold.woff2
│ │ │ │ ├── Roboto-Slab-Regular.woff
│ │ │ │ ├── Roboto-Slab-Regular.woff2
│ │ │ │ ├── fontawesome-webfont.eot
│ │ │ │ ├── fontawesome-webfont.svg
│ │ │ │ ├── fontawesome-webfont.ttf
│ │ │ │ ├── fontawesome-webfont.woff
│ │ │ │ ├── fontawesome-webfont.woff2
│ │ │ │ ├── lato-bold-italic.woff
│ │ │ │ ├── lato-bold-italic.woff2
│ │ │ │ ├── lato-bold.woff
│ │ │ │ ├── lato-bold.woff2
│ │ │ │ ├── lato-normal-italic.woff
│ │ │ │ ├── lato-normal-italic.woff2
│ │ │ │ ├── lato-normal.woff
│ │ │ │ └── lato-normal.woff2
│ │ │ └── theme.css
│ │ ├── doctools.js
│ │ ├── documentation_options.js
│ │ ├── file.png
│ │ ├── jquery-3.5.1.js
│ │ ├── jquery.js
│ │ ├── js
│ │ │ ├── badge_only.js
│ │ │ ├── html5shiv-printshiv.min.js
│ │ │ ├── html5shiv.min.js
│ │ │ └── theme.js
│ │ ├── language_data.js
│ │ ├── minus.png
│ │ ├── plus.png
│ │ ├── pygments.css
│ │ ├── searchtools.js
│ │ ├── underscore-1.3.1.js
│ │ └── underscore.js
│ │ ├── campyros.html
│ │ ├── campyros.tests.html
│ │ ├── genindex.html
│ │ ├── index.html
│ │ ├── modules.html
│ │ ├── objects.inv
│ │ ├── search.html
│ │ └── searchindex.js
├── make.bat
└── source
│ ├── CamPyRoS.rst
│ ├── campyros.tests.rst
│ ├── conf.py
│ ├── develop.rst
│ ├── help.rst
│ ├── index.rst
│ ├── licences.rst
│ └── modules.rst
├── environment.yml
├── example.ipynb
├── example.py
├── examples
└── stats_settings.json
├── img
├── favicon.ico
├── flowchart1.jpeg
└── logo.svg
├── legacy
├── Coordinate System Definitions.docx
├── Variable Moment of Inertia Model.docx
└── aerodynamic heating test cases
│ ├── Black Brant VC Flight 21.006 GT.json
│ ├── Black Brant VC Flight 21.006 GT.py
│ ├── How to run the test cases.txt
│ ├── NASA TND889.json
│ ├── NASA TND889.py
│ ├── NQLDW019 Problem BA.py
│ └── NQLDW019 Problem BA.xlsx
├── novus_sim_6.1
├── Changes from novus_sim_6.txt
├── LICENSE.txt
├── L_Nitrous_S_HDPE.propep
├── atmosphere_data.csv
├── drag_coefficient_data.csv
├── example_motor_inputs.txt
├── hybrid.eng
├── hybrid_functions.py
├── motor_out.csv
├── motor_sim.py
├── n2o_compressibility_factors.csv
├── readme.txt
└── trajectory_sim.py
├── requirements.txt
├── setup.py
├── stats_example.py
├── stats_settings.json
└── tests
├── wind_check.ipynb
└── wind_test.ipynb
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Either full config json or file please (or related parts if part of a larger project)
16 |
17 | **Expected behavior**
18 | A clear and concise description of what you expected to happen.
19 |
20 | **Screenshots**
21 | If applicable, add screenshots to help explain your problem.
22 |
23 | **Setup**
24 | Please note details of your OS, how you installed dependencies, if not by making a conda env please list all dependencies and python versions, which version of CamPyRoS you're using
25 |
26 | **Additional context**
27 | Add any other context about the problem here.
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflows/black.yml:
--------------------------------------------------------------------------------
1 | name: Lint
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | lint:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v2
10 | - uses: actions/setup-python@v2
11 | - uses: psf/black@stable
12 | with:
13 | args: ". --check"
14 |
--------------------------------------------------------------------------------
/.github/workflows/testcase.yml:
--------------------------------------------------------------------------------
1 | name: Test case
2 | on: [push]
3 | jobs:
4 | test:
5 | runs-on: ubuntu-latest
6 | steps:
7 | - name: Prepare repo
8 | uses: actions/checkout@master
9 | - name: Test
10 | uses: onichandame/python-test-action@master
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | .DS_Store
3 | *.pyc
4 | results/
5 | .ipynb_checkpoints/
6 | __pycache__/
7 | documentation/Moment of Inertia Liquid Calculation.pdn
8 | documentation/Cylinder Definitions.pdn
9 | *.aux
10 | *.bbl
11 | *.blg
12 | *.fdb_latexmk
13 | *.fls
14 | *.idx
15 | *.ilg
16 | *.ind
17 | *.lof
18 | *.log
19 | *.lot
20 | *.nlo
21 | *.out
22 | *.gz
23 | *.toc
24 | output.json
25 | *.grb2
26 | *.grib
27 | *.NC
28 | *.anl
29 | data/pyro.json
30 | pyro_venv/
31 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at js2430@cam.ac.uk. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to CamPyRoS
2 | Thank you for taking the time to contribute and for checking out these guidelines.
3 | ## Table of content
4 |
5 | [Have a problem?](#have-a-problem)
6 |
7 | [Helping out](#helping-out)
8 | - [What you need to know](#what-do-i-need-to-know-before-i-start)
9 | - [The contribution process](#the-contribution-process)
10 | - [Comments and docstrings](#comments-and-docstrings)
11 | - [Autoformatting with black](#autoformatting-with-black)
12 | - [Building sphinx documentation](#building-sphinx-documentation)
13 |
14 | ## Have a problem?
15 | If you have a problem you have thre courses of action:
16 | 1. Ask for help - if you aren't sure how something works please check out the documentation (work in progress) and if you don't find an answer there start a [discussion](https://github.com/cuspaceflight/CamPyRoS/discussions)
17 | 2. Open an issue - if you think there is a problem with the software then please open an [issue](https://github.com/cuspaceflight/CamPyRoS/issues) (if you have a question please use the discussion instead of issues)
18 | 3. Fix it - if you know how to fix your problem then please follow the contribution guidlines below
19 |
20 | ## Helping out
21 | We are always looking for help with this project, the main need is more coding but if you can't do that please get in touch as we have lots of holes in the (documentation)[https://cuspaceflight.github.io/CamPyRoS-Docs/develop.html] and could do with administrative help too.
22 | ### What do I need to know before I start
23 | Although this project didn't have firm conventions from the start we are trying to formalise them to make the code more readable and easier to modify. Some pointers for now are:
24 | - Functionality that requires more than one class method should be in their own file
25 | - Try to stick to the [black](https://github.com/psf/black#the-black-code-style) coding style, instructions below for automation
26 | - We use [Sphinx](https://www.sphinx-doc.org/en/master/) for API documentation so comments must be in a very specific format
27 | - **When you modify code this usually has a knock on effect. This means when you make a change you should try to make it backwards compatiable or go through the other files and modify them to accomodate your changes. This usually includes modifcation to `example.py`, `statistical.py` (this could be complicated so please contact us to check how we want the changes implimented in it), `stats_example.py`, `example.ipynb`, `stats_example.ipynb` and `main.py` if you have made changes in another file. If your code includes a new functionality you should change the examples to be able to demonstrait that.**
28 | - We have a [code of conduct](https://github.com/cuspaceflight/CamPyRoS/blob/main/CODE_OF_CONDUCT.md). TLDR - be nice to other people.
29 |
30 | ### The contribution process
31 |
32 | 1. If you are a first time contributor
33 | - Go to the project [home page](https://github.com/cuspaceflight/CamPyRoS) and click "fork" in the top right of the page
34 | - Clone the repository to your computer either with the [desktop app](https://desktop.github.com/) or by
35 |
36 | ```git clone https://github.com/your-username/CamPyRoS.git```
37 | - Add the upstream by going into the directory (`cd CamPyRoS`) and then
38 | -
39 | ```git remote add upstream https://github.com/cuspaceflight/CamPyRoS.git```
40 | 2. Develop your contribution:
41 | - Update to get the latest changes by pulling it in with the website or
42 |
43 | ```git checkout main```
44 |
45 | ```git pull upstream main```
46 | - Create a branch for your contribution with the desktop app or
47 |
48 | ```git checkout -b branch-name```
49 | - Create and add your contributions locally (`git add` and `git commit`)
50 | 3. Test your code
51 | - Before you push your code please please please run thorough tests on it so the reviewers don't have to
52 | - This should include (at the minimum) running an example file and the stats example (because this sort of tests for edge cases randomly) and if you have made a change that changes the physics of the simulation a thorough analysis of the difference in results to determine if it is implimented correctly
53 | - If you have added something that can be tested on its own please do a thorough test as a unit (if you need inspiration for a testing stratagy please make a discussion). For example if you have added a new recovery method that has dependance on altitude and velocity make a file where you import the class and request the forces from it over a range of velocities and altitudes and see if it is as you would expect.
54 | - Keep a record of these teste (e.g. screenshots of outputs showing problems or expected behaviours)
55 | 5. Submit your contribution
56 | - Push your changes to your fork on github
57 |
58 | ``git push origin branch-name``
59 |
60 | - Go to the your Github page (`https://github.com/your-username/CamPyRoS`) and onto the branch where there will be a green button asking if you want to make a pull request, alternativly click pull request in the top bar
61 | - Create a pull request from your branch to the main branch of `https://github.com/cuspaceflight/CamPyRoS` with a clear title, explanation of what your code does and any maths/physics that are not obvious that you have implimented and details of the tests you ran and their results (example plots etc. would be nice especially if they are different before and after your change)
62 |
63 | 6. Review
64 | - There is now an automatic test case that runs a shortened version of the example file. This is to check that your changes do not break the core functionality and so if you change something core (e.g. how aeros work) then the test case will fail. Please check the reason that your test failed and if it is because one of the tests actually fails (rather than the test not being able to run) then contact me and we will look at if the test needs changing.
65 | - We will have a look at the pull request as soon as we can and go through it, possibly testing the code ourselves
66 | - We are likely to suggest changes to style or funcitonality (please don't be offended, we have spent so much of our time working on it we don't want anything to break or be wrong)
67 | - Once this is done we will pull it into the main branch
68 |
69 |
70 | #### Comments and docstrings
71 | Please try to document your code so you and everyone else knows whats going on. This should at a minimum include adding or updating a [Google style](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html) docstring for each function/method and class. A few points to note are:
72 | - Docstrings must be surrounded by `"""` not `'''`
73 | - Other comments should not use `"""` as they will show up in random places in the documentation
74 | - Docstrings are whitespace sensiative - you must leave a line space between sections (follow the examples closely)
75 | - Titles (like `Note`) must have a line of `-` under them that is as long as the line (e.g. `Note↵----`)
76 | Other comments to help with your code (e.g. descriptions of what sections do) should ise `#` sytle comments - please use often (we haven't been good at this)
77 |
78 | #### Autoformatting with black
79 | To make the code cleaner and more consistent we are starting to use black autoformatting. To use `pip install black` and then `black director-name`. For more information see the link above. At some point we will transition to have git force you to use this before in which case you will need to install [pre-commit](https://pre-commit.com/).
80 |
81 | #### Building sphinx documentation
82 | When you make changes these are not automatically added to the documentation (especially since the docs work out of a different repo). You will need to [install sphinx](https://www.sphinx-doc.org/en/master/usage/installation.html) then enter `docs/` and then `make html` to update the docs. For them to be updated on the website you need to follow the above contribution steps but for `https://github.com/cuspaceflight/CamPyRoS-docs` and copy the `docs/` folder from this repository into your clone of the docs reporitory.
83 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CamPyRoS - A 6DOF Rocket Trajectory Simulator
2 | [](https://zenodo.org/badge/latestdoi/308847422) [](https://github.com/psf/black)[](https://github.com/cuspaceflight/CamPyRoS/actions/workflows/testcase.yml)
3 |
4 | CamPyRoS (Cambridge Python Rocketry Simulator) is a Python package which provides fully featured rocket trajectory simulation including features like:
5 | - 6 degrees of freedom (3 translational, 3 rotational)
6 | - Monte Carlo stochastic analysis
7 | - Aerodynamic heating model
8 | - Use of live wind data
9 | - Variable mass and moments of inertia models
10 |
11 | ## Getting started
12 | Currently not all dependancies are supported by the same install methods so the easiest install doesn't contain the full functionality. To install the core library:
13 |
14 | `pip install git+https://github.com/cuspaceflight/CamPyRoS.git`
15 |
16 | Statistics has a dependancy not fully supported by windows, to install it:
17 |
18 | `pip install ray` on most platforms, for Windows problems see [here](https://docs.ray.io/en/master/installation.html).
19 |
20 | If you don't install this the statistics module will run but only single threaded which will be *extremely* slow.
21 |
22 | ## Usage
23 |
24 | The repository contains some examples you can run:
25 | - `example.ipynb` or `example.py` : Launch of a simple rocket (the Martlet 4).
26 | - `Stats Model Example.ipynb` : Example of how to use the statistics model and stochastic analysis.
27 | - `Aerodynamic Heating Example.ipynb` : Example of how to run an aerodynamic heating simulation.
28 |
29 | ## Helping out
30 | If you would like to contribute please have a look at the [guidelines](CONTRIBUTING.md)
31 |
32 |
33 | ## In progress
34 | - **GUI:** An incomplete (and outdated) GUI has been made using Tkinter, and is in gui.py.
35 | - **Slosh modelling:** Some slosh modelling functions have been put together in slosh.py, based on the following source - [The Dynamic Behavior of Liquids in Moving Containers, with Applications to Space Vehicle Technology](https://ntrs.nasa.gov/citations/19670006555).
36 | - **Wind variability:** Statistical analysis of historic wind forecasts and obervations are analysed to create a Guassian difference profile to vary the wind in the statistical models (see wind-stats branch for a very poorly documented insight to current progress)
37 |
38 |
39 | ## Potential for expansion
40 | - **Multistage rockets**
41 | - **Fin cant, roll damping, and roll acceleration:** [OpenRocket Technical Documentation](http://openrocket.info/documentation.html)
42 | - **CFD coupling:** [PyFoam](https://openfoamwiki.net/index.php/Contrib/PyFoam), [Simulations of 6-DOF Motion
43 | with a Cartesian Method](https://pdfs.semanticscholar.org/ace3/5a61803390b0e0b70f6ca34492ad20a03e03.pdf)
44 | - **Multiphysics coupling:** [PRECICE](https://www.precice.org/)
45 |
46 | ## Cite as
47 | Daniel Gibbons, & Jago Strong-Wright. (2021, February 11). cuspaceflight/CamPyRoS: First release! (Version V1.0). Zenodo. http://doi.org/10.5281/zenodo.4535672
48 |
49 | ## Main References
50 |
51 | [1] - [Stochastic Six-Degree-of-Freedom Flight Simulator for Passively Controlled High-Power Rockets](https://ascelibrary.org/doi/10.1061/%28ASCE%29AS.1943-5525.0000051)
52 |
53 | [2] - [Tangent ogive nose aerodynamic heating program: NQLD019](https://ntrs.nasa.gov/citations/19730063810)
54 |
55 |
56 | ## Additional References
57 | [3] - [NASA Basic Considerations for Rocket Trajectory Simulation](https://apps.dtic.mil/sti/pdfs/AD0642855.pdf)
58 |
59 | [4] - [SIX DEGREE OF FREEDOM DIGITAL SIMULATION MODEL FOR UNGUIDED FIN-STABILIZED ROCKETS](https://apps.dtic.mil/dtic/tr/fulltext/u2/452106.pdf)
60 |
61 | [5] - [Trajectory Prediction for a Typical Fin Stabilized Artillery Rocket](https://journals.ekb.eg/article_23742_f19c1da1a61e78c1f5bb7ce58a7b30dd.pdf)
62 |
63 | [6] - [Central Limit Theorem and Sample Size](https://www.umass.edu/remp/Papers/Smith&Wells_NERA06.pdf)
64 |
65 | [7] - [Monte Carlo Simulations: Number of Iterations and Accuracy](https://apps.dtic.mil/dtic/tr/fulltext/u2/a621501.pdf)
66 |
67 | [8] - [Method for Calculating Aerodynamic Heating on Sounding Rocket Tangent Ogive Noses](https://arc.aiaa.org/doi/abs/10.2514/3.62081)
68 |
69 | [9] - [Six degree-of-freedom (6-DOF) Flight Simulation Check-cases](https://nescacademy.nasa.gov/flightsim/)
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/campyros/__init__.py:
--------------------------------------------------------------------------------
1 | from .main import *
2 | from .mass import *
3 | from .plot import *
4 | from .aero import *
5 | from .motor import *
6 |
7 |
8 | __copyright__ = """
9 |
10 | Copyright 2021 Jago Strong-Wright & Daniel Gibbons
11 |
12 | This program is free software: you can redistribute it and/or modify
13 | it under the terms of the GNU General Public License as published by
14 | the Free Software Foundation, either version 3 of the License, or
15 | (at your option) any later version.
16 |
17 | This program is distributed in the hope that it will be useful,
18 | but WITHOUT ANY WARRANTY; without even the implied warranty of
19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 | GNU General Public License for more details.
21 |
22 | You should have received a copy of the GNU General Public License
23 | along with this program. If not, see .
24 |
25 | """
26 |
--------------------------------------------------------------------------------
/campyros/constants.py:
--------------------------------------------------------------------------------
1 | """Earth constants"""
2 | r_earth = 6378137 # Earth's semimajor axis in meters
3 | ang_vel_earth = 7.292115090e-5 # Earth's angular velocity in rads / s
4 | e_earth = 0.081819191 # Earth ecentricity
5 | f = 1.0 / 298.257223563 # Flattening of Earth spheroid for WGS84 Model
6 |
--------------------------------------------------------------------------------
/campyros/motor.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import csv
3 | import scipy.interpolate
4 | import matplotlib.pyplot as plt
5 | import pandas as pd
6 |
7 | __copyright__ = """
8 |
9 | Copyright 2021 Jago Strong-Wright & Daniel Gibbons
10 |
11 | Licensed under the Apache License, Version 2.0 (the "License");
12 | you may not use this file except in compliance with the License.
13 | You may obtain a copy of the License at
14 |
15 | http://www.apache.org/licenses/LICENSE-2.0
16 |
17 | Unless required by applicable law or agreed to in writing, software
18 | distributed under the License is distributed on an "AS IS" BASIS,
19 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 | See the License for the specific language governing permissions and
21 | limitations under the License.
22 |
23 | """
24 | __license__ = "Apache 2.0"
25 |
26 |
27 | class Motor:
28 | """Object for holding rocket engine data.
29 |
30 | Assumes constant nozzle exit area.
31 |
32 | Args:
33 | thrust_array (list): Thrust data (N).
34 | time_array (list): Times corresponding to thrust_array data points (s).
35 | exit_area (float): Nozzle exit area (m^2).
36 | pos (float): Distance between the nose tip and the point at which the thrust acts (m).
37 | ambient_pressure (float, optional): Ambient pressure used to obtain the thrust_array data (Pa). Defaults to 1e5.
38 |
39 | Attributes:
40 | thrust_array (list): Thrust data (N).
41 | time_array (list): Times corresponding to thrust_array data points (s).
42 | exit_area (float): Nozzle exit area (m^2).
43 | pos (float): Distance between the nose tip and the point at which the thrust acts (m).
44 | ambient_pressure (float, optional): Ambient pressure used to obtain the thrust_array data (Pa).
45 |
46 | """
47 |
48 | def __init__(self, thrust_array, time_array, exit_area, pos, ambient_pressure=1e5):
49 |
50 | self.thrust_array = thrust_array # Thrust data (N)
51 | self.time_array = (
52 | time_array # Times corresponding to thrust_array data points (s)
53 | )
54 | self.pos = pos # Distance between the nose tip and the point at which the thrust acts (m)
55 | self.exit_area = exit_area # Nozzle exit area (m^2)
56 | self.ambient_pressure = ambient_pressure # Ambient pressure used to obtain the thrust_array data (Pa)
57 |
58 | def thrust(self, time):
59 | """Function for calculating the thrust at a given time, with an ambient pressure of self.ambient_pressure.
60 |
61 | Args:
62 | time (float): Time since ignition (s).
63 |
64 | Returns:
65 | float: Thrust (N).
66 | """
67 | return np.interp(time, self.time_array, self.thrust_array)
68 |
69 | @staticmethod
70 | def from_novus(csv_directory, pos):
71 | """Generate Motor object from a novus_sim_6 output csv file. Modified from Joe Hunt's NOVUS simulator.
72 |
73 | Args:
74 | csv_directory (string): Directory of the .CSV file.
75 | pos (float): Distance between the nose tip and the point at which the thrust acts (m).
76 |
77 | Returns:
78 | Motor: The Motor object.
79 | """
80 |
81 | # Collect data from the CSV
82 | with open(csv_directory) as csvfile:
83 | motor_out = csv.reader(csvfile)
84 |
85 | (
86 | time_data,
87 | prop_mass_data,
88 | cham_pres_data,
89 | throat_data,
90 | gamma_data,
91 | nozzle_efficiency_data,
92 | exit_pres_data,
93 | area_ratio_data,
94 | ) = ([], [], [], [], [], [], [], [])
95 |
96 | next(motor_out)
97 | for row in motor_out:
98 | time_data.append(float(row[0]))
99 | prop_mass_data.append(float(row[1]))
100 | cham_pres_data.append(float(row[2]))
101 | throat_data.append(float(row[3]))
102 | gamma_data.append(float(row[4]))
103 | nozzle_efficiency_data.append(float(row[5]))
104 | exit_pres_data.append(float(row[6]))
105 | area_ratio_data.append(float(row[7]))
106 |
107 | # Convert everything into numpy arrays so we can do element-wise operations
108 | pres_cham = np.array(cham_pres_data)
109 | dia_throat = np.array(throat_data)
110 | gamma = np.array(gamma_data)
111 | nozzle_efficiency = np.array(nozzle_efficiency_data)
112 | pres_exit = np.array(exit_pres_data)
113 | nozzle_area_ratio = np.array(area_ratio_data)
114 |
115 | # Let's use ambient pressure = 1 bar
116 | pres_ambient = 1e5
117 |
118 | # Calculate the thrust
119 | area_throat = ((dia_throat / 2) ** 2) * np.pi
120 | exit_area = area_throat * nozzle_area_ratio
121 | thrust = (
122 | area_throat
123 | * pres_cham
124 | * (
125 | (
126 | (2 * gamma ** 2 / (gamma - 1))
127 | * ((2 / (gamma + 1)) ** ((gamma + 1) / (gamma - 1)))
128 | * (1 - (pres_exit / pres_cham) ** ((gamma - 1) / gamma))
129 | )
130 | ** 0.5
131 | )
132 | + (pres_exit - pres_ambient) * exit_area
133 | )
134 |
135 | thrust = thrust * nozzle_efficiency
136 |
137 | return Motor(
138 | thrust_array=thrust,
139 | time_array=time_data,
140 | exit_area=exit_area[0],
141 | pos=pos,
142 | ambient_pressure=pres_ambient,
143 | )
144 |
145 |
146 | def load_motor(file):
147 | """Legacy requirment for statistical models
148 |
149 | Args:
150 | file (string): Location of novus output file
151 |
152 | Returns:
153 | dict: Motor data - "motor_time","prop_mass",
154 | "cham_pres","throat","gamma", "nozzle_efficiency",
155 | "exit_pres","area_ratio","vmass","lden","lmass",
156 | "fuel_mass","density_fuel","dia_fuel","length_port"
157 | """
158 | motor_csv = pd.read_csv(file)
159 |
160 | time_array = motor_csv["Time"]
161 | smass_array = motor_csv["Solid Fuel Mass (kg)"]
162 | S_DEN = motor_csv["Solid Fuel Density (kg/m^3)"][0]
163 | S_L = motor_csv["Solid Fuel Length (m)"][0]
164 | S_ROUT = motor_csv["Solid Fuel Outer Diameter (m)"][0]
165 | vmass_array = motor_csv["Vapour Mass (kg)"]
166 | vden_array = motor_csv["Vapour Density (kg/m^3)"]
167 | lmass_array = motor_csv["Liquid Mass (kg)"]
168 | lden_array = motor_csv["Liquid Density (kg/m^3)"]
169 |
170 | return {
171 | "time": time_array,
172 | "smass": smass_array,
173 | "sden": S_DEN,
174 | "s_l": S_L,
175 | "s_rout": S_ROUT,
176 | "vmass": vmass_array,
177 | "vden": vden_array,
178 | "lmass": lmass_array,
179 | "lden": lden_array,
180 | }
181 |
--------------------------------------------------------------------------------
/campyros/post.py:
--------------------------------------------------------------------------------
1 | """
2 | Useful functions for post-processing of the trajectory data, e.g. when imported from a .JSON file.
3 |
4 | The trajectory data is usually stored in a minimalistic format, so only contains:
5 | - time
6 | - pos_i
7 | - vel_i
8 | - b2imat
9 | - w_b
10 |
11 | So things like the altitude, yaw, pitch, and roll, etc... need to be obtained from only this data.
12 |
13 | """
14 | import numpy as np
15 | from scipy.spatial.transform import Rotation
16 |
17 | __copyright__ = """
18 |
19 | Copyright 2021 Jago Strong-Wright & Daniel Gibbons
20 |
21 | This program is free software: you can redistribute it and/or modify
22 | it under the terms of the GNU General Public License as published by
23 | the Free Software Foundation, either version 3 of the License, or
24 | (at your option) any later version.
25 |
26 | This program is distributed in the hope that it will be useful,
27 | but WITHOUT ANY WARRANTY; without even the implied warranty of
28 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 | GNU General Public License for more details.
30 |
31 | You should have received a copy of the GNU General Public License
32 | along with this program. If not, see .
33 |
34 | """
35 |
36 |
37 | def ypr_i(simulation_output):
38 | """
39 | Get yaw, pitch and roll data (relative to inertial axes).
40 |
41 | Inputs
42 | ------
43 | simulation_output : pandas DataFrame
44 |
45 | Returns
46 | -------
47 | yaw : list
48 | pitch : list
49 | roll : list
50 | """
51 | output_dict = simulation_output.to_dict(orient="list")
52 | time = output_dict["time"]
53 |
54 | yaw = []
55 | pitch = []
56 | roll = []
57 |
58 | for i in range(len(time)):
59 | ypr = Rotation.from_matrix(output_dict["b2imat"][i]).as_euler("zyx")
60 | yaw.append(ypr[0])
61 | pitch.append(ypr[1])
62 | roll.append(ypr[2])
63 |
64 | return yaw, pitch, roll
65 |
--------------------------------------------------------------------------------
/campyros/ray_alt.py:
--------------------------------------------------------------------------------
1 | import warnings
2 |
3 |
4 | class remote:
5 | def __init__(self, f):
6 | warnings.warn(
7 | "Ray was not available so multithread running will not work. Stats will take a long time"
8 | )
9 | self.f = f
10 |
11 | def __call__(self, *args):
12 | self.f(*args)
13 |
--------------------------------------------------------------------------------
/campyros/slosh.py:
--------------------------------------------------------------------------------
1 | """
2 | Liquid fuel slosh modelling tools
3 |
4 | References
5 | -----------
6 | [1] - The Dynamic Behavior of Liquids in Moving Containers, with Applications to Space Vehicle Technology
7 | Hyperlink - https://ntrs.nasa.gov/citations/19670006555
8 |
9 | """
10 |
11 | import numpy as np
12 |
13 | __copyright__ = """
14 |
15 | Copyright 2021 Daniel Gibbons
16 |
17 | This program is free software: you can redistribute it and/or modify
18 | it under the terms of the GNU General Public License as published by
19 | the Free Software Foundation, either version 3 of the License, or
20 | (at your option) any later version.
21 |
22 | This program is distributed in the hope that it will be useful,
23 | but WITHOUT ANY WARRANTY; without even the implied warranty of
24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 | GNU General Public License for more details.
26 |
27 | You should have received a copy of the GNU General Public License
28 | along with this program. If not, see .
29 |
30 | """
31 |
32 |
33 | class CylindricalFuelTank:
34 | def __init__(self, h, d, rho):
35 | """
36 | Cylindrical fuel tank
37 |
38 | Inputs
39 | -------
40 | h : float
41 | Liquid height (m)
42 | d : float
43 | Fuel tank diameter (m)
44 | rho : float
45 | Liquid density (kg m^-3)
46 | """
47 | self.h = h
48 | self.d = d
49 | self.rho = rho
50 |
51 | # Table 6.1 from Reference [1]
52 | self.mt = 0.25 * np.pi * rho * h * d ** 2
53 | self.I_rigid = self.mt * d ** 2 * (1 / 12 * (h / d) ** 2 + 1 / 16)
54 |
55 | def pendulum_analogy(self):
56 | # Table 6.1 from Reference [1]
57 | # Let A = 3.68*h/d
58 | A = 3.68 * self.h / self.d
59 | L1 = (self.d / 3.68) * 1 / np.tanh(A)
60 | m1 = self.mt * self.d / (4.4 * self.h) * np.tanh(A)
61 | m0 = self.mt - m1
62 | l1 = -self.d / 7.36 * 1 / np.sinh(2 * A)
63 | l0 = self.mt / m0 * (self.h / 2 - self.d ** 2 / (8 * self.h)) - (l1 + L1) * (
64 | m1 / m0
65 | )
66 |
67 | I0 = (
68 | self.I_rigid
69 | + self.mt * (self.h / 2) ** 2
70 | - self.mt
71 | * self.d ** 2
72 | / 8
73 | * (1.995 - self.d / self.h * ((1.07 * np.cosh(A) - 1.07) / np.sinh(A)))
74 | - m0 * l0 ** 2
75 | - m1 * (l1 + L1)
76 | )
77 |
78 | return L1, m1, m0, l1, l0, I0
79 |
80 | def spring_analogy(self):
81 | # Table 6.1 from Reference [1]
82 | # Let A = 3.68*h/d
83 | A = 3.68 * self.h / self.d
84 | K1 = self.mt * (9.81 / (1.19 * self.h)) * np.tanh(A) ** 2
85 | m1 = self.mt * self.d / (4.4 * self.h) * np.tanh(A)
86 | m0 = self.mt - m1
87 | l1 = (self.d / 3.68) * np.tanh(A)
88 | l0 = self.mt / m0 * (self.h / 2 - self.d ** 2 / (8 * self.h)) - l1 * (m1 / m0)
89 |
90 | I0 = (
91 | self.I_rigid
92 | + self.mt * (self.h / 2) ** 2
93 | - self.mt
94 | * self.d ** 2
95 | / 8
96 | * (1.995 - self.d / self.h * ((1.07 * np.cosh(A) - 1.07) / np.sinh(A)))
97 | - m0 * l0 ** 2
98 | - m1 * l1
99 | )
100 |
101 | return K1, m1, m0, l1, l0, I0
102 |
103 | def w_spring(self):
104 | K1, m1, m0, l1, l0, I0 = self.spring_analogy()
105 | return (K1 / m1) ** 0.5
106 |
107 | def w_pendulum(self):
108 | L1, m1, m0, l1, l0, I0 = self.pendulum_analogy()
109 | return (9.81 / L1) ** 0.5
110 |
--------------------------------------------------------------------------------
/campyros/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/campyros/tests/__init__.py
--------------------------------------------------------------------------------
/campyros/tests/test.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import sys, os, json
3 | from datetime import date, timedelta, datetime
4 |
5 | sys.path.append(
6 | "/".join(
7 | os.path.dirname(os.path.dirname(os.path.abspath(__file__))).split("/")[:-1]
8 | )
9 | )
10 | import campyros as pyro
11 | from campyros import statistical as stats
12 | from campyros import wind
13 | import csv
14 | import time
15 | import numpy as np
16 | import pandas as pd
17 | from datetime import date, timedelta
18 |
19 | __copyright__ = """
20 |
21 | Copyright 2021 Jago Strong-Wright & Daniel Gibbons
22 |
23 | This program is free software: you can redistribute it and/or modify
24 | it under the terms of the GNU General Public License as published by
25 | the Free Software Foundation, either version 3 of the License, or
26 | (at your option) any later version.
27 |
28 | This program is distributed in the hope that it will be useful,
29 | but WITHOUT ANY WARRANTY; without even the implied warranty of
30 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 | GNU General Public License for more details.
32 |
33 | You should have received a copy of the GNU General Public License
34 | along with this program. If not, see .
35 |
36 | """
37 |
38 | # Setup test case
39 | """Import motor data to use for the mass model"""
40 | motor_csv = pd.read_csv("campyros/tests/testmotor.csv")
41 | time_array = motor_csv["Time"]
42 | smass_array = motor_csv["Solid Fuel Mass (kg)"]
43 | S_DEN = motor_csv["Solid Fuel Density (kg/m^3)"][0]
44 | S_L = motor_csv["Solid Fuel Length (m)"][0]
45 | S_ROUT = motor_csv["Solid Fuel Outer Diameter (m)"][0]
46 | vmass_array = motor_csv["Vapour Mass (kg)"]
47 | vden_array = motor_csv["Vapour Density (kg/m^3)"]
48 | lmass_array = motor_csv["Liquid Mass (kg)"]
49 | lden_array = motor_csv["Liquid Density (kg/m^3)"]
50 |
51 | """Rocket parameters"""
52 | DRY_MASS = 60 # Rocket dry mass (kg)
53 | ROCKET_L = 6.529 # Rocket length (m)
54 | ROCKET_R = 98.5e-3 # Rocket radius (m)
55 | ROCKET_T = 1e-2 # Rocket wall thickness (m) - used when approximating the rocket airframe as a thin walled cylinder
56 | POS_TANK_BOTTOM = (
57 | 4.456 # Distance between the nose tip and the bottom of the nitrous tank (m)
58 | )
59 | POS_SOLIDFUEL_BOTTOM = (
60 | 4.856 + S_L
61 | ) # Distance between the nose tip and bottom of the solid fuel grain (m)
62 | REF_AREA = 0.0305128422 # Reference area for aerodynamic coefficients (m^2)
63 |
64 | """Set up aerodynamic properties"""
65 | # Get approximate values for the rotational damping coefficients
66 | C_DAMP_PITCH = pyro.pitch_damping_coefficient(
67 | ROCKET_L, ROCKET_R, fin_number=4, area_per_fin=0.07369928
68 | )
69 | C_DAMP_ROLL = 0
70 |
71 | # Import drag coefficients from RASAero II
72 | aero_data = pyro.AeroData.from_rasaero(
73 | "campyros/tests/testaero.csv", REF_AREA, C_DAMP_PITCH, C_DAMP_ROLL
74 | )
75 | # aero_data.show_plot() #Show plots of how the program interpreted the data, so you can visually check if it's correct
76 |
77 | """Set up the mass model"""
78 | mass_model = pyro.MassModel()
79 | mass_model.add_hollowcylinder(
80 | DRY_MASS, ROCKET_R, ROCKET_R - ROCKET_T, ROCKET_L, ROCKET_L / 2
81 | )
82 | mass_model.add_liquidtank(
83 | lmass_array,
84 | lden_array,
85 | time_array,
86 | ROCKET_R,
87 | POS_TANK_BOTTOM,
88 | vmass_array,
89 | vden_array,
90 | )
91 | mass_model.add_solidfuel(
92 | smass_array, time_array, S_DEN, S_ROUT, S_L, POS_SOLIDFUEL_BOTTOM
93 | )
94 |
95 | """Create the other objects needed to initialise the Rocket object"""
96 | pulsar = pyro.Motor.from_novus("campyros/tests/testmotor.csv", pos=ROCKET_L)
97 |
98 | """
99 | launch_site = pyro.LaunchSite(rail_length=10,
100 | rail_yaw=0,
101 | rail_pitch=0,
102 | alt=1,
103 | longi=0.1160127,
104 | lat=52.2079404,
105 | variable_wind=True,
106 | forcast_plus_time="016",
107 | run_date="20201216",
108 | fast_wind=False)
109 | """
110 | launch_site = pyro.LaunchSite(
111 | rail_length=5,
112 | rail_yaw=0,
113 | rail_pitch=0,
114 | alt=10,
115 | longi=0.1,
116 | lat=52.1,
117 | variable_wind=False,
118 | ) # Use this version if you don't want to use the real wind (e.g. to test something else)
119 |
120 | parachute = pyro.Parachute(
121 | main_s=13.9,
122 | drogue_s=1.13,
123 | main_c_d=0.78,
124 | drogue_c_d=0.78,
125 | main_alt=500,
126 | attach_distance=0,
127 | )
128 |
129 | """Create the Rocket object"""
130 | martlet4 = pyro.Rocket(
131 | mass_model,
132 | pulsar,
133 | aero_data,
134 | launch_site,
135 | h=0.05,
136 | variable=True,
137 | alt_poll_interval=1,
138 | parachute=parachute,
139 | )
140 |
141 | run = martlet4.run(debug=False)
142 |
143 | test_output = pyro.from_json("campyros/tests/test.json")
144 | run_time = test_output.time.max()
145 | min_pos = min(
146 | [(l[0] ** 2 + l[1] ** 2 + l[2] ** 2) ** 0.5 for l in test_output.pos_i.to_list()]
147 | ) # should be about landing site
148 | apogee = max(
149 | [(l[0] ** 2 + l[1] ** 2 + l[2] ** 2) ** 0.5 for l in test_output.pos_i.to_list()]
150 | )
151 | max_vel = max(
152 | [(l[0] ** 2 + l[1] ** 2 + l[2] ** 2) ** 0.5 for l in test_output.vel_i.to_list()]
153 | )
154 |
155 | parachute_ind = 0
156 | rail_ind = 0
157 | for ind, ev in enumerate(test_output.events):
158 | if "Parachute deployed" in ev:
159 | parachute_ind = ind
160 | if "Cleared rail" in ev:
161 | rail_ind = ind
162 |
163 | parachute_ind_run = 0
164 | rail_ind_run = 0
165 | for ind, ev in enumerate(run.events):
166 | if "Parachute deployed" in ev:
167 | parachute_ind_run = ind
168 | if "Cleared rail" in ev:
169 | rail_ind_run = ind
170 |
171 |
172 | class ExampleTest(unittest.TestCase):
173 | def test_time(self):
174 | self.assertAlmostEqual(run_time // 5, run.time.max() // 5, places=0)
175 |
176 | def test_apogee(self):
177 | run_apogee = max(
178 | [(l[0] ** 2 + l[1] ** 2 + l[2] ** 2) ** 0.5 for l in run.pos_i.to_list()]
179 | )
180 | self.assertAlmostEqual(apogee, run_apogee, places=0)
181 |
182 | def test_max_vel(self):
183 | run_max_vel = max(
184 | [(l[0] ** 2 + l[1] ** 2 + l[2] ** 2) ** 0.5 for l in run.vel_i.to_list()]
185 | )
186 | self.assertAlmostEqual(max_vel, run_max_vel, places=0)
187 |
188 | def test_min_pos(self):
189 | run_min_pos = min(
190 | [(l[0] ** 2 + l[1] ** 2 + l[2] ** 2) ** 0.5 for l in run.pos_i.to_list()]
191 | )
192 | self.assertAlmostEqual(min_pos / 100, run_min_pos / 100, places=0)
193 |
194 | def test_rail(self):
195 | self.assertAlmostEqual(
196 | test_output.time[rail_ind], run.time[rail_ind_run], places=0
197 | )
198 |
199 | def test_parachute(self):
200 | self.assertAlmostEqual(
201 | test_output.time[parachute_ind] // 5,
202 | run.time[parachute_ind_run] // 5,
203 | places=0,
204 | )
205 |
206 | def test_stats(self):
207 | with open("campyros/tests/test_stats.json", "r") as f:
208 | stat_dat = json.load(f)
209 | stat_dat["launch_site"]["launch_datetime"] = (
210 | date.today() - timedelta(days=2)
211 | ).strftime("%Y%m%d %H:%M")
212 | with open("campyros/tests/test_stats.json", "w") as f:
213 | json.dump(stat_dat, f)
214 | stats_model = stats.StatisticalModel("campyros/tests/test_stats.json")
215 | ran = stats_model.run_model(test_mode=True, num_cpus=1)
216 | print(ran)
217 | if ran == "results/stats_testcase":
218 | ran = True
219 | else:
220 | ran = False
221 | self.assertEqual(
222 | True,
223 | ran,
224 | msg="Statistical model run failed, no further information automatically available",
225 | )
226 |
227 | def test_wind(self):
228 | wind1 = wind.Wind((date.today() - timedelta(days=1)).strftime("%Y%m%d %H:%M"))
229 | wind1_val = wind1.get_wind(239, 923, 292728238)
230 | self.assertIsInstance(wind1_val, type(np.array([0.0, 0.0])))
231 | self.assertEqual(3, len(wind1_val))
232 |
233 | wind2 = wind.Wind(variable=False)
234 | wind2_val = wind2.get_wind(-239, -923, -292728238)
235 | self.assertEqual(0, wind2_val[0])
236 | self.assertEqual(0, wind2_val[1])
237 |
238 |
239 | if __name__ == "__main__":
240 | unittest.main()
241 |
--------------------------------------------------------------------------------
/campyros/tests/test_stats.json:
--------------------------------------------------------------------------------
1 | {
2 | "name":"stats_testcase",
3 | "itterations":1,
4 | "launch_site":{
5 | "rail_length":[10,0.01],
6 | "rail_yaw":[0,0.03],
7 | "rail_pitch":[0,0.03],
8 | "alt":[0,1],
9 | "long":[0.1160127,0.01],
10 | "lat":[52.2079404,0.01],
11 | "fast_wind":1,
12 | "run_date":"20210206",
13 | "run_time":"00",
14 | "run_plus_time":"000",
15 | "variable_wind":0
16 | },
17 | "aero_file":"campyros/tests/testaero.csv",
18 | "aero":{
19 | "COP":0.05,
20 | "CN":0.05,
21 | "CA":0.05,
22 | "ref_area":[0.0305128422,0.01],
23 | "area_per_fin":[0.07369928,0.01],
24 | "fins":4
25 | },
26 | "parachute":{
27 | "main_s":[13.9,0.05],
28 | "main_c_d":[0.78,0.05],
29 | "drogue_s":[1.13,0.05],
30 | "drogue_c_d":[0.78,0.05],
31 | "main_alt":[1000,0.05],
32 | "attatch_distance":[0,0],
33 | "failure_rate":0.01
34 | },
35 | "enviromental":{
36 | "gravity":0.01,
37 | "pressure":0.05,
38 | "density":0.05,
39 | "speed_of_sound":0.05
40 | },
41 | "motor_file":"campyros/tests/testmotor.csv",
42 | "motor_pos":6.529,
43 | "thrust_error":{
44 | "magnitude":0.03,
45 | "alignment":0.0006
46 | },
47 | "mass":{
48 | "dry_mass":[60,0.01],
49 | "rocket_length":[6.529,0.01],
50 | "rocket_radius":[98.5e-3,0.01],
51 | "rocket_wall_thickness":[1e-2,0.01],
52 | "pos_tank_bottom":[4.456,0.01],
53 | "pos_solidfuel_bottom_base":[4.856,0.01],
54 | "length_port":0.01,
55 | "lden":0.01,
56 | "lmass":0.01,
57 | "smass":0.01,
58 | "sden":0.01,
59 | "vmass":0.01,
60 | "vden":0.01,
61 | "fuel_diameter":0.01
62 | }
63 | }
--------------------------------------------------------------------------------
/campyros/transforms.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from scipy.spatial.transform import Rotation
3 |
4 | from .constants import r_earth, ang_vel_earth, f
5 |
6 | __copyright__ = """
7 |
8 | Copyright 2021 Jago Strong-Wright & Daniel Gibbons
9 |
10 | This program is free software: you can redistribute it and/or modify
11 | it under the terms of the GNU General Public License as published by
12 | the Free Software Foundation, either version 3 of the License, or
13 | (at your option) any later version.
14 |
15 | This program is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | GNU General Public License for more details.
19 |
20 | You should have received a copy of the GNU General Public License
21 | along with this program. If not, see .
22 |
23 | """
24 |
25 |
26 | def pos_i2alt(pos_i, time):
27 | """Returns the altitude (height from surface in launch frame) from pos_i
28 |
29 | Note
30 | ----
31 | -Uses a spherical Earth model
32 |
33 | Parameters
34 | ----------
35 | pos_i : numpy array
36 | Position of the rocket in the inertial coordinate system [x,y,z] /m
37 |
38 | Returns
39 | -------
40 | float
41 | Altitude /m
42 |
43 | """
44 | l, l, a = i2lla(pos_i, time)
45 | return a
46 |
47 |
48 | def pos_l2i(pos_l, launch_site, time):
49 | """Converts position in launch frame to position in inertial frame.
50 | Note
51 | ----
52 | -Converting spherical coordinates to Cartesian
53 | -https://math.libretexts.org/Bookshelves/Calculus/Book%3A_Calculus_(OpenStax)/12%3A_Vectors_in_Space/12.7%3A_Cylindrical_and_Spherical_Coordinates#:~:text=To%20convert%20a%20point%20from,y2%2Bz2)
54 | Parameters
55 | ----------
56 | pos_l : numpy array
57 | Position in the launch site frame [x,y,z] /m
58 | launch_site : LaunchSite object
59 | Holds the launch site parameters
60 | time : float
61 | Time since ignition /s
62 | Returns
63 | -------
64 | numpy array
65 | Position in the inertial frame
66 | """
67 |
68 | pos_launch_site_i = lla2i(launch_site.lat, launch_site.longi, launch_site.alt, time)
69 |
70 | pos_rocket_i = pos_launch_site_i + direction_l2i(pos_l, launch_site, time)
71 |
72 | return pos_rocket_i
73 |
74 |
75 | def pos_i2l(position, launch_site, time):
76 | """Converts position in launch frame to position in inertial frame.
77 | Note
78 | ----
79 | -Converting spherical coordinates to Cartesian
80 | -https://math.libretexts.org/Bookshelves/Calculus/Book%3A_Calculus_(OpenStax)/12%3A_Vectors_in_Space/12.7%3A_Cylindrical_and_Spherical_Coordinates#:~:text=To%20convert%20a%20point%20from,y2%2Bz2)
81 | Parameters
82 | ----------
83 | position : numpy array
84 | Position in the inertial frame [x,y,z] /m
85 | launch_site : LaunchSite object
86 | Holds the launch site parameters
87 | time : float
88 | Time since ignition /s
89 | Returns
90 | -------
91 | numpy array
92 | Position in the launch frame
93 | """
94 | pos_launch_site_i = lla2i(launch_site.lat, launch_site.longi, launch_site.alt, time)
95 |
96 | pos_rocket_i = position
97 | pos_rocket_l = direction_i2l(pos_rocket_i - pos_launch_site_i, launch_site, time)
98 |
99 | return pos_rocket_l
100 |
101 |
102 | def vel_i2l(vel_i, launch_site, time):
103 | """Converts velocity in inertial frame to velocity in launch frame.
104 | Note
105 | ----
106 | -v = w x r for a rigid body, where v, w and r are vectors
107 | Parameters
108 | ----------
109 | vel_i : numpy array
110 | Velocity in the inertial frame [x,y,z] /m/s
111 | launch_site : LaunchSite object
112 | Holds the launch site parameters
113 | time : float
114 | Time since ignition /s
115 | Returns
116 | -------
117 | numpy array
118 | Velocity in the launch frame
119 | """
120 | w_earth = np.array([0, 0, ang_vel_earth])
121 | pos_launch_site_i = lla2i(launch_site.lat, launch_site.longi, launch_site.alt, time)
122 |
123 | launch_site_velocity_i = np.cross(w_earth, pos_launch_site_i)
124 |
125 | return direction_i2l(vel_i - launch_site_velocity_i, launch_site, time)
126 |
127 |
128 | def vel_l2i(vel_l, launch_site, time):
129 | """Converts position in launch frame to position in inertial frame.
130 | Note
131 | ----
132 | -v = w x r for a rigid body, where v, w and r are vectors
133 | Parameters
134 | ----------
135 | vel_i : numpy array
136 | Velocity in the launch frame [x,y,z] /m/s
137 | launch_site : LaunchSite object
138 | Holds the launch site parameters
139 | time : float
140 | Time since ignition /s
141 | Returns
142 | -------
143 | numpy array
144 | Velocity in the inertial frame
145 | """
146 | w_earth = np.array([0, 0, ang_vel_earth])
147 | pos_launch_site_i = lla2i(launch_site.lat, launch_site.longi, launch_site.alt, time)
148 |
149 | launch_site_velocity_i = np.cross(w_earth, pos_launch_site_i)
150 |
151 | return direction_l2i(vel_l, launch_site, time) + launch_site_velocity_i
152 |
153 |
154 | def direction_i2l(vector, launch_site, time):
155 | """Converts position in launch frame to position in inertial frame.
156 | Note
157 | ----
158 | -Problem in the yaw pitch conversions, unexplained negative sign needed
159 | Parameters
160 | ----------
161 | vector : numpy array
162 | Vector in the inertial frame [x,y,z] /m/s
163 | launch_site : LaunchSite object
164 | Holds the launch site parameters
165 | time : float
166 | Time since ignition /s
167 | Returns
168 | -------
169 | numpy array
170 | Vector in the launch frame
171 | """
172 | return Rotation.from_euler(
173 | "zy",
174 | [
175 | -launch_site.longi - (180 / np.pi) * ang_vel_earth * time,
176 | -90 + launch_site.lat,
177 | ],
178 | degrees=True,
179 | ).apply(vector)
180 |
181 |
182 | def direction_l2i(vector, launch_site, time):
183 | """Converts position in launch frame to position in inertial frame.
184 | Note
185 | ----
186 | -Problem in the yaw pitch conversions, unexplained negative sign needed
187 | Parameters
188 | ----------
189 | vector : numpy array
190 | Vector in the launch frame [x,y,z] /m/s
191 | launch_site : LaunchSite object
192 | Holds the launch site parameters
193 | time : float
194 | Time since ignition /s
195 | Returns
196 | -------
197 | numpy array
198 | Vector in the launch frame
199 | """
200 | return (
201 | Rotation.from_euler(
202 | "zy",
203 | [
204 | -launch_site.longi - (180 / np.pi) * ang_vel_earth * time,
205 | -90 + launch_site.lat,
206 | ],
207 | degrees=True,
208 | )
209 | .inv()
210 | .apply(vector)
211 | )
212 |
213 |
214 | def i2airspeed(pos_i, vel_i, launch_site, time):
215 | """Converts velocity in the inertial frame to airspeed (before wind is taken into account) using site launch coordinates
216 |
217 |
218 | Note
219 | ----
220 | Assumes that the atmosphere moves at the same angular velocity as the Earth. Hence, at a given altitude, v_atmosphere = w_earth x r_i
221 |
222 | Parameters
223 | ----------
224 | vel_i : numpy array
225 | Velocity in the inertial frame [x,y,z] /m/s
226 | pos_i : numpy array
227 | Position in the intertial frame [x,y,z] /m
228 | launch_site : LaunchSite object
229 | Holds the launch site parameters
230 | time : float
231 | Time since ignition /s
232 | Returns
233 | -------
234 | numpy array
235 | Airspeed (assuming no wind), given using launch site coordinates
236 | """
237 | w_earth = np.array([0, 0, ang_vel_earth])
238 | atmosphere_velocity_i = np.cross(w_earth, pos_i)
239 |
240 | return direction_i2l(vel_i - atmosphere_velocity_i, launch_site, time)
241 |
242 |
243 | def lla2i(lat, lon, alt, time):
244 | # see http://www.mathworks.de/help/toolbox/aeroblks/llatoecefposition.html
245 | cosLat = np.cos(lat * np.pi / 180)
246 | sinLat = np.sin(lat * np.pi / 180)
247 | FF = (1.0 - f) ** 2
248 | C = 1 / np.sqrt(cosLat ** 2 + FF * sinLat ** 2)
249 | S = C * FF
250 |
251 | x = (r_earth * C + alt) * cosLat * np.cos(lon * np.pi / 180 + ang_vel_earth * time)
252 | y = (r_earth * C + alt) * cosLat * np.sin(lon * np.pi / 180 + ang_vel_earth * time)
253 | z = (r_earth * S + alt) * sinLat
254 | return np.array([x, y, z])
255 |
256 |
257 | def i2lla(pos_i, time):
258 | # https://uk.mathworks.com/help/aeroblks/ecefpositiontolla.html
259 | x, y, z = pos_i[0], pos_i[1], pos_i[2]
260 | longi = np.angle(x + 1j * y)
261 |
262 | s = np.sqrt(x ** 2 + y ** 2)
263 | e = np.sqrt(1 - (1 - f) ** 2)
264 |
265 | beta = np.angle(1j * z + (1 - f) * s)
266 | mu = 0
267 | mu_ = np.angle(
268 | (s - e ** 2 * r_earth * np.cos(beta) ** 3)
269 | + 1j * (z + e ** 2 * (1 - f) * r_earth * np.sin(beta) ** 3 / (1 - e ** 2))
270 | )
271 | while (
272 | abs(mu_ - mu) > 1e-2
273 | ): # May as well be low since it is only ever used to query wind which is only granular to 0.25 long/lat
274 | mu = mu_
275 | beta = np.angle(1j * (1 - f) * np.sin(mu) + np.cos(mu))
276 | mu_ = np.angle(
277 | (s - e ** 2 * r_earth * np.cos(beta) ** 3)
278 | + 1j * (z + e ** 2 * (1 - f) * r_earth * np.sin(beta) ** 3 / (1 - e ** 2))
279 | )
280 | n = r_earth / np.sqrt(1 - e ** 2 * np.sin(mu) ** 2)
281 |
282 | h = s * np.cos(mu) + (z + e ** 2 * n * np.sin(mu)) * np.sin(mu) - n
283 |
284 | lat = mu * 180 / np.pi
285 | longi = (longi - ang_vel_earth * time) * 180 / np.pi
286 |
287 | return lat, longi, h
288 |
--------------------------------------------------------------------------------
/campyros/wind.py:
--------------------------------------------------------------------------------
1 | import getgfs, dateutil.parser, pickle, warnings
2 | from datetime import datetime, date, timedelta
3 | from pathlib import Path
4 | import numpy as np
5 |
6 | __copyright__ = """
7 |
8 | Copyright 2021 Jago Strong-Wright
9 |
10 | This program is free software: you can redistribute it and/or modify
11 | it under the terms of the GNU General Public License as published by
12 | the Free Software Foundation, either version 3 of the License, or
13 | (at your option) any later version.
14 |
15 | This program is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | GNU General Public License for more details.
19 |
20 | You should have received a copy of the GNU General Public License
21 | along with this program. If not, see .
22 |
23 | """
24 |
25 | Path("data/wind").mkdir(parents=True, exist_ok=True)
26 |
27 |
28 | class Wind:
29 | def __init__(
30 | self,
31 | datetime=(date.today() - timedelta(days=2)).strftime("%Y%m%d %H:%M"),
32 | default=np.array([0, 0, 0]),
33 | variable=True,
34 | cache=False,
35 | ):
36 | """Initites wind object so wind for some position can be searched
37 |
38 | Note
39 | ----
40 | - Historic forcasts are not yet implimented in getgfs so historics not available in this
41 |
42 | Args:
43 | datetime (string): datetime of flight, can be in 'any' format as long as datetime parser can work it out (for now this will need to be UTC)
44 | cache (bool, optional): cache lat/long/time profile, useful for stats models where downloading for each run is a bottle neck. Defaults to False.
45 | """
46 | self.launch_time = dateutil.parser.parse(datetime).timestamp()
47 | self.cache = cache
48 | self.default = default
49 | self.variable = variable
50 |
51 | self.forecast = getgfs.Forecast("0p25", "1hr")
52 | self.profiles = {} # (lat,long,datetime):interp profile
53 | self.points = [] # tuples (lat,long,forecast)
54 |
55 | def get_wind(self, lat, long, alt, flight_time=0):
56 | """[summary]
57 |
58 | Args:
59 | lat (float): latitude
60 | long (float): longitude
61 | alt (float): altitude
62 | flight_time (float, optional): time into flight. Defaults to 0.
63 |
64 | Returns:
65 | tuple: returns u and v components of wind
66 | """
67 | if self.variable == True:
68 | lat = [
69 | float(self.forecast.coords["lat"]["resolution"]) * n
70 | + float(self.forecast.coords["lat"]["minimum"])
71 | for n in range(0, int(self.forecast.coords["lat"]["grads_size"]))
72 | ][self.forecast.value_to_index("lat", lat)]
73 | long = [
74 | float(self.forecast.coords["lon"]["resolution"]) * n
75 | + float(self.forecast.coords["lon"]["minimum"])
76 | for n in range(0, int(self.forecast.coords["lon"]["grads_size"]))
77 | ][self.forecast.value_to_index("lon", long)]
78 | request_timestamp = self.launch_time + flight_time
79 | request_datetime = datetime.fromtimestamp(request_timestamp).strftime(
80 | "%Y-%m-%d %H:%M"
81 | )
82 | forecast_to_use = self.forecast.datetime_to_forecast(request_datetime)
83 | if (lat, long, forecast_to_use) not in self.points:
84 | if (
85 | self.cache == False
86 | or not Path(
87 | "data/wind/%s_%s_%s.pkl" % (lat, long, forecast_to_use)
88 | ).is_file()
89 | ):
90 | try:
91 | self.profiles[
92 | (lat, long, forecast_to_use)
93 | ] = self.forecast.get_windprofile(request_datetime, lat, long)
94 | self.points.append((lat, long, forecast_to_use))
95 | except Exception as e:
96 | warnings.warn(
97 | "Wind could not be found - defaults used, gfspy gave the message:"
98 | )
99 | print(e)
100 | return self.default
101 |
102 | if self.cache == True:
103 | with open(
104 | "data/wind/%s_%s_%s.pkl" % (lat, long, forecast_to_use),
105 | "wb",
106 | ) as dump_file:
107 | pickle.dump(
108 | self.profiles[(lat, long, forecast_to_use)], dump_file
109 | )
110 | else:
111 | with open(
112 | "data/wind/%s_%s_%s.pkl" % (lat, long, forecast_to_use), "rb"
113 | ) as dump_file:
114 | self.profiles[(lat, long, forecast_to_use)] = pickle.load(
115 | dump_file
116 | )
117 | self.points.append((lat, long, forecast_to_use))
118 |
119 | return np.array(
120 | [
121 | self.profiles[(lat, long, forecast_to_use)][0](alt),
122 | self.profiles[(lat, long, forecast_to_use)][1](alt),
123 | 0,
124 | ]
125 | )
126 | else:
127 | return self.default
128 |
129 |
130 | if __name__ == "__main__":
131 | w = Wind("20210428 15:00", cache=True)
132 | print(w.get(2938, 29328, 20866566))
133 | """times=np.linspace(0,60*60*12,1000)
134 | x=[]
135 | for t in times:
136 | x.append(w.get(37,20,1000,t)[0])
137 | import matplotlib.pyplot as plt
138 | plt.plot(times,x)
139 | plt.show()"""
140 |
--------------------------------------------------------------------------------
/data/Notes about RasAero Data.txt:
--------------------------------------------------------------------------------
1 | As far as I can tell:
2 |
3 | 'Power-Off' assumes the rocket is coasting, "Power-On" assumes the engine is firing.
4 | I think the only difference is that the latter takes into account the nozzle exit pressure?
5 |
6 | CA is axially along the body (i.e. x-direction)
7 | CN is normal to the body (i.e. y or z direction, since it is rotationally symmetric).
8 |
9 | CD is parralel to the incoming flow
10 | CL is normal to the incoming flow
11 |
12 | I think "CNalpha (0 to 4 deg) (per rad)" is simply
13 |
14 | (CN at alpha=4 - CN at alpha=0) / (4 degrees, in radians)
15 |
16 | CP (0 to 4 deg) is simply the CP at alpha=4
17 |
18 | It uses the largest cross-sectional area to normalise force coefficients.
19 | = 0.0305128422 m^2 for the RasAero II Marlet 4 file
20 |
21 |
22 |
--------------------------------------------------------------------------------
/data/Sample_Parachute_Cd.csv:
--------------------------------------------------------------------------------
1 | Mach_Number,Drogue_Cd,Main_Cd
2 | 0,0.78,0.78
3 | 0.05,0.78,0.78
4 | 0.1,0.78,0.78
5 | 0.15,0.78,0.78
6 | 0.2,0.78,0.78
7 | 0.25,0.78,0.78
8 | 0.3,0.78,0.78
9 | 0.35,0.78,0.78
10 | 0.4,0.78,0.78
11 | 0.45,0.78,0.78
12 | 0.5,0.78,0.78
13 | 0.55,0.78,0.78
14 | 0.6,0.78,0.78
15 | 0.65,0.78,0.78
16 | 0.7,0.78,0.78
17 | 0.75,0.78,0.78
18 | 0.8,0.78,0.78
19 | 0.85,0.78,0.78
20 | 0.9,0.78,0.78
21 | 0.95,0.78,0.78
22 | 1,0.78,0.78
23 | 1.05,0.78,0.78
24 | 1.1,0.78,0.78
25 | 1.15,0.78,0.78
26 | 1.2,0.78,0.78
27 | 1.25,0.78,0.78
28 | 1.3,0.78,0.78
29 | 1.35,0.78,0.78
30 | 1.4,0.78,0.78
31 | 1.45,0.78,0.78
32 | 1.5,0.78,0.78
33 | 1.55,0.78,0.78
34 | 1.6,0.78,0.78
35 | 1.65,0.78,0.78
36 | 1.7,0.78,0.78
37 | 1.75,0.78,0.78
38 | 1.8,0.78,0.78
39 | 1.85,0.78,0.78
40 | 1.9,0.78,0.78
41 | 1.95,0.78,0.78
42 | 2,0.78,0.78
43 | 2.05,0.78,0.78
44 | 2.1,0.78,0.78
45 | 2.15,0.78,0.78
46 | 2.2,0.78,0.78
47 | 2.25,0.78,0.78
48 | 2.3,0.78,0.78
49 | 2.35,0.78,0.78
50 | 2.4,0.78,0.78
51 | 2.45,0.78,0.78
52 | 2.5,0.78,0.78
53 | 2.55,0.78,0.78
54 | 2.6,0.78,0.78
55 | 2.65,0.78,0.78
56 | 2.7,0.78,0.78
57 | 2.75,0.78,0.78
58 | 2.8,0.78,0.78
59 | 2.85,0.78,0.78
60 | 2.9,0.78,0.78
61 | 2.95,0.78,0.78
62 | 3,0.78,0.78
63 | 3.05,0.78,0.78
64 | 3.1,0.78,0.78
65 | 3.15,0.78,0.78
66 | 3.2,0.78,0.78
67 | 3.25,0.78,0.78
68 | 3.3,0.78,0.78
69 | 3.35,0.78,0.78
70 | 3.4,0.78,0.78
71 | 3.45,0.78,0.78
72 | 3.5,0.78,0.78
73 | 3.55,0.78,0.78
74 | 3.6,0.78,0.78
75 | 3.65,0.78,0.78
76 | 3.7,0.78,0.78
77 | 3.75,0.78,0.78
78 | 3.8,0.78,0.78
79 | 3.85,0.78,0.78
80 | 3.9,0.78,0.78
81 | 3.95,0.78,0.78
82 | 4,0.78,0.78
83 | 4.05,0.78,0.78
84 | 4.1,0.78,0.78
85 | 4.15,0.78,0.78
86 | 4.2,0.78,0.78
87 | 4.25,0.78,0.78
88 | 4.3,0.78,0.78
89 | 4.35,0.78,0.78
90 | 4.4,0.78,0.78
91 | 4.45,0.78,0.78
92 | 4.5,0.78,0.78
93 | 4.55,0.78,0.78
94 | 4.6,0.78,0.78
95 | 4.65,0.78,0.78
96 | 4.7,0.78,0.78
97 | 4.75,0.78,0.78
98 | 4.8,0.78,0.78
99 | 4.85,0.78,0.78
100 | 4.9,0.78,0.78
101 | 4.95,0.78,0.78
102 | 5,0.78,0.78
103 | 5.05,0.78,0.78
104 | 5.1,0.78,0.78
105 | 5.15,0.78,0.78
106 | 5.2,0.78,0.78
107 | 5.25,0.78,0.78
108 | 5.3,0.78,0.78
109 | 5.35,0.78,0.78
110 | 5.4,0.78,0.78
111 | 5.45,0.78,0.78
112 | 5.5,0.78,0.78
113 | 5.55,0.78,0.78
114 | 5.6,0.78,0.78
115 | 5.65,0.78,0.78
116 | 5.7,0.78,0.78
117 | 5.75,0.78,0.78
118 | 5.8,0.78,0.78
119 | 5.85,0.78,0.78
120 | 5.9,0.78,0.78
121 | 5.95,0.78,0.78
122 | 6,0.78,0.78
123 | 6.05,0.78,0.78
124 | 6.1,0.78,0.78
125 | 6.15,0.78,0.78
126 | 6.2,0.78,0.78
127 | 6.25,0.78,0.78
128 | 6.3,0.78,0.78
129 | 6.35,0.78,0.78
130 | 6.4,0.78,0.78
131 | 6.45,0.78,0.78
132 | 6.5,0.78,0.78
133 | 6.55,0.78,0.78
134 | 6.6,0.78,0.78
135 | 6.65,0.78,0.78
136 | 6.7,0.78,0.78
137 | 6.75,0.78,0.78
138 | 6.8,0.78,0.78
139 | 6.85,0.78,0.78
140 | 6.9,0.78,0.78
141 | 6.95,0.78,0.78
142 | 7,0.78,0.78
143 | 7.05,0.78,0.78
144 | 7.1,0.78,0.78
145 | 7.15,0.78,0.78
146 | 7.2,0.78,0.78
147 | 7.25,0.78,0.78
148 | 7.3,0.78,0.78
149 | 7.35,0.78,0.78
150 | 7.4,0.78,0.78
151 | 7.45,0.78,0.78
152 | 7.5,0.78,0.78
153 | 7.55,0.78,0.78
154 | 7.6,0.78,0.78
155 | 7.65,0.78,0.78
156 | 7.7,0.78,0.78
157 | 7.75,0.78,0.78
158 | 7.8,0.78,0.78
159 | 7.85,0.78,0.78
160 | 7.9,0.78,0.78
161 | 7.95,0.78,0.78
162 | 8,0.78,0.78
163 |
--------------------------------------------------------------------------------
/data/martlet4.py:
--------------------------------------------------------------------------------
1 | import CamPyRoS as trajectory
2 | import csv
3 | import time
4 | import numpy as np
5 | import pandas as pd
6 |
7 | """Import motor data to use for the mass model"""
8 | motor_csv = pd.read_csv("novus_sim_6.1/motor_out.csv")
9 |
10 | time_array = motor_csv["Time"]
11 | smass_array = motor_csv["Solid Fuel Mass (kg)"]
12 | S_DEN = motor_csv["Solid Fuel Density (kg/m^3)"][0]
13 | S_L = motor_csv["Solid Fuel Length (m)"][0]
14 | S_ROUT = motor_csv["Solid Fuel Outer Diameter (m)"][0]
15 | vmass_array = motor_csv["Vapour Mass (kg)"]
16 | vden_array = motor_csv["Vapour Density (kg/m^3)"]
17 | lmass_array = motor_csv["Liquid Mass (kg)"]
18 | lden_array = motor_csv["Liquid Density (kg/m^3)"]
19 |
20 | """Rocket parameters"""
21 | DRY_MASS = 60 # Rocket dry mass (kg)
22 | ROCKET_L = 6.529 # Rocket length (m)
23 | ROCKET_R = 98.5e-3 # Rocket radius (m)
24 | ROCKET_T = 1e-2 # Rocket wall thickness (m) - used when approximating the rocket airframe as a thin walled cylinder
25 | POS_TANK_BOTTOM = (
26 | 4.456 # Distance between the nose tip and the bottom of the nitrous tank (m)
27 | )
28 | POS_SOLIDFUEL_BOTTOM = (
29 | 4.856 + S_L
30 | ) # Distance between the nose tip and bottom of the solid fuel grain (m)
31 | REF_AREA = 0.0305128422 # Reference area for aerodynamic coefficients (m^2)
32 |
33 | """Set up aerodynamic properties"""
34 | # Get approximate values for the rotational damping coefficients
35 | C_DAMP_PITCH = trajectory.pitch_damping_coefficient(
36 | ROCKET_L, ROCKET_R, fin_number=4, area_per_fin=0.07369928
37 | )
38 | C_DAMP_ROLL = 0
39 |
40 | # Import drag coefficients from RASAero II
41 | aero_data = trajectory.AeroData.from_rasaero(
42 | "data/Martlet4RASAeroII.CSV", REF_AREA, C_DAMP_PITCH, C_DAMP_ROLL
43 | )
44 | # aero_data.show_plot() #Show plots of how the program interpreted the data, so you can visually check if it's correct
45 |
46 | """Set up the mass model"""
47 | mass_model = trajectory.MassModel()
48 | mass_model.add_hollowcylinder(
49 | DRY_MASS, ROCKET_R, ROCKET_R - ROCKET_T, ROCKET_L, ROCKET_L / 2
50 | )
51 | mass_model.add_liquidtank(
52 | lmass_array,
53 | lden_array,
54 | time_array,
55 | ROCKET_R,
56 | POS_TANK_BOTTOM,
57 | vmass_array,
58 | vden_array,
59 | )
60 | mass_model.add_solidfuel(
61 | smass_array, time_array, S_DEN, S_ROUT, S_L, POS_SOLIDFUEL_BOTTOM
62 | )
63 |
64 | """Create the other objects needed to initialise the Rocket object"""
65 | pulsar = trajectory.Motor.from_novus("novus_sim_6.1/motor_out.csv", pos=ROCKET_L)
66 |
67 | """
68 | launch_site = trajectory.LaunchSite(rail_length=10,
69 | rail_yaw=0,
70 | rail_pitch=0,
71 | alt=1,
72 | longi=0.1160127,
73 | lat=52.2079404,
74 | variable_wind=True,
75 | forcast_plus_time="016",
76 | run_date="20201216",
77 | fast_wind=False)
78 | """
79 | launch_site = trajectory.LaunchSite(
80 | rail_length=5,
81 | rail_yaw=0,
82 | rail_pitch=0,
83 | alt=1,
84 | longi=0,
85 | lat=0,
86 | variable_wind=False,
87 | default_wind=np.array([5, 0, 0]),
88 | ) # Use this version if you don't want to use the real wind (e.g. to test something else)
89 |
90 | parachute = trajectory.Parachute(
91 | main_s=13.9,
92 | main_c_d=0.78,
93 | drogue_s=1.13,
94 | drogue_c_d=0.78,
95 | main_alt=1000,
96 | attach_distance=0,
97 | )
98 |
99 | """Create the Rocket object"""
100 | martlet4 = trajectory.Rocket(
101 | mass_model,
102 | pulsar,
103 | aero_data,
104 | launch_site,
105 | h=0.05,
106 | variable=True,
107 | alt_poll_interval=1,
108 | parachute=parachute,
109 | )
110 |
--------------------------------------------------------------------------------
/data/w0_0_('20210321', '12', '[1]').pkl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/data/w0_0_('20210321', '12', '[1]').pkl
--------------------------------------------------------------------------------
/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 = source
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 |
--------------------------------------------------------------------------------
/docs/build/doctrees/campyros.doctree:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/doctrees/campyros.doctree
--------------------------------------------------------------------------------
/docs/build/doctrees/campyros.tests.doctree:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/doctrees/campyros.tests.doctree
--------------------------------------------------------------------------------
/docs/build/doctrees/environment.pickle:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/doctrees/environment.pickle
--------------------------------------------------------------------------------
/docs/build/doctrees/index.doctree:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/doctrees/index.doctree
--------------------------------------------------------------------------------
/docs/build/doctrees/modules.doctree:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/doctrees/modules.doctree
--------------------------------------------------------------------------------
/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: 505d63ed4f5b50875b75cf2e4a8823ae
4 | tags: 645f666f9bcd5a90fca523b33c5a78b7
5 |
--------------------------------------------------------------------------------
/docs/build/html/_sources/campyros.rst.txt:
--------------------------------------------------------------------------------
1 | campyros package
2 | ================
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 | :maxdepth: 4
9 |
10 | campyros.tests
11 |
12 | Submodules
13 | ----------
14 |
15 | campyros.aero module
16 | --------------------
17 |
18 | .. automodule:: campyros.aero
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | campyros.constants module
24 | -------------------------
25 |
26 | .. automodule:: campyros.constants
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 | campyros.gui module
32 | -------------------
33 |
34 | .. automodule:: campyros.gui
35 | :members:
36 | :undoc-members:
37 | :show-inheritance:
38 |
39 | campyros.heating module
40 | -----------------------
41 |
42 | .. automodule:: campyros.heating
43 | :members:
44 | :undoc-members:
45 | :show-inheritance:
46 |
47 | campyros.main module
48 | --------------------
49 |
50 | .. automodule:: campyros.main
51 | :members:
52 | :undoc-members:
53 | :show-inheritance:
54 |
55 | campyros.mass module
56 | --------------------
57 |
58 | .. automodule:: campyros.mass
59 | :members:
60 | :undoc-members:
61 | :show-inheritance:
62 |
63 | campyros.motor module
64 | ---------------------
65 |
66 | .. automodule:: campyros.motor
67 | :members:
68 | :undoc-members:
69 | :show-inheritance:
70 |
71 | campyros.plot module
72 | --------------------
73 |
74 | .. automodule:: campyros.plot
75 | :members:
76 | :undoc-members:
77 | :show-inheritance:
78 |
79 | campyros.post module
80 | --------------------
81 |
82 | .. automodule:: campyros.post
83 | :members:
84 | :undoc-members:
85 | :show-inheritance:
86 |
87 | campyros.ray\_alt module
88 | ------------------------
89 |
90 | .. automodule:: campyros.ray_alt
91 | :members:
92 | :undoc-members:
93 | :show-inheritance:
94 |
95 | campyros.slosh module
96 | ---------------------
97 |
98 | .. automodule:: campyros.slosh
99 | :members:
100 | :undoc-members:
101 | :show-inheritance:
102 |
103 | campyros.statistical module
104 | ---------------------------
105 |
106 | .. automodule:: campyros.statistical
107 | :members:
108 | :undoc-members:
109 | :show-inheritance:
110 |
111 | campyros.transforms module
112 | --------------------------
113 |
114 | .. automodule:: campyros.transforms
115 | :members:
116 | :undoc-members:
117 | :show-inheritance:
118 |
119 | campyros.wind module
120 | --------------------
121 |
122 | .. automodule:: campyros.wind
123 | :members:
124 | :undoc-members:
125 | :show-inheritance:
126 |
127 | Module contents
128 | ---------------
129 |
130 | .. automodule:: campyros
131 | :members:
132 | :undoc-members:
133 | :show-inheritance:
134 |
--------------------------------------------------------------------------------
/docs/build/html/_sources/campyros.tests.rst.txt:
--------------------------------------------------------------------------------
1 | campyros.tests package
2 | ======================
3 |
4 | Submodules
5 | ----------
6 |
7 | campyros.tests.test module
8 | --------------------------
9 |
10 | .. automodule:: campyros.tests.test
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | Module contents
16 | ---------------
17 |
18 | .. automodule:: campyros.tests
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
--------------------------------------------------------------------------------
/docs/build/html/_sources/index.rst.txt:
--------------------------------------------------------------------------------
1 | .. CamPyRoS documentation master file, created by
2 | sphinx-quickstart on Mon Mar 1 12:13:47 2021.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to CamPyRoS's documentation!
7 | ====================================
8 |
9 | .. toctree::
10 | :maxdepth: 2
11 | :caption: Contents:
12 |
13 |
14 |
15 | Indices and tables
16 | ==================
17 |
18 | * :ref:`genindex`
19 | * :ref:`modindex`
20 | * :ref:`search`
21 |
--------------------------------------------------------------------------------
/docs/build/html/_sources/modules.rst.txt:
--------------------------------------------------------------------------------
1 | campyros
2 | ========
3 |
4 | .. toctree::
5 | :maxdepth: 4
6 |
7 | campyros
8 |
--------------------------------------------------------------------------------
/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}}
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/html/_static/css/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/html/_static/css/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/html/_static/css/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/html/_static/css/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/lato-bold-italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/html/_static/css/fonts/lato-bold-italic.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/lato-bold-italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/html/_static/css/fonts/lato-bold-italic.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/lato-bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/html/_static/css/fonts/lato-bold.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/lato-bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/html/_static/css/fonts/lato-bold.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/lato-normal-italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/html/_static/css/fonts/lato-normal-italic.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/lato-normal-italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/html/_static/css/fonts/lato-normal-italic.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/lato-normal.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/html/_static/css/fonts/lato-normal.woff
--------------------------------------------------------------------------------
/docs/build/html/_static/css/fonts/lato-normal.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/html/_static/css/fonts/lato-normal.woff2
--------------------------------------------------------------------------------
/docs/build/html/_static/doctools.js:
--------------------------------------------------------------------------------
1 | /*
2 | * doctools.js
3 | * ~~~~~~~~~~~
4 | *
5 | * Sphinx JavaScript utilities for all documentation.
6 | *
7 | * :copyright: Copyright 2007-2020 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 | jQuery.urldecode = function(x) {
34 | return decodeURIComponent(x).replace(/\+/g, ' ');
35 | };
36 |
37 | /**
38 | * small helper function to urlencode strings
39 | */
40 | jQuery.urlencode = encodeURIComponent;
41 |
42 | /**
43 | * This function returns the parsed url parameters of the
44 | * current request. Multiple values per key are supported,
45 | * it will always return arrays of strings for the value parts.
46 | */
47 | jQuery.getQueryParameters = function(s) {
48 | if (typeof s === 'undefined')
49 | s = document.location.search;
50 | var parts = s.substr(s.indexOf('?') + 1).split('&');
51 | var result = {};
52 | for (var i = 0; i < parts.length; i++) {
53 | var tmp = parts[i].split('=', 2);
54 | var key = jQuery.urldecode(tmp[0]);
55 | var value = jQuery.urldecode(tmp[1]);
56 | if (key in result)
57 | result[key].push(value);
58 | else
59 | result[key] = [value];
60 | }
61 | return result;
62 | };
63 |
64 | /**
65 | * highlight a given string on a jquery object by wrapping it in
66 | * span elements with the given class name.
67 | */
68 | jQuery.fn.highlightText = function(text, className) {
69 | function highlight(node, addItems) {
70 | if (node.nodeType === 3) {
71 | var val = node.nodeValue;
72 | var pos = val.toLowerCase().indexOf(text);
73 | if (pos >= 0 &&
74 | !jQuery(node.parentNode).hasClass(className) &&
75 | !jQuery(node.parentNode).hasClass("nohighlight")) {
76 | var span;
77 | var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
78 | if (isInSVG) {
79 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
80 | } else {
81 | span = document.createElement("span");
82 | span.className = className;
83 | }
84 | span.appendChild(document.createTextNode(val.substr(pos, text.length)));
85 | node.parentNode.insertBefore(span, node.parentNode.insertBefore(
86 | document.createTextNode(val.substr(pos + text.length)),
87 | node.nextSibling));
88 | node.nodeValue = val.substr(0, pos);
89 | if (isInSVG) {
90 | var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
91 | var bbox = node.parentElement.getBBox();
92 | rect.x.baseVal.value = bbox.x;
93 | rect.y.baseVal.value = bbox.y;
94 | rect.width.baseVal.value = bbox.width;
95 | rect.height.baseVal.value = bbox.height;
96 | rect.setAttribute('class', className);
97 | addItems.push({
98 | "parent": node.parentNode,
99 | "target": rect});
100 | }
101 | }
102 | }
103 | else if (!jQuery(node).is("button, select, textarea")) {
104 | jQuery.each(node.childNodes, function() {
105 | highlight(this, addItems);
106 | });
107 | }
108 | }
109 | var addItems = [];
110 | var result = this.each(function() {
111 | highlight(this, addItems);
112 | });
113 | for (var i = 0; i < addItems.length; ++i) {
114 | jQuery(addItems[i].parent).before(addItems[i].target);
115 | }
116 | return result;
117 | };
118 |
119 | /*
120 | * backward compatibility for jQuery.browser
121 | * This will be supported until firefox bug is fixed.
122 | */
123 | if (!jQuery.browser) {
124 | jQuery.uaMatch = function(ua) {
125 | ua = ua.toLowerCase();
126 |
127 | var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
128 | /(webkit)[ \/]([\w.]+)/.exec(ua) ||
129 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
130 | /(msie) ([\w.]+)/.exec(ua) ||
131 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
132 | [];
133 |
134 | return {
135 | browser: match[ 1 ] || "",
136 | version: match[ 2 ] || "0"
137 | };
138 | };
139 | jQuery.browser = {};
140 | jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
141 | }
142 |
143 | /**
144 | * Small JavaScript module for the documentation.
145 | */
146 | var Documentation = {
147 |
148 | init : function() {
149 | this.fixFirefoxAnchorBug();
150 | this.highlightSearchWords();
151 | this.initIndexTable();
152 | if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) {
153 | this.initOnKeyListeners();
154 | }
155 | },
156 |
157 | /**
158 | * i18n support
159 | */
160 | TRANSLATIONS : {},
161 | PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; },
162 | LOCALE : 'unknown',
163 |
164 | // gettext and ngettext don't access this so that the functions
165 | // can safely bound to a different name (_ = Documentation.gettext)
166 | gettext : function(string) {
167 | var translated = Documentation.TRANSLATIONS[string];
168 | if (typeof translated === 'undefined')
169 | return string;
170 | return (typeof translated === 'string') ? translated : translated[0];
171 | },
172 |
173 | ngettext : function(singular, plural, n) {
174 | var translated = Documentation.TRANSLATIONS[singular];
175 | if (typeof translated === 'undefined')
176 | return (n == 1) ? singular : plural;
177 | return translated[Documentation.PLURALEXPR(n)];
178 | },
179 |
180 | addTranslations : function(catalog) {
181 | for (var key in catalog.messages)
182 | this.TRANSLATIONS[key] = catalog.messages[key];
183 | this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')');
184 | this.LOCALE = catalog.locale;
185 | },
186 |
187 | /**
188 | * add context elements like header anchor links
189 | */
190 | addContextElements : function() {
191 | $('div[id] > :header:first').each(function() {
192 | $('').
193 | attr('href', '#' + this.id).
194 | attr('title', _('Permalink to this headline')).
195 | appendTo(this);
196 | });
197 | $('dt[id]').each(function() {
198 | $('').
199 | attr('href', '#' + this.id).
200 | attr('title', _('Permalink to this definition')).
201 | appendTo(this);
202 | });
203 | },
204 |
205 | /**
206 | * workaround a firefox stupidity
207 | * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075
208 | */
209 | fixFirefoxAnchorBug : function() {
210 | if (document.location.hash && $.browser.mozilla)
211 | window.setTimeout(function() {
212 | document.location.href += '';
213 | }, 10);
214 | },
215 |
216 | /**
217 | * highlight the search words provided in the url in the text
218 | */
219 | highlightSearchWords : function() {
220 | var params = $.getQueryParameters();
221 | var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : [];
222 | if (terms.length) {
223 | var body = $('div.body');
224 | if (!body.length) {
225 | body = $('body');
226 | }
227 | window.setTimeout(function() {
228 | $.each(terms, function() {
229 | body.highlightText(this.toLowerCase(), 'highlighted');
230 | });
231 | }, 10);
232 | $('
' + _('Hide Search Matches') + '
')
234 | .appendTo($('#searchbox'));
235 | }
236 | },
237 |
238 | /**
239 | * init the domain index toggle buttons
240 | */
241 | initIndexTable : function() {
242 | var togglers = $('img.toggler').click(function() {
243 | var src = $(this).attr('src');
244 | var idnum = $(this).attr('id').substr(7);
245 | $('tr.cg-' + idnum).toggle();
246 | if (src.substr(-9) === 'minus.png')
247 | $(this).attr('src', src.substr(0, src.length-9) + 'plus.png');
248 | else
249 | $(this).attr('src', src.substr(0, src.length-8) + 'minus.png');
250 | }).css('display', '');
251 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) {
252 | togglers.click();
253 | }
254 | },
255 |
256 | /**
257 | * helper function to hide the search marks again
258 | */
259 | hideSearchWords : function() {
260 | $('#searchbox .highlight-link').fadeOut(300);
261 | $('span.highlighted').removeClass('highlighted');
262 | },
263 |
264 | /**
265 | * make the url absolute
266 | */
267 | makeURL : function(relativeURL) {
268 | return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL;
269 | },
270 |
271 | /**
272 | * get the current relative url
273 | */
274 | getCurrentURL : function() {
275 | var path = document.location.pathname;
276 | var parts = path.split(/\//);
277 | $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() {
278 | if (this === '..')
279 | parts.pop();
280 | });
281 | var url = parts.join('/');
282 | return path.substring(url.lastIndexOf('/') + 1, path.length - 1);
283 | },
284 |
285 | initOnKeyListeners: function() {
286 | $(document).keydown(function(event) {
287 | var activeElementType = document.activeElement.tagName;
288 | // don't navigate when in search box or textarea
289 | if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT'
290 | && !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey) {
291 | switch (event.keyCode) {
292 | case 37: // left
293 | var prevHref = $('link[rel="prev"]').prop('href');
294 | if (prevHref) {
295 | window.location.href = prevHref;
296 | return false;
297 | }
298 | case 39: // right
299 | var nextHref = $('link[rel="next"]').prop('href');
300 | if (nextHref) {
301 | window.location.href = nextHref;
302 | return false;
303 | }
304 | }
305 | }
306 | });
307 | }
308 | };
309 |
310 | // quick alias for translations
311 | _ = Documentation.gettext;
312 |
313 | $(document).ready(function() {
314 | Documentation.init();
315 | });
316 |
--------------------------------------------------------------------------------
/docs/build/html/_static/documentation_options.js:
--------------------------------------------------------------------------------
1 | var DOCUMENTATION_OPTIONS = {
2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
3 | VERSION: '1.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 | };
--------------------------------------------------------------------------------
/docs/build/html/_static/file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/html/_static/file.png
--------------------------------------------------------------------------------
/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){}});
--------------------------------------------------------------------------------
/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/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/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="#"]'))}t.length>0&&($(".wy-menu-vertical .current").removeClass("current"),t.addClass("current"),t.closest("li.toctree-l1").addClass("current"),t.closest("li.toctree-l1").parent().addClass("current"),t.closest("li.toctree-l1").addClass("current"),t.closest("li.toctree-l2").addClass("current"),t.closest("li.toctree-l3").addClass("current"),t.closest("li.toctree-l4").addClass("current"),t.closest("li.toctree-l5").addClass("current"),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"),e.siblings().find("li.current").removeClass("current"),e.find("> ul li.current").removeClass("current"),e.toggleClass("current")}},"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;t2;a==
12 | null&&(a=[]);if(y&&a.reduce===y)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(z&&a.reduceRight===z)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=
13 | function(a,c,b){var e;E(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(A&&a.filter===A)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(B&&a.every===B)return a.every(c,b);j(a,function(a,g,h){if(!(e=
14 | e&&c.call(b,a,g,h)))return n});return e};var E=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(C&&a.some===C)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return n});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return p&&a.indexOf===p?a.indexOf(c)!=-1:b=E(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck=
15 | function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;bd?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,
17 | c,d){d||(d=b.identity);for(var e=0,f=a.length;e>1;d(a[g])=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e=0;d--)b=[a[d].apply(this,b)];return b[0]}};
24 | b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=J||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.defaults=function(a){j(i.call(arguments,
25 | 1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return q(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=o||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};
26 | b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!b.has(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};
27 | b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,b){return I.call(a,b)};b.noConflict=function(){r._=G;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.mixin=function(a){j(b.functions(a),
28 | function(c){K(c,b[c]=a[c])})};var L=0;b.uniqueId=function(a){var b=L++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var t=/.^/,u=function(a){return a.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape||t,function(a,b){return"',_.escape("+
29 | u(b)+"),'"}).replace(d.interpolate||t,function(a,b){return"',"+u(b)+",'"}).replace(d.evaluate||t,function(a,b){return"');"+u(b).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var v=function(a,c){return c?b(a).chain():a},K=function(a,c){m.prototype[a]=
30 | function(){var a=i.call(arguments);H.call(a,this._wrapped);return v(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return v(d,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return v(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=
31 | true;return this};m.prototype.value=function(){return this._wrapped}}).call(this);
32 |
--------------------------------------------------------------------------------
/docs/build/html/campyros.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | campyros package — CamPyRoS 1.0.0 documentation
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
116 |
117 |
118 |
119 |
120 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 | - »
155 |
156 | - campyros package
157 |
158 |
159 | -
160 |
161 |
162 | View page source
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
campyros package
177 |
178 |
Subpackages
179 |
189 |
190 |
191 |
Submodules
192 |
193 |
194 |
campyros.aero module
195 |
196 |
197 |
campyros.constants module
198 |
199 |
200 |
campyros.gui module
201 |
202 |
203 |
campyros.heating module
204 |
205 |
206 |
campyros.main module
207 |
208 |
209 |
campyros.mass module
210 |
211 |
212 |
campyros.motor module
213 |
214 |
215 |
campyros.plot module
216 |
217 |
218 |
campyros.post module
219 |
220 |
221 |
campyros.ray_alt module
222 |
223 |
224 |
campyros.slosh module
225 |
226 |
227 |
campyros.statistical module
228 |
229 |
230 |
campyros.transforms module
231 |
232 |
233 |
campyros.wind module
234 |
235 |
236 |
Module contents
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
--------------------------------------------------------------------------------
/docs/build/html/campyros.tests.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | campyros.tests package — CamPyRoS 1.0.0 documentation
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
102 |
103 |
104 |
105 |
106 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 | - »
141 |
142 | - campyros.tests package
143 |
144 |
145 | -
146 |
147 |
148 | View page source
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
campyros.tests package
163 |
164 |
Submodules
165 |
166 |
167 |
campyros.tests.test module
168 |
169 |
170 |
Module contents
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
--------------------------------------------------------------------------------
/docs/build/html/genindex.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Index — CamPyRoS 1.0.0 documentation
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
94 |
95 |
96 |
97 |
98 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 | - »
133 |
134 | - Index
135 |
136 |
137 | -
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
Index
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
--------------------------------------------------------------------------------
/docs/build/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Welcome to CamPyRoS’s documentation! — CamPyRoS 1.0.0 documentation
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
98 |
99 |
100 |
101 |
102 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 | - »
137 |
138 | - Welcome to CamPyRoS’s documentation!
139 |
140 |
141 | -
142 |
143 |
144 | View page source
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
Welcome to CamPyRoS’s documentation!
159 |
160 |
161 |
162 |
163 |
Indices and tables
164 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
--------------------------------------------------------------------------------
/docs/build/html/modules.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | campyros — CamPyRoS 1.0.0 documentation
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
97 |
98 |
99 |
100 |
101 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 | - »
136 |
137 | - campyros
138 |
139 |
140 | -
141 |
142 |
143 | View page source
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
campyros
158 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
--------------------------------------------------------------------------------
/docs/build/html/objects.inv:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/docs/build/html/objects.inv
--------------------------------------------------------------------------------
/docs/build/html/search.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Search — CamPyRoS 1.0.0 documentation
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
97 |
98 |
99 |
100 |
101 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 | - »
136 |
137 | - Search
138 |
139 |
140 | -
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
201 |
202 |
203 |
204 |
205 |
206 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
--------------------------------------------------------------------------------
/docs/build/html/searchindex.js:
--------------------------------------------------------------------------------
1 | Search.setIndex({docnames:["campyros","campyros.tests","index","modules"],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":2,"sphinx.domains.rst":2,"sphinx.domains.std":1,sphinx:56},filenames:["campyros.rst","campyros.tests.rst","index.rst","modules.rst"],objects:{},objnames:{},objtypes:{},terms:{aero:3,constant:3,content:3,gui:3,heat:3,index:2,main:3,mass:3,modul:[2,3],motor:3,packag:3,page:2,plot:3,post:3,ray_alt:3,search:2,slosh:3,statist:3,submodul:3,subpackag:3,test:[0,3],transform:3,wind:3},titles:["campyros package","campyros.tests package","Welcome to CamPyRoS\u2019s documentation!","campyros"],titleterms:{aero:0,campyro:[0,1,2,3],constant:0,content:[0,1],document:2,gui:0,heat:0,indic:2,main:0,mass:0,modul:[0,1],motor:0,packag:[0,1],plot:0,post:0,ray_alt:0,slosh:0,statist:0,submodul:[0,1],subpackag:0,tabl:2,test:1,transform:0,welcom:2,wind:0}})
--------------------------------------------------------------------------------
/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=source
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/source/CamPyRoS.rst:
--------------------------------------------------------------------------------
1 | campyros package
2 | ================
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 | :maxdepth: 4
9 |
10 | campyros.tests
11 |
12 | Submodules
13 | ----------
14 |
15 | campyros.aero module
16 | --------------------
17 |
18 | .. automodule:: campyros.aero
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | campyros.constants module
24 | -------------------------
25 |
26 | .. automodule:: campyros.constants
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 | campyros.gui module
32 | -------------------
33 |
34 | .. automodule:: campyros.gui
35 | :members:
36 | :undoc-members:
37 | :show-inheritance:
38 |
39 | campyros.heating module
40 | -----------------------
41 |
42 | .. automodule:: campyros.heating
43 | :members:
44 | :undoc-members:
45 | :show-inheritance:
46 |
47 | campyros.main module
48 | --------------------
49 |
50 | .. automodule:: campyros.main
51 | :members:
52 | :undoc-members:
53 | :show-inheritance:
54 |
55 | campyros.mass module
56 | --------------------
57 |
58 | .. automodule:: campyros.mass
59 | :members:
60 | :undoc-members:
61 | :show-inheritance:
62 |
63 | campyros.motor module
64 | ---------------------
65 |
66 | .. automodule:: campyros.motor
67 | :members:
68 | :undoc-members:
69 | :show-inheritance:
70 |
71 | campyros.plot module
72 | --------------------
73 |
74 | .. automodule:: campyros.plot
75 | :members:
76 | :undoc-members:
77 | :show-inheritance:
78 |
79 | campyros.post module
80 | --------------------
81 |
82 | .. automodule:: campyros.post
83 | :members:
84 | :undoc-members:
85 | :show-inheritance:
86 |
87 | campyros.ray\_alt module
88 | ------------------------
89 |
90 | .. automodule:: campyros.ray_alt
91 | :members:
92 | :undoc-members:
93 | :show-inheritance:
94 |
95 | campyros.slosh module
96 | ---------------------
97 |
98 | .. automodule:: campyros.slosh
99 | :members:
100 | :undoc-members:
101 | :show-inheritance:
102 |
103 | campyros.statistical module
104 | ---------------------------
105 |
106 | .. automodule:: campyros.statistical
107 | :members:
108 | :undoc-members:
109 | :show-inheritance:
110 |
111 | campyros.transforms module
112 | --------------------------
113 |
114 | .. automodule:: campyros.transforms
115 | :members:
116 | :undoc-members:
117 | :show-inheritance:
118 |
119 | campyros.wind module
120 | --------------------
121 |
122 | .. automodule:: campyros.wind
123 | :members:
124 | :undoc-members:
125 | :show-inheritance:
126 |
127 | Module contents
128 | ---------------
129 |
130 | .. automodule:: campyros
131 | :members:
132 | :undoc-members:
133 | :show-inheritance:
134 |
--------------------------------------------------------------------------------
/docs/source/campyros.tests.rst:
--------------------------------------------------------------------------------
1 | campyros.tests package
2 | ======================
3 |
4 | Submodules
5 | ----------
6 |
7 | campyros.tests.test module
8 | --------------------------
9 |
10 | .. automodule:: campyros.tests.test
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | Module contents
16 | ---------------
17 |
18 | .. automodule:: campyros.tests
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
--------------------------------------------------------------------------------
/docs/source/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # This file only contains a selection of the most common options. For a full
4 | # list see the documentation:
5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
6 |
7 | # -- Path setup --------------------------------------------------------------
8 |
9 | # If extensions (or modules to document with autodoc) are in another directory,
10 | # add these directories to sys.path here. If the directory is relative to the
11 | # documentation root, use os.path.abspath to make it absolute, like shown here.
12 | #
13 | import os
14 | import sys
15 | import sphinx_rtd_theme
16 |
17 | sys.path.insert(0, os.path.split(os.path.abspath(".."))[0])
18 | # -- Project information -----------------------------------------------------
19 |
20 | project = "CamPyRoS"
21 | copyright = "2020, Jago Strong-Wright and Daniel Gibbons"
22 | author = "Jago Strong-Wright and Daniel Gibbons"
23 |
24 | # The full version, including alpha/beta/rc tags
25 | release = "1.0.0"
26 |
27 |
28 | # -- General configuration ---------------------------------------------------
29 |
30 | # Add any Sphinx extension module names here, as strings. They can be
31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
32 | # ones.
33 | extensions = []
34 |
35 | # Add any paths that contain templates here, relative to this directory.
36 | templates_path = ["_templates"]
37 |
38 | # List of patterns, relative to source directory, that match files and
39 | # directories to ignore when looking for source files.
40 | # This pattern also affects html_static_path and html_extra_path.
41 | exclude_patterns = []
42 |
43 |
44 | # -- Options for HTML output -------------------------------------------------
45 |
46 | # The theme to use for HTML and HTML Help pages. See the documentation for
47 | # a list of builtin themes.
48 | #
49 | html_theme = "sphinx_rtd_theme"
50 |
51 | # Add any paths that contain custom static files (such as style sheets) here,
52 | # relative to this directory. They are copied after the builtin static files,
53 | # so a file named "default.css" will overwrite the builtin "default.css".
54 | html_static_path = ["_static"]
55 |
56 | extensions = ["sphinx.ext.autodoc", "sphinx.ext.napoleon"]
57 |
--------------------------------------------------------------------------------
/docs/source/develop.rst:
--------------------------------------------------------------------------------
1 | Want to help out?
2 | =================
3 | Check out our `contribution guidelines `_
--------------------------------------------------------------------------------
/docs/source/help.rst:
--------------------------------------------------------------------------------
1 | Need help
2 | =========
3 | If you have any problems with this libray please email js2430@cam.ac.uk or dug20@cam.ac.uk, open an `issue `_ or a `discussion `_
4 |
--------------------------------------------------------------------------------
/docs/source/index.rst:
--------------------------------------------------------------------------------
1 | CamPyRoS - A 6DOF Rocket Trajectory Simulator
2 | =============================================
3 | .. image:: https://zenodo.org/badge/308847422.svg
4 | :target: https://zenodo.org/badge/latestdoi/308847422
5 |
6 | CamPyRoS (Cambridge Python Rocketry Simulator) is a Python package which provides fully featured rocket trajectory simulation including features like:
7 |
8 | - 6 degrees of freedom (3 translational, 3 rotational)
9 | - Monte Carlo stochastic analysis
10 | - Aerodynamic heating model
11 | - Use of live wind data
12 | - Variable mass and moments of inertia models
13 |
14 | Getting started
15 | ===============
16 | Currently not all dependancies are supported by the same install methods so the easiest install doesn't contain the full functionality. To install the core library:
17 |
18 | .. code-block:: python
19 | :linenos:
20 |
21 | pip install git+https://github.com/cuspaceflight/CamPyRoS.git
22 |
23 | The "wind" and "statistics" modules will not run. Statistics has a dependancy not fully supported by windows, to install it:
24 |
25 | `pip install ray` on most platforms, for Windows problems see `here `_.
26 |
27 | Wind depends on a library called iris which can only be installed with conda:
28 |
29 | .. code-block:: python
30 | :linenos:
31 |
32 | conda install iris, iris_grib
33 |
34 | .. note::
35 | Note: after some more testing this won't work for anyone on Windows, I am in the process fo sorting this out (the problem is a codec dependancy which there is no way for us to fix) by writing an library to read the other GFS distrobution method, see `getgfs `_. You can still run and contribute without using the wind module.
36 |
37 | This may then demand you install another library when you try to run it:
38 |
39 | .. code-block:: python
40 | :linenos:
41 |
42 | pip install eccodes-python
43 |
44 | Alternativly you can download this repository and move it to either your system path or somewhere you will exclusivly use it from then:
45 |
46 | .. code-block:: python
47 | :linenos:
48 |
49 | conda env create -f enviroment.yml -n
50 | conda activate
51 |
52 | From within the downloaded folder.
53 |
54 | Usage
55 | =====
56 |
57 | The repository contains some examples you can run:
58 | - `example.ipynb` or `example.py` : Launch of a simple rocket (the Martlet 4).
59 | - `Stats Model Example.ipynb` : Example of how to use the statistics model and stochastic analysis.
60 | - `Aerodynamic Heating Example.ipynb` : Example of how to run an aerodynamic heating simulation.
61 |
62 | Helping out
63 | ===========
64 | If you would like to contribute please have a look at the `guidelines `_
65 |
66 |
67 | In progress
68 | ===========
69 | - **GUI:** An incomplete (and outdated) GUI has been made using Tkinter, and is in gui.py.
70 | - **Slosh modelling:** Some slosh modelling functions have been put together in slosh.py, based on the following source - `The Dynamic Behavior of Liquids in Moving Containers, with Applications to Space Vehicle Technology `_.
71 | - **Wind variability:** Statistical analysis of historic wind forecasts and obervations are analysed to create a Guassian difference profile to vary the wind in the statistical models (see wind-stats branch for a very poorly documented insight to current progress)
72 |
73 |
74 | Potential for expansion
75 | =======================
76 | - **Multistage rockets**
77 | - **Fin cant, roll damping, and roll acceleration:** `OpenRocket Technical Documentation `_
78 | - **CFD coupling:** `PyFoam `_, `Simulations of 6-DOF Motion with a Cartesian Method `_
79 | - **Multiphysics coupling:** `PRECICE `_
80 |
81 | Cite as
82 | =======
83 | Daniel Gibbons, & Jago Strong-Wright. (2021, February 11). cuspaceflight/CamPyRoS: First release! (Version V1.0). Zenodo. http://doi.org/10.5281/zenodo.4535672
84 |
85 | References
86 | ==========
87 |
88 | [1] - `Stochastic Six-Degree-of-Freedom Flight Simulator for Passively Controlled High-Power Rockets `_
89 |
90 | [2] - `Tangent ogive nose aerodynamic heating program: NQLD019 `_
91 |
92 | [3] - `NASA Basic Considerations for Rocket Trajectory Simulation `_
93 |
94 | [4] - `SIX DEGREE OF FREEDOM DIGITAL SIMULATION MODEL FOR UNGUIDED FIN-STABILIZED ROCKETS `_
95 |
96 | [5] - `Trajectory Prediction for a Typical Fin Stabilized Artillery Rocket `_
97 |
98 | [6] - `Central Limit Theorem and Sample Size `_
99 |
100 | [7] - `Monte Carlo Simulations: Number of Iterations and Accuracy `_
101 |
102 | [8] - `Method for Calculating Aerodynamic Heating on Sounding Rocket Tangent Ogive Noses `_
103 |
104 | [9] - `Six degree-of-freedom (6-DOF) Flight Simulation Check-cases `_
105 |
106 |
107 |
108 | Technical Documentation
109 | =======================
110 | Full technical documentation is coming soon
111 |
112 |
113 | Content
114 | =======
115 | .. toctree::
116 | :maxdepth: 3
117 | :caption: Contents
118 |
119 | help.rst
120 | campyros.rst
121 | develop.rst
122 | licences.rst
123 |
124 |
125 |
126 | Index and moduals
127 | =================
128 |
129 | * :ref:`genindex`
130 | * :ref:`modindex`
131 | * :ref:`search`
132 |
--------------------------------------------------------------------------------
/docs/source/modules.rst:
--------------------------------------------------------------------------------
1 | campyros
2 | ========
3 |
4 | .. toctree::
5 | :maxdepth: 4
6 |
7 | campyros
8 |
--------------------------------------------------------------------------------
/environment.yml:
--------------------------------------------------------------------------------
1 | name: campyros
2 | channels:
3 | - anaconda
4 | - idaholab
5 | - conda-forge
6 | - defaults
7 | dependencies:
8 | - antlr-python-runtime=4.7.2=py38h32f6830_1002
9 | - appdirs=1.4.4=pyh9f0ad1d_0
10 | - appnope=0.1.0=py38_1001
11 | - backcall=0.2.0=py_0
12 | - bokeh=2.2.3=py38h50d1736_0
13 | - brotlipy=0.7.0=py38h5406a74_1001
14 | - bzip2=1.0.8=hc929b4f_4
15 | - c-ares=1.17.1=hc929b4f_0
16 | - ca-certificates=2020.12.5=h033912b_0
17 | - cartopy=0.18.0=py38h45f612a_9
18 | - certifi=2020.12.5=py38h50d1736_1
19 | - cf-units=2.1.4=py38h174b24a_2
20 | - cffi=1.14.4=py38h6afc60c_0
21 | - cftime=1.2.1=py38h174b24a_1
22 | - click=7.1.2=pyh9f0ad1d_0
23 | - cloudpickle=1.6.0=py_0
24 | - coolprop=6.4.1=py38h0a5c65b_2
25 | - cryptography=3.4.4=py38h43df06b_0
26 | - curl=7.71.1=hcb81553_8
27 | - cycler=0.10.0=py_2
28 | - cytoolz=0.11.0=py38h5406a74_3
29 | - dask=2021.2.0=pyhd8ed1ab_0
30 | - dask-core=2021.2.0=pyhd8ed1ab_0
31 | - decorator=4.4.2=py_0
32 | - distributed=2021.2.0=py38h50d1736_0
33 | - expat=2.2.10=h1c7c35f_0
34 | - fluids=1.0.0=pyh44b312d_0
35 | - freetype=2.10.4=h4cff582_1
36 | - fsspec=0.8.5=pyhd8ed1ab_0
37 | - fuzzywuzzy=0.18.0=pyhd8ed1ab_0
38 | - geos=3.9.1=h1c7c35f_0
39 | - gmp=6.2.1=h2e338ed_0
40 | - gmpy2=2.1.0b1=py38haf64e2c_1
41 | - hdf4=4.2.13=h71d84a9_1004
42 | - hdf5=1.10.6=nompi_hc5d9132_1114
43 | - heapdict=1.0.1=py_0
44 | - idna=2.10=pyh9f0ad1d_0
45 | - importlib-metadata=3.4.0=py38h50d1736_0
46 | - importlib_metadata=3.4.0=hd8ed1ab_0
47 | - importlib_resources=5.1.0=py38h50d1736_0
48 | - ipykernel=5.3.4=py38h5ca1d4c_0
49 | - ipython=7.18.1=py38h5ca1d4c_0
50 | - ipython_genutils=0.2.0=py_1
51 | - iris=3.0.1=py38h50d1736_0
52 | - jedi=0.18.0=py38h50d1736_2
53 | - jinja2=2.11.3=pyh44b312d_0
54 | - jpeg=9d=hbcb3906_0
55 | - jupyter_client=6.1.7=py_0
56 | - jupyter_core=4.6.3=py38_0
57 | - kiwisolver=1.3.1=py38hd9c93a9_1
58 | - krb5=1.17.2=h60d9502_0
59 | - lcms2=2.12=h577c468_0
60 | - libblas=3.9.0=8_openblas
61 | - libcblas=3.9.0=8_openblas
62 | - libcurl=7.71.1=h9bf37e3_8
63 | - libcxx=11.0.1=habf9029_0
64 | - libedit=3.1.20191231=h0678c8f_2
65 | - libev=4.33=haf1e3a3_1
66 | - libffi=3.2.1=hb1e8313_1007
67 | - libgfortran=5.0.0=9_3_0_h6c81a4c_17
68 | - libgfortran5=9.3.0=h6c81a4c_17
69 | - liblapack=3.9.0=8_openblas
70 | - libnetcdf=4.7.4=nompi_h9d8a93f_107
71 | - libnghttp2=1.43.0=h07e645a_0
72 | - libopenblas=0.3.12=openmp_h54245bb_1
73 | - libpng=1.6.37=h7cec526_2
74 | - libsodium=1.0.18=h1de35cc_0
75 | - libssh2=1.9.0=h8a08a2b_5
76 | - libtiff=4.2.0=h355d032_0
77 | - libwebp-base=1.2.0=hbcf498f_0
78 | - llvm-openmp=11.0.1=h7c73e74_0
79 | - locket=0.2.0=py_2
80 | - lz4-c=1.9.3=h046ec9c_0
81 | - markupsafe=1.1.1=py38h5406a74_3
82 | - metpy=1.0=pyhd8ed1ab_0
83 | - mpc=1.1.0=ha57cd0f_1009
84 | - mpfr=4.0.2=h72d8aaf_1
85 | - mpmath=1.2.1=pyhd8ed1ab_0
86 | - msgpack-python=1.0.2=py38hd9c93a9_1
87 | - ncurses=6.2=h2e338ed_4
88 | - netcdf4=1.5.5.1=nompi_py38h0bc7383_101
89 | - numexpr=2.7.2=py38he9f00de_0
90 | - olefile=0.46=pyh9f0ad1d_1
91 | - openssl=1.1.1i=h35c211d_0
92 | - packaging=20.9=pyh44b312d_0
93 | - pandas=1.2.2=py38hb77cc89_0
94 | - parso=0.8.0=py_0
95 | - partd=1.1.0=py_0
96 | - pexpect=4.8.0=py38_1
97 | - pickleshare=0.7.5=py38_1001
98 | - pillow=8.1.0=py38h4c06724_2
99 | - pint=0.16.1=py_0
100 | - pip=21.0.1=pyhd8ed1ab_0
101 | - pooch=1.3.0=pyhd8ed1ab_0
102 | - proj=7.2.0=h19c2039_1
103 | - prompt-toolkit=3.0.8=py_0
104 | - psutil=5.8.0=py38h5406a74_1
105 | - ptyprocess=0.6.0=py38_0
106 | - pycparser=2.20=pyh9f0ad1d_2
107 | - pygments=2.7.1=py_0
108 | - pyke=1.1.1=pyhd8ed1ab_1004
109 | - pyopenssl=20.0.1=pyhd8ed1ab_0
110 | - pyparsing=2.4.7=pyh9f0ad1d_0
111 | - pyproj=3.0.0.post1=py38h9d4eb05_0
112 | - pyshp=2.1.3=pyh44b312d_0
113 | - pysocks=1.7.1=py38h50d1736_3
114 | - python=3.8.0=hd366da7_5
115 | - python-dateutil=2.8.1=py_0
116 | - python-levenshtein=0.12.2=py38hca655e8_0
117 | - python-xxhash=2.0.0=py38h5406a74_1
118 | - python_abi=3.8=1_cp38
119 | - pytz=2021.1=pyhd8ed1ab_0
120 | - pyyaml=5.4.1=py38h5406a74_0
121 | - pyzmq=19.0.2=py38hb1e8313_1
122 | - readline=8.0=h0678c8f_2
123 | - requests=2.25.1=pyhd3deb0d_0
124 | - scipy=1.6.0=py38ha68b950_0
125 | - setuptools=49.6.0=py38h50d1736_3
126 | - shapely=1.7.1=py38h51457ac_3
127 | - six=1.15.0=pyh9f0ad1d_0
128 | - sortedcontainers=2.3.0=pyhd8ed1ab_0
129 | - sqlite=3.34.0=h17101e1_0
130 | - sympy=1.7.1=py38h50d1736_1
131 | - tblib=1.6.0=py_0
132 | - thermo=0.1.40=py_0
133 | - tk=8.6.10=h0419947_1
134 | - toolz=0.11.1=py_0
135 | - tornado=6.1=py38h5406a74_1
136 | - traitlets=5.0.5=py_0
137 | - typing_extensions=3.7.4.3=py_0
138 | - udunits2=2.2.27.27=h9371bac_0
139 | - urllib3=1.26.3=pyhd8ed1ab_0
140 | - wcwidth=0.2.5=py_0
141 | - wheel=0.36.2=pyhd3deb0d_0
142 | - xarray=0.16.2=pyhd8ed1ab_0
143 | - xxhash=0.8.0=h35c211d_3
144 | - xz=5.2.5=haf1e3a3_1
145 | - yaml=0.2.5=haf1e3a3_0
146 | - zeromq=4.3.3=hb1e8313_3
147 | - zict=2.0.0=py_0
148 | - zipp=3.4.0=py_0
149 | - zlib=1.2.11=h7795811_1010
150 | - zstd=1.4.8=hf387650_1
151 | - pip:
152 | - aiohttp==3.7.3
153 | - aiohttp-cors==0.7.0
154 | - aioredis==1.3.1
155 | - ambiance==1.1.0
156 | - async-timeout==3.0.1
157 | - attrs==20.3.0
158 | - blessings==1.7
159 | - cachetools==4.2.1
160 | - chardet==3.0.4
161 | - colorama==0.4.4
162 | - colorful==0.5.4
163 | - eccodes-python==0.9.9
164 | - filelock==3.0.12
165 | - gas-dynamics==0.4.2
166 | - google-api-core==1.26.0
167 | - google-auth==1.26.1
168 | - googleapis-common-protos==1.52.0
169 | - gpustat==0.6.0
170 | - grpcio==1.35.0
171 | - hiredis==1.1.0
172 | - iris-grib==0.16.0
173 | - jsonschema==3.2.0
174 | - matplotlib==3.3.2
175 | - multidict==5.1.0
176 | - numpy==1.19.3
177 | - nvidia-ml-py3==7.352.0
178 | - opencensus==0.7.12
179 | - opencensus-context==0.1.2
180 | - prometheus-client==0.9.0
181 | - protobuf==3.14.0
182 | - py-spy==0.3.3
183 | - pyasn1==0.4.8
184 | - pyasn1-modules==0.2.8
185 | - pyrsistent==0.17.3
186 | - ray==1.1.0
187 | - redis==3.5.3
188 | - rsa==4.7
189 | - yarl==1.6.3
190 | prefix: /anaconda3/envs/campyros
191 |
--------------------------------------------------------------------------------
/example.py:
--------------------------------------------------------------------------------
1 | import campyros as pyro
2 | import csv
3 | import time
4 | import numpy as np
5 | import pandas as pd
6 |
7 | """Import motor data to use for the mass model"""
8 | motor_csv = pd.read_csv("novus_sim_6.1/motor_out.csv")
9 | time_array = motor_csv["Time"]
10 | smass_array = motor_csv["Solid Fuel Mass (kg)"]
11 | S_DEN = motor_csv["Solid Fuel Density (kg/m^3)"][0]
12 | S_L = motor_csv["Solid Fuel Length (m)"][0]
13 | S_ROUT = motor_csv["Solid Fuel Outer Diameter (m)"][0]
14 | vmass_array = motor_csv["Vapour Mass (kg)"]
15 | vden_array = motor_csv["Vapour Density (kg/m^3)"]
16 | lmass_array = motor_csv["Liquid Mass (kg)"]
17 | lden_array = motor_csv["Liquid Density (kg/m^3)"]
18 |
19 | """Rocket parameters"""
20 | DRY_MASS = 60 # Rocket dry mass (kg)
21 | ROCKET_L = 6.529 # Rocket length (m)
22 | ROCKET_R = 98.5e-3 # Rocket radius (m)
23 | ROCKET_T = 1e-2 # Rocket wall thickness (m) - used when approximating the rocket airframe as a thin walled cylinder
24 | POS_TANK_BOTTOM = (
25 | 4.456 # Distance between the nose tip and the bottom of the nitrous tank (m)
26 | )
27 | POS_SOLIDFUEL_BOTTOM = (
28 | 4.856 + S_L
29 | ) # Distance between the nose tip and bottom of the solid fuel grain (m)
30 | REF_AREA = 0.0305128422 # Reference area for aerodynamic coefficients (m^2)
31 |
32 | """Set up aerodynamic properties"""
33 | # Get approximate values for the rotational damping coefficients
34 | C_DAMP_PITCH = pyro.pitch_damping_coefficient(
35 | ROCKET_L, ROCKET_R, fin_number=4, area_per_fin=0.07369928
36 | )
37 | C_DAMP_ROLL = 0
38 |
39 | # Import drag coefficients from RASAero II
40 | aero_data = pyro.AeroData.from_rasaero(
41 | "data/Martlet4RasAeroII.CSV", REF_AREA, C_DAMP_PITCH, C_DAMP_ROLL
42 | )
43 | # aero_data.show_plot() #Show plots of how the program interpreted the data, so you can visually check if it's correct
44 |
45 | """Set up the mass model"""
46 | mass_model = pyro.MassModel()
47 | mass_model.add_hollowcylinder(
48 | DRY_MASS, ROCKET_R, ROCKET_R - ROCKET_T, ROCKET_L, ROCKET_L / 2
49 | )
50 | mass_model.add_liquidtank(
51 | lmass_array,
52 | lden_array,
53 | time_array,
54 | ROCKET_R,
55 | POS_TANK_BOTTOM,
56 | vmass_array,
57 | vden_array,
58 | )
59 | mass_model.add_solidfuel(
60 | smass_array, time_array, S_DEN, S_ROUT, S_L, POS_SOLIDFUEL_BOTTOM
61 | )
62 |
63 | """Create the other objects needed to initialise the Rocket object"""
64 | pulsar = pyro.Motor.from_novus("novus_sim_6.1/motor_out.csv", pos=ROCKET_L)
65 |
66 | launch_site = pyro.LaunchSite(
67 | rail_length=5,
68 | rail_yaw=0,
69 | rail_pitch=0,
70 | alt=10,
71 | longi=0.1,
72 | lat=52.1,
73 | variable_wind=True,
74 | cache_Wind=True
75 | )
76 |
77 | parachute_Data = pd.read_csv("data/Sample_Parachute_Cd.csv")
78 | mach_array = parachute_Data["Mach_Number"].to_numpy()
79 | main_cd_array = parachute_Data["Main_Cd"].to_numpy()
80 | dro_cd_array = parachute_Data["Drogue_Cd"].to_numpy()
81 |
82 | parachute = pyro.Parachute(
83 | main_s=13.9,
84 | drogue_s=1.13,
85 | main_c_d=(mach_array, main_cd_array),
86 | drogue_c_d=(mach_array, dro_cd_array),
87 | main_alt=1000,
88 | attach_distance=0,
89 | )
90 | """
91 | parachute = pyro.Parachute(
92 | main_s=13.9,
93 | drogue_s=1.13,
94 | main_cd=0.78,
95 | dro_cd=0.78,
96 | main_alt=1000,
97 | attach_distance=0,
98 | )"""
99 |
100 | """Create the Rocket object"""
101 | martlet4 = pyro.Rocket(
102 | mass_model,
103 | pulsar,
104 | aero_data,
105 | launch_site,
106 | h=0.05,
107 | variable=True,
108 | alt_poll_interval=1,
109 | parachute=parachute,
110 | )
111 |
112 | """Run the simulation"""
113 | t = time.time()
114 | simulation_output = martlet4.run(debug=True, to_json="data/pyro.json")
115 | print(f"Simulation run time: {time.time()-t:.2f} s")
116 |
117 | """Example of how you can import data from a .csv file"""
118 | imported_data = pyro.from_json("data/trajectory.json")
119 |
120 | """Plot the results"""
121 | pyro.plot_launch_trajectory_3d(
122 | imported_data, martlet4, show_orientation=False
123 | ) # Could have also used simulation_output instead of imported_data
124 | pyro.plot_altitude_time(imported_data, martlet4)
125 | pyro.plot_ypr(imported_data, martlet4)
126 | # pyro.animate_orientation(imported_data)
127 |
--------------------------------------------------------------------------------
/examples/stats_settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "name":"stats_example",
3 | "itterations":1000,
4 | "launch_site":{
5 | "rail_length":[10,0.01],
6 | "rail_yaw":[0,0.03],
7 | "rail_pitch":[0,0.03],
8 | "alt":[0,1],
9 | "long":[0.1160127,0.01],
10 | "lat":[52.2079404,0.01],
11 | "fast_wind":1,
12 | "run_date":"20210206",
13 | "run_time":"00",
14 | "run_plus_time":"000"
15 | },
16 | "aero_file":"data/Martlet4RasAeroII.CSV",
17 | "aero":{
18 | "COP":0.05,
19 | "CN":0.05,
20 | "CA":0.05,
21 | "ref_area":[0.0305128422,0.01],
22 | "area_per_fin":[0.07369928,0.01],
23 | "fins":4
24 | },
25 | "parachute":{
26 | "main_s":[13.9,0.05],
27 | "main_c_d":[0.78,0.05],
28 | "drogue_s":[1.13,0.05],
29 | "drogue_c_d":[0.78,0.05],
30 | "main_alt":[1000,0.05],
31 | "attatch_distance":[0,0],
32 | "failure_rate":0.01
33 | },
34 | "enviromental":{
35 | "gravity":0.01,
36 | "pressure":0.05,
37 | "density":0.05,
38 | "speed_of_sound":0.05
39 | },
40 | "motor_file":"novus_sim_6.1/motor_out.csv",
41 | "motor_pos":6.529,
42 | "thrust_error":{
43 | "magnitude":0.03,
44 | "alignment":0.0006
45 | },
46 | "mass":{
47 | "dry_mass":[60,0.01],
48 | "rocket_length":[6.529,0.01],
49 | "rocket_radius":[98.5e-3,0.01],
50 | "rocket_wall_thickness":[1e-2,0.01],
51 | "pos_tank_bottom":[4.456,0.01],
52 | "pos_solidfuel_bottom_base":[4.856,0.01],
53 | "length_port":0.01,
54 | "lden":0.01,
55 | "lmass":0.01,
56 | "smass":0.01,
57 | "sden":0.01,
58 | "vmass":0.01,
59 | "vden":0.01,
60 | "fuel_diameter":0.01
61 | }
62 | }
--------------------------------------------------------------------------------
/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/img/favicon.ico
--------------------------------------------------------------------------------
/img/flowchart1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/img/flowchart1.jpeg
--------------------------------------------------------------------------------
/img/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/legacy/Coordinate System Definitions.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/legacy/Coordinate System Definitions.docx
--------------------------------------------------------------------------------
/legacy/Variable Moment of Inertia Model.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/legacy/Variable Moment of Inertia Model.docx
--------------------------------------------------------------------------------
/legacy/aerodynamic heating test cases/How to run the test cases.txt:
--------------------------------------------------------------------------------
1 | Move the programs into a directory where they can import the 'trajectory' module, then just run them.
2 |
3 | Uncomment the relevant lines if you want to generate new data, instead of just importing it from a .JSON.
4 |
5 | Most of them import a "martlet4" to use as a rocket, but if this isn't available you can make an arbritrary rocket object, the details of the rocket don't matter.
6 |
7 |
--------------------------------------------------------------------------------
/legacy/aerodynamic heating test cases/NQLDW019 Problem BA.py:
--------------------------------------------------------------------------------
1 | """
2 | Used to check data against page 70 of the NASA "TANGENT OGIVE NOSE AERODYNAMIC HEATING PROGRAM - NQLDW019 (NASA)" documentation
3 |
4 | Note that Problem BA in the NASA document is actually at an angle of attack of 10 degrees - which is ignored in my current Python script
5 |
6 | """
7 |
8 | import trajectory, trajectory.aero, trajectory.heating, csv
9 | import numpy as np
10 | from trajectory.transforms import (
11 | pos_l2i,
12 | pos_i2l,
13 | vel_l2i,
14 | vel_i2l,
15 | direction_l2i,
16 | direction_i2l,
17 | i2airspeed,
18 | pos_i2alt,
19 | )
20 |
21 | from martlet4 import (
22 | martlet4,
23 | ) # It doesn't matter which rocket we use, we just need something to use as an input.
24 |
25 | """
26 | Specify the nosecone, using:
27 | xprime = 2.741 ft = 0.8354568 m
28 | yprime = 0.5 ft = 0.1524 m
29 | """
30 | tangent_ogive = trajectory.heating.TangentOgive(xprime=0.8354568, yprime=0.1524)
31 |
32 | # Freestream conditions:
33 | ALT = 15240 # 50000 ft
34 | VINF = 1219.2 # 4000 ft/s
35 | ALPHA = 10 * np.pi / 180
36 |
37 | # Properties using standard atmosphere
38 | TINF = 216.650 # K
39 | PINF = 11597.3 # Pa
40 | RHOINF = 0.186481 # kg/m3
41 | speed_of_sound = 295.070 # m/s
42 | MINF = VINF / speed_of_sound
43 |
44 | # Set up the conditions for problem BA in the paper:
45 | pos_i = pos_l2i([0, 0, ALT], martlet4.launch_site, 0)
46 | vel_i = vel_l2i([VINF, 0, 0], martlet4.launch_site, 0)
47 |
48 | trajectory_data = {
49 | "time": [0, 1],
50 | "pos_i": [pos_i, pos_i],
51 | "vel_i": [vel_i, vel_i],
52 | "b2imat": [None, None],
53 | "w_b": [None, None],
54 | "events": [],
55 | }
56 |
57 | analysis = trajectory.heating.AeroHeatingAnalysis(
58 | tangent_ogive, trajectory_data, martlet4, starting_temperature=300
59 | )
60 | analysis.step(print_style="FORTRAN")
61 |
--------------------------------------------------------------------------------
/legacy/aerodynamic heating test cases/NQLDW019 Problem BA.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuspaceflight/CamPyRoS/6ae4a214e793da6b3b1e65df68d58b35441c7610/legacy/aerodynamic heating test cases/NQLDW019 Problem BA.xlsx
--------------------------------------------------------------------------------
/novus_sim_6.1/Changes from novus_sim_6.txt:
--------------------------------------------------------------------------------
1 | Now stores and exports additional data that is needed more a more complex moment of inertia model in the 6DOF simulator.
2 |
3 | Additional properties added to motor_out.csv:
4 | - Vapour Density
5 | - Vapour Mass
6 | - Liquid Density
7 | - Liquid Mass
8 | - Solid Fuel Mass
9 | - Solid Fuel Density
10 | - Solid Fuel Outer Diameter
11 | - Solid Fuel Length
12 |
--------------------------------------------------------------------------------
/novus_sim_6.1/atmosphere_data.csv:
--------------------------------------------------------------------------------
1 | altitude (m),density (kg/m^3),speed of sound ms^-1,pressure (Pa)
2 | 0,1.225,340.3,101325
3 | 1000,1.11164,336.4,89874.6
4 | 2000,1.00649,332.5,79495.2
5 | 3000,0.909122,328.6,70108.5
6 | 4000,0.819129,324.6,61640.2
7 | 5000,0.736116,320.6,54019.9
8 | 6000,0.659697,316.428,47181
9 | 7000,0.589501,312.3,41060.7
10 | 8000,0.525168,308.063,35599.8
11 | 9000,0.466348,303.793,30742.5
12 | 10000,0.412707,299.5,26436.3
13 | 11000,0.363918,295.07,22632.1
14 | 12000,0.310828,295.07,19330.4
15 | 13000,0.265483,295.07,16510.4
16 | 14000,0.226753,295.07,14101.8
17 | 15000,0.193674,295.1,12044.6
18 | 16000,0.16542,295.1,10287.5
19 | 17000,0.141288,295.1,8786.68
20 | 18000,0.120676,295.1,7504.84
21 | 19000,0.103071,295.1,6410.01
22 | 20000,0.0880349,295.1,5474.89
23 | 21000,0.0748737,295.75,4677.89
24 | 22000,0.0637273,296.428,3999.79
25 | 23000,0.0542803,297.105,3422.43
26 | 24000,0.0462674,297.781,2930.49
27 | 25000,0.0394658,298.4,2511.02
28 | 26000,0.0336882,299.128,2153.09
29 | 27000,0.0287769,299.799,1847.46
30 | 28000,0.0245988,300.468,1586.29
31 | 29000,0.021042,301.136,1362.96
32 | 30000,0.0180119,301.803,1171.87
33 | 31000,0.0154288,302.468,1008.23
34 | 32000,0.013225,303.131,868.019
35 | 33000,0.011262,304.982,748.228
36 | 34000,0.00960889,306.821,646.122
37 | 35000,0.00821392,308.649,558.924
38 | 36000,0.00703441,310.467,484.317
39 | 37000,0.00603513,312.274,420.367
40 | 38000,0.00518691,314.07,365.455
41 | 39000,0.00446557,315.856,318.22
42 | 40000,0.00385101,317.633,277.522
43 | 41000,0.00332648,319.399,242.395
44 | 42000,0.00287802,321.156,212.03
45 | 43000,0.00249393,322.903,185.738
46 | 44000,0.00216443,324.641,162.937
47 | 45000,0.00188129,326.369,143.135
48 | 46000,0.0016376,328.088,125.91
49 | 47000,0.00142753,329.799,110.906
50 | 48000,0.00125825,329.799,97.7545
51 | 49000,0.00110904,329.799,86.1623
52 | 50000,0.000977525,329.799,75.9448
53 | 51000,0.000861606,329.799,66.9389
54 | 52000,0.000766867,328.088,58.9622
55 | 53000,0.00068171,326.369,51.8668
56 | 54000,0.000605252,324.641,45.5632
57 | 55000,0.000536684,322.903,39.97
58 | 56000,0.000475263,321.156,35.0137
59 | 57000,0.000420311,319.399,30.6274
60 | 58000,0.000371207,317.633,26.7509
61 | 59000,0.000327382,315.856,23.3296
62 | 60000,0.000288321,314.07,20.3143
63 | 61000,0.00025355,312.274,17.6606
64 | 62000,0.00022264,310.467,15.3287
65 | 63000,0.0001952,308.649,13.2826
66 | 64000,0.000170875,306.821,11.49
67 | 65000,0.000149342,304.982,9.92203
68 | 66000,0.000130308,303.131,8.55275
69 | 67000,0.00011351,301.269,7.35895
70 | 68000,9.87E-05,299.396,6.31992
71 | 69000,8.57E-05,297.511,5.41717
72 | 70000,7.42E-05,295.614,4.63422
73 | 71000,6.42E-05,293.704,3.95642
74 | 72000,5.52E-05,292.333,3.37176
75 | 73000,4.74E-05,290.955,2.86917
76 | 74000,4.07E-05,289.57,2.43773
77 | 75000,3.49E-05,288.179,2.06792
78 | 76000,2.98E-05,286.781,1.7514
79 | 77000,2.55E-05,285.377,1.48092
80 | 78000,2.17E-05,283.965,1.25012
81 | 79000,1.85E-05,282.546,1.05351
82 | 80000,1.57E-05,281.12,0.88628
83 |
--------------------------------------------------------------------------------
/novus_sim_6.1/example_motor_inputs.txt:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Input parameters for Pulsar 2 motor
3 | ###############################################################################
4 |
5 | VOL_TANK = 60*0.001 #tank volume (m^3)
6 | HEAD_SPACE = 0.1 #initial vapour phase proportion
7 |
8 | # primary injectory orifices were drilled by shaped machining
9 | NUM_INJ1 = 40 #number of primary injector orifices
10 | DIA_INJ1 = 0.0013 #diameter of primary injector orifices (m)
11 |
12 | # secondary injector orifices were drilled prior to first test
13 | NUM_INJ2 = 4 #number of secondary injector orifices
14 | DIA_INJ2 = 0.002 #diameter of secondary injector orifices (m)
15 |
16 | # tertiary injectory orifices are new orifices to be drilled
17 | NUM_INJ3 = 8 # number of tertiary injector orifices
18 | DIA_INJ3 = 0.0015 #diameter of tertiary injector orifices (m)
19 |
20 | DIA_PORT = 0.075 #diameter of fuel port (m)
21 | LENGTH_PORT = 1.33 #length of fuel port (m)
22 | DIA_FUEL = 0.112 #Outside diameter of fuel grain (m)
23 | C_STAR_EFFICIENCY = 0.95 # Ratio between actual and theoretical characteristic velocity
24 |
25 | DIA_THROAT = 0.0432 # nozzle throat diameter (m)
26 | NOZZLE_EFFICIENCY = 0.97 # factor by which to reduce thrust coefficient
27 | NOZZLE_AREA_RATIO = 4.5 # ratio of nozzle exit area to throat area
28 |
29 | DIA_FEED = 0.02 #feed pipe diameter (m)
30 | LENGTH_FEED = 0.5 #feed pipe length (m)
31 | VALVE_MODEL_TYPE = 'ball' # either 'kv' or 'ball' (models as thick orifice)
32 | KV_VALVE = 5 # used if VALVE_MODEL_TRY='kv'
33 | DIA_VALVE = 0.015 # used if VALVE_MODEL_TRY='ball'
34 | LENGTH_VALVE = 0.08 # used if VALVE_MODEL_TRY='ball'
35 |
36 | DENSITY_FUEL = 935 #solid fuel density (kg m^-3)
37 | REG_COEFF = 1.157E-4 #regression rate coefficient (usually 'a' in textbooks)
38 | REG_EXP = 0.331 #regression rate exponent (usually 'n' in textbooks)
39 |
40 | PRES_EXTERNAL = 101325 #external atmospheric pressure at test site (Pa)
41 | temp = 20+273.15 #initial tank temperature (K)
42 |
43 | ###############################################################################
44 | # Input parameters for Pulsar 1 motor
45 | ###############################################################################
46 |
47 | VOL_TANK = 53*0.001 #tank volume (m^3)
48 | HEAD_SPACE = 0.38 #initial vapour phase proportion
49 |
50 | # primary injectory orifices were drilled by shaped machining
51 | NUM_INJ1 = 40 #number of primary injector orifices
52 | DIA_INJ1 = 0.0013 #diameter of primary injector orifices (m)
53 |
54 | # secondary injector orifices were drilled prior to first test
55 | NUM_INJ2 = 4 #number of secondary injector orifices
56 | DIA_INJ2 = 0.002 #diameter of secondary injector orifices (m)
57 |
58 | # tertiary injectory orifices are new orifices to be drilled
59 | NUM_INJ3 = 0 # number of tertiary injector orifices
60 | DIA_INJ3 = 0 #diameter of tertiary injector orifices (m)
61 |
62 | DIA_PORT = 0.0789 #diameter of fuel port (m)
63 | LENGTH_PORT = 1.33 #length of fuel port (m)
64 | DIA_FUEL = 0.112 #Outside diameter of fuel grain (m)
65 | C_STAR_EFFICIENCY = 0.95 # Ratio between actual and theoretical characteristic velocity
66 |
67 | DIA_THROAT = 0.0438 # nozzle throat diameter (m)
68 | NOZZLE_EFFICIENCY = 0.97 # Ratio between actual and theoretical thrust coefficient
69 | NOZZLE_AREA_RATIO = 4.5 # ratio of nozzle exit area to throat area
70 |
71 | DIA_FEED = 0.022 #feed pipe diameter (m)
72 | LENGTH_FEED = 3 #feed pipe length (m)
73 | VALVE_MODEL_TYPE = 'ball' # either 'kv' or 'ball' (models as thick orifice)
74 | KV_VALVE = 0 # used if VALVE_MODEL_TRY='kv'
75 | DIA_VALVE = 0.022 # used if VALVE_MODEL_TRY='ball'
76 | LENGTH_VALVE = 0.04 # used if VALVE_MODEL_TRY='ball'
77 |
78 | DENSITY_FUEL = 935 #solid fuel density (kg m^-3)
79 | REG_COEFF = 1.157E-4
80 | REG_EXP = 0.331
81 |
82 | PRES_EXTERNAL = 101325 #external atmospheric pressure at test site (Pa)
83 | temp = -2+273.15 #initial tank temperature (K)
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/novus_sim_6.1/hybrid.eng:
--------------------------------------------------------------------------------
1 | ;
2 | Pulsar 160 3000 P 50.16 90.16 CUSF
3 | 0.01 4726.16
4 | 0.8 5512.86
5 | 1.6 5478.61
6 | 2.4 5480.23
7 | 3.2 5442.44
8 | 4.0 5403.09
9 | 4.8 5389.32
10 | 5.6 5346.58
11 | 6.39 5334.02
12 | 7.19 5287.51
13 | 7.99 5239.27
14 | 8.79 5189.23
15 | 9.59 5162.47
16 | 10.39 5108.22
17 | 11.19 5051.94
18 | 11.99 4993.54
19 | 12.78 4933.66
20 | 13.58 4889.1
21 | 14.38 4823.13
22 | 15.18 4754.4
23 | 15.98 4695.56
24 | 16.78 2102.26
25 | 17.58 1804.86
26 | 18.38 1491.79
27 | 19.17 1196.17
28 | 19.97 911.56
29 | 20.77 688.37
30 | 21.57 490.11
31 | 22.37 321.12
32 | 23.17 179.34
33 | 23.97 61.74
34 | 24.76 0.0
35 | ;
--------------------------------------------------------------------------------
/novus_sim_6.1/n2o_compressibility_factors.csv:
--------------------------------------------------------------------------------
1 | "Compressibility factor data for nitrous oxide at 30C, Source: Couch, E.J. et al: Volumetric Behavior of Nitrous Oxide",
2 | Pressure (Pa),z
3 | 101325,1
4 | 607950,0.9687
5 | 810600,0.9582
6 | 1013250,0.9473
7 | 1215900,0.9363
8 | 1519875,0.9191
9 | 2026500,0.8894
10 | 2533125,0.8583
11 | 3039750,0.8245
12 | 3546375,0.7889
13 | 4053000,0.7512
14 | 4559625,0.7083
15 | 5066250,0.6606
16 | 5572875,0.6038
17 | 5876850,0.5614
18 | 6079500,0.527
19 | 6282150,0.4817
20 |
--------------------------------------------------------------------------------
/novus_sim_6.1/readme.txt:
--------------------------------------------------------------------------------
1 | _ _ ____ _
2 | | \ | | _____ ___ _ ___ / ___|(_)_ __ ___
3 | | \| |/ _ \ \ / / | | / __| \___ \| | '_ ` _ \
4 | | |\ | (_) \ V /| |_| \__ \ ___) | | | | | | |
5 | |_| \_|\___/ \_/ \__,_|___/ |____/|_|_| |_| |_|
6 |
7 |
8 | V6.0
9 | Simulation program for vapour pressure fed nitrous oxide hybrid rocket motors
10 | Joe Hunt
11 |
12 | Instructions:
13 | Modify inputs at top of motor_sim.py and run using python 3.
14 | Program outputs file motor_out.csv. This is used as an input to trajectory_sim.py.
15 |
16 |
17 | Other dependencies: numpy, scipy, matplotlib
18 |
19 | Files:
20 |
21 | motor_sim.py simulation program for motor
22 | motor_out.csv motor performance output file generated by motor_sim.pt
23 | hybrid.rasp output .rasp file for RAS aero*
24 | trajectory_sim.py simuluation program for rocket trajectory
25 | hybrid_functions contains functions governing motor behaviour and material properties
26 |
27 | Additional input data:
28 | atmosphere_data.csv 1976 standard atmosphere
29 | drag_coefficient_data.csv drag coefficient to mach data (RAS Aero)
30 | L_Nitrous_S_HDPE.propep propep frozen and shifting outputs for a range of pressures and O/F ratios
31 |
32 | *Note that a perennial problem with RASP data is the rate of decrease of mass is always assumed to be proportional
33 | to thrust, which is invalid as a nitrous hybrid's ISP changes with time. Hence further use of this file introduces
34 | error from this approximation.
--------------------------------------------------------------------------------
/novus_sim_6.1/trajectory_sim.py:
--------------------------------------------------------------------------------
1 | """Simple 3DOF Martlet 4 trajectory simulator"""
2 |
3 | __copyright__ = """
4 |
5 | Copyright 2019 Joe Hunt
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with this program. If not, see .
19 |
20 | """
21 |
22 | ### Joe Hunt updated 20/06/19 ###
23 | ### All units SI unless otherwise stated ###
24 |
25 | import csv
26 | import numpy as np
27 | import matplotlib.pyplot as plt
28 |
29 | ###############################################################################
30 | # Input parameters
31 | ###############################################################################
32 |
33 | MASS_DRY = 60 # rocket dry mass (kg)
34 | DIAMETER = 0.197 # rocket body DIAMETER (m)
35 | LAUNCH_ALT = 615 # launch altitude (msl)
36 | ANGLE_RAIL = 89 # launch rail angle, degrees
37 | LENGTH_RAIL = 11 # launch rail length (m)
38 |
39 | vvel = 0 # initial rocket vertical velocity (m s^-1)
40 | hvel = 0 # initial rocket horizontal velocity (m s^-1)
41 | ground = 0 # initial down-range distance (m)
42 |
43 | ###############################################################################
44 | # Initialize simulation
45 | ###############################################################################
46 |
47 | STEP = 0.01 # time step (s)
48 |
49 | # open motor performance file (output of motor_sim.py)
50 | with open("motor_out.csv") as csvfile:
51 | motor_out = csv.reader(csvfile)
52 |
53 | (
54 | motor_time_data,
55 | prop_mass_data,
56 | cham_pres_data,
57 | throat_data,
58 | gamma_data,
59 | nozzle_efficiency_data,
60 | exit_pres_data,
61 | area_ratio_data,
62 | ) = ([], [], [], [], [], [], [], [])
63 |
64 | next(motor_out)
65 | for row in motor_out:
66 | motor_time_data.append(float(row[0]))
67 | prop_mass_data.append(float(row[1]))
68 | cham_pres_data.append(float(row[2]))
69 | throat_data.append(float(row[3]))
70 | gamma_data.append(float(row[4]))
71 | nozzle_efficiency_data.append(float(row[5]))
72 | exit_pres_data.append(float(row[6]))
73 | area_ratio_data.append(float(row[7]))
74 |
75 | # open standard atmosphere data
76 | with open("atmosphere_data.csv") as csvfile:
77 | standard_atmo_data = csv.reader(csvfile)
78 | adat, ddat, sdat, padat = [], [], [], []
79 | next(standard_atmo_data)
80 | for row in standard_atmo_data:
81 | adat.append(float(row[0]))
82 | ddat.append(float(row[1]))
83 | sdat.append(float(row[2]))
84 | padat.append(float(row[3]))
85 |
86 | # import drag coeffcient as a function of Mach data
87 | with open("drag_coefficient_data.csv") as csvfile:
88 | drag_coefficient_data = csv.reader(csvfile)
89 | machdat = []
90 | cddat = []
91 | next(drag_coefficient_data)
92 | for row in drag_coefficient_data:
93 | machdat.append(float(row[0]))
94 | cddat.append(float(row[1]))
95 |
96 | # compute state of vehicle
97 | apogee = False
98 | GRAV_ACCEL = 9.81
99 | alt = LAUNCH_ALT
100 | stable = True
101 | rail_left = False
102 | time = 0
103 | vel = np.sqrt((vvel ** 2) + (hvel ** 2))
104 | pitch = np.radians(ANGLE_RAIL)
105 | if alt < 80000:
106 | density = np.interp(alt, adat, ddat)
107 | vsound = np.interp(alt, adat, sdat)
108 | pres_static = np.interp(alt, adat, padat)
109 | else:
110 | density, vsound = 0, float("inf")
111 | mach = vel / vsound
112 |
113 | # create empty lists to fill with output data
114 | (
115 | time_data,
116 | alt_data,
117 | vel_data,
118 | acc_data,
119 | drag_data,
120 | thrust_data,
121 | mass_data,
122 | mach_data,
123 | ground_data,
124 | pitch_data,
125 | ) = ([], [], [], [], [], [], [], [], [], [])
126 |
127 |
128 | ###############################################################################
129 | # Simulation loop
130 | ###############################################################################
131 | while True:
132 | time += STEP
133 |
134 | # Update density(altitude), speed of sound(altitude), and cd(mach) from input data
135 | if alt < 80000:
136 | density = np.interp(alt, adat, ddat)
137 | vsound = np.interp(alt, adat, sdat)
138 | pres_static = np.interp(alt, adat, padat)
139 |
140 | else:
141 | density, vsound, pres_static = 0, float("inf"), 0
142 |
143 | cd = np.interp(mach, machdat, cddat)
144 |
145 | # Find current thrust
146 | if time < max(motor_time_data):
147 | pres_cham = np.interp(time, motor_time_data, cham_pres_data)
148 | dia_throat = np.interp(time, motor_time_data, throat_data)
149 | gamma = np.interp(time, motor_time_data, gamma_data)
150 | nozzle_efficiency = np.interp(time, motor_time_data, nozzle_efficiency_data)
151 | pres_exit = np.interp(time, motor_time_data, exit_pres_data)
152 | nozzle_area_ratio = np.interp(time, motor_time_data, area_ratio_data)
153 |
154 | # motor performance calculations
155 | area_throat = ((dia_throat / 2) ** 2) * np.pi
156 | thrust = (
157 | area_throat
158 | * pres_cham
159 | * (
160 | (
161 | (2 * gamma ** 2 / (gamma - 1))
162 | * ((2 / (gamma + 1)) ** ((gamma + 1) / (gamma - 1)))
163 | * (1 - (pres_exit / pres_cham) ** ((gamma - 1) / gamma))
164 | )
165 | ** 0.5
166 | )
167 | + (pres_exit - pres_static) * area_throat * nozzle_area_ratio
168 | )
169 |
170 | thrust *= nozzle_efficiency
171 | else:
172 | thrust = 0
173 |
174 | # update acceleration and integrate
175 | mass_prop = np.interp(time, motor_time_data, prop_mass_data)
176 | mass = mass_prop + MASS_DRY
177 | drag = 0.5 * cd * density * (vel ** 2) * (((DIAMETER / 2) ** 2) * np.pi)
178 | vacc = (
179 | ((thrust * np.sin(pitch)) / mass) - ((drag * np.sin(pitch)) / mass) - GRAV_ACCEL
180 | )
181 | hacc = ((thrust * np.cos(pitch)) / mass) - ((drag * np.cos(pitch)) / mass)
182 | vvel += vacc * STEP
183 | hvel += hacc * STEP
184 | vel = np.sqrt((vvel ** 2) + (hvel ** 2))
185 | acc = np.sqrt((vacc ** 2) + (hacc ** 2))
186 |
187 | if alt - LAUNCH_ALT < np.sin(pitch) * LENGTH_RAIL and apogee == False:
188 | pitch = np.radians(ANGLE_RAIL)
189 | else:
190 | pitch = np.arctan2(vvel, hvel)
191 | if rail_left == False:
192 | print("rail cleared at", vel, "m/s", "T/W:", thrust / (mass * GRAV_ACCEL))
193 | rail_left = True
194 |
195 | mach = vel / vsound
196 | alt += vvel * STEP
197 | ground += hvel * STEP
198 |
199 | # check for ground impact
200 | if alt < 0:
201 | break
202 |
203 | # update trajectory plot _data
204 | time_data.append(time)
205 | thrust_data.append(thrust)
206 | drag_data.append(-drag)
207 | alt_data.append(alt)
208 | vel_data.append(vel)
209 | acc_data.append(acc)
210 | mass_data.append(mass)
211 | mach_data.append(mach)
212 | ground_data.append(ground)
213 | pitch_data.append(np.degrees(pitch))
214 |
215 |
216 | ###############################################################################
217 | # Print and plot results
218 | ###############################################################################
219 |
220 | print(
221 | "\nResults:\napogee:",
222 | (max(alt_data) - LAUNCH_ALT) / 1000,
223 | "km\nmax Mach:",
224 | max(mach_data),
225 | )
226 |
227 | print("Gross lift off mass:", mass_data[0], "kg")
228 |
229 | plt.figure(figsize=(9, 9))
230 |
231 | plt.subplot(321)
232 | plt.plot(ground_data, [a - LAUNCH_ALT for a in alt_data])
233 | plt.xlabel("Downrange (m)")
234 | plt.ylabel("Altitude (m)")
235 | plt.ylim(0, max([a - LAUNCH_ALT for a in alt_data]) * 1.1)
236 | plt.xlim(-1000, (max(ground_data) * 1.1))
237 | plt.tight_layout()
238 | plt.gca().set_aspect("equal", adjustable="box")
239 |
240 | plt.subplot(322)
241 | plt.plot(time_data, vel_data)
242 | plt.xlabel("Time (s)")
243 | plt.ylabel("Speed (ms-1)")
244 | plt.ylim(min(vel_data) * 1.3, max(vel_data) * 1.3)
245 | plt.axhline(y=0, color="k", linestyle="-")
246 | plt.tight_layout()
247 |
248 | plt.subplot(323)
249 | plt.plot(time_data, pitch_data)
250 | plt.xlabel("Time (s)")
251 | plt.ylabel("Pitch (degrees)")
252 | plt.ylim(min(pitch_data) * 1.2, max(pitch_data) * 1.2)
253 | plt.axhline(y=0, color="k", linestyle="-")
254 | plt.tight_layout()
255 |
256 | plt.subplot(324)
257 | plt.plot(time_data, thrust_data, "r", label="thrust force")
258 | plt.plot(time_data, drag_data, "b", label="Drag force")
259 | plt.plot(time_data, [-GRAV_ACCEL * m for m in mass_data], "g", label="Weight force")
260 | plt.xlabel("Time (s)")
261 | plt.ylabel("Force (N)")
262 | plt.ylim(
263 | min(min(drag_data), min(thrust_data), min([-GRAV_ACCEL * m for m in mass_data]))
264 | * 1.2,
265 | max(max(drag_data), max(thrust_data)) * 1.2,
266 | )
267 | plt.axhline(y=0, color="k", linestyle="-")
268 | plt.legend()
269 | plt.tight_layout()
270 |
271 | plt.show()
272 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | numpy
2 | matplotlib
3 | scipy
4 | ambiance
5 | thermo
6 | gas_dynamics
7 | pandas
8 | numexpr
9 | requests
10 | getgfs>=1.0.0
11 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | """
2 | Allows installation via pip by navigating to this directory, and running "pip install ."
3 | """
4 |
5 | import sys
6 | from setuptools import setup, find_packages
7 |
8 | setup(
9 | name="CamPyRoS",
10 | version="1.1",
11 | author="Jago Strong-Wright & Daniel Gibbons",
12 | author_email="jagoosw@protonmail.com",
13 | packages=find_packages(),
14 | install_requires=[
15 | "numpy>=1.20.1",
16 | "matplotlib",
17 | "scipy",
18 | "ambiance",
19 | "thermo",
20 | "gas_dynamics",
21 | "pandas",
22 | "numexpr",
23 | "requests",
24 | "getgfs>=1.0.0"
25 | ],
26 | description="Cambridge Python Rocketry Simulator",
27 | )
28 |
--------------------------------------------------------------------------------
/stats_example.py:
--------------------------------------------------------------------------------
1 | import campyros.statistical as stats
2 |
3 | model = stats.StatisticalModel("stats_settings.json")
4 |
5 | model.run_model(test_mode=False, debug=True, num_cpus=3)
6 |
--------------------------------------------------------------------------------
/stats_settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "name":"stats_example",
3 | "itterations":10,
4 | "launch_site":{
5 | "rail_length":[10,0.01],
6 | "rail_yaw":[0,0.03],
7 | "rail_pitch":[0,0.03],
8 | "alt":[0,1],
9 | "long":[0.1160127,0.01],
10 | "lat":[52.2079404,0.01],
11 | "fast_wind":1,
12 | "launch_datetime":"20210428 15:00",
13 | "variable_wind":1
14 | },
15 | "aero_file":"data/Martlet4RasAeroII.CSV",
16 | "aero":{
17 | "COP":0.05,
18 | "CN":0.05,
19 | "CA":0.05,
20 | "ref_area":[0.0305128422,0.01],
21 | "area_per_fin":[0.07369928,0.01],
22 | "fins":4
23 | },
24 | "parachute":{
25 | "main_s":[13.9,0.05],
26 | "main_c_d":[0.78,0.05],
27 | "drogue_s":[1.13,0.05],
28 | "drogue_c_d":[0.78,0.05],
29 | "main_alt":[1000,0.05],
30 | "attatch_distance":[0,0],
31 | "failure_rate":0.01
32 | },
33 | "enviromental":{
34 | "gravity":0.01,
35 | "pressure":0.05,
36 | "density":0.05,
37 | "speed_of_sound":0.05
38 | },
39 | "motor_file":"novus_sim_6.1/motor_out.csv",
40 | "motor_pos":6.529,
41 | "thrust_error":{
42 | "magnitude":0.03,
43 | "alignment":0.0006
44 | },
45 | "mass":{
46 | "dry_mass":[60,0.01],
47 | "rocket_length":[6.529,0.01],
48 | "rocket_radius":[98.5e-3,0.01],
49 | "rocket_wall_thickness":[1e-2,0.01],
50 | "pos_tank_bottom":[4.456,0.01],
51 | "pos_solidfuel_bottom_base":[4.856,0.01],
52 | "length_port":0.01,
53 | "lden":0.01,
54 | "lmass":0.01,
55 | "smass":0.01,
56 | "sden":0.01,
57 | "vmass":0.01,
58 | "vden":0.01,
59 | "fuel_diameter":0.01
60 | }
61 | }
--------------------------------------------------------------------------------