├── .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 | [![DOI](https://zenodo.org/badge/308847422.svg)](https://zenodo.org/badge/latestdoi/308847422) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)[![Testing](https://github.com/cuspaceflight/CamPyRoS/actions/workflows/testcase.yml/badge.svg)](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 | $('\u00B6'). 193 | attr('href', '#' + this.id). 194 | attr('title', _('Permalink to this headline')). 195 | appendTo(this); 196 | }); 197 | $('dt[id]').each(function() { 198 | $('\u00B6'). 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 | $('') 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 |
180 | 188 |
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 |
245 | 246 |
247 | 248 |
249 |

250 | © Copyright 2020, Jago Strong-Wright and Daniel Gibbons. 251 | 252 |

253 |
254 | 255 | 256 | 257 | Built with Sphinx using a 258 | 259 | theme 260 | 261 | provided by Read the Docs. 262 | 263 |
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 |
179 | 180 |
181 | 182 |
183 |

184 | © Copyright 2020, Jago Strong-Wright and Daniel Gibbons. 185 | 186 |

187 |
188 | 189 | 190 | 191 | Built with Sphinx using a 192 | 193 | theme 194 | 195 | provided by Read the Docs. 196 | 197 |
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 |
163 | 164 |
165 | 166 |
167 |

168 | © Copyright 2020, Jago Strong-Wright and Daniel Gibbons. 169 | 170 |

171 |
172 | 173 | 174 | 175 | Built with Sphinx using a 176 | 177 | theme 178 | 179 | provided by Read the Docs. 180 | 181 |
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 |
176 | 177 |
178 | 179 |
180 |

181 | © Copyright 2020, Jago Strong-Wright and Daniel Gibbons. 182 | 183 |

184 |
185 | 186 | 187 | 188 | Built with Sphinx using a 189 | 190 | theme 191 | 192 | provided by Read the Docs. 193 | 194 |
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 | 196 |
197 | 198 |
199 | 200 |
201 |

202 | © Copyright 2020, Jago Strong-Wright and Daniel Gibbons. 203 | 204 |

205 |
206 | 207 | 208 | 209 | Built with Sphinx using a 210 | 211 | theme 212 | 213 | provided by Read the Docs. 214 | 215 |
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 |
169 | 170 |
171 | 172 |
173 |

174 | © Copyright 2020, Jago Strong-Wright and Daniel Gibbons. 175 | 176 |

177 |
178 | 179 | 180 | 181 | Built with Sphinx using a 182 | 183 | theme 184 | 185 | provided by Read the Docs. 186 | 187 |
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 | } --------------------------------------------------------------------------------