├── .github └── workflows │ └── CI_tests.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── archive ├── ISPKivStatus.py └── simulation │ ├── README.md │ ├── matrix_trajectory.py │ ├── trajectory_calc.py │ └── transformation_matrices.py ├── data_streaming ├── __init__.py ├── ground_server │ ├── __init__.py │ ├── find-my-ip.py │ ├── general_server.py │ ├── general_server_src_priority.py │ ├── general_server_w_email.py │ ├── multi_connection_server.py │ ├── server_testing │ │ ├── server_test_recv.py │ │ └── server_test_vector.py │ ├── simple_client.py │ └── telem_sim_and_video_stream.py └── streaming_classes.py ├── ground_station ├── GUI │ ├── 2019_GUI.py │ ├── FinalGUI.py │ ├── location.txt │ ├── matt.kv │ ├── matt.py │ └── quads.kv ├── README.md ├── ground_server │ ├── find-my-ip.py │ ├── general_server.py │ ├── general_server_src_priority.py │ ├── general_server_w_email.py │ ├── multi_connection_server.py │ ├── server_testing │ │ ├── server_test_recv.py │ │ └── server_test_vector.py │ ├── simple_client.py │ └── telem_sim_and_video_stream.py └── install_notes.txt ├── img ├── payload_diagram.jpg ├── server_diagram.jpg └── system_diagram.png ├── nav ├── .gitignore ├── NavFilter.py ├── NavMain.py ├── NavMerge.py ├── README.md ├── __init__.py ├── test │ ├── .gitignore │ ├── NavMain_test.py │ ├── NavMerge_test.py │ ├── __init__.py │ ├── frame_utils_test.py │ ├── main.py │ ├── quaternion_utils_test.py │ ├── sim_data.csv │ └── sim_data.xlsx └── utils │ ├── .gitignore │ ├── __init__.py │ ├── common_utils.py │ ├── constants.py │ ├── frame_utils.py │ └── quaternion_utils.py ├── packetizer ├── conversion.cpp ├── conversion.h ├── makefile ├── packetizer.c ├── packetizer.h └── test.c ├── raspi ├── Camera │ ├── README.md │ ├── circular_buffer_stream.py │ ├── file_subprocess.py │ └── video_stream.py ├── Camera_and_LTE │ └── LTE_stream.py ├── Flight_Version │ ├── README.md │ ├── battery_testing.py │ ├── flight_script.py │ ├── network_config.py │ ├── serial_json.py │ ├── stream_client.py │ └── telem_and_video_stream.py ├── LTE │ ├── recv_msg.py │ └── send_msg.py ├── README.md └── web_sockets │ ├── README.md │ ├── recv_client.py │ ├── recv_server.py │ ├── stream_client.py │ ├── stream_server.py │ ├── stream_video_to_google_test.py │ ├── test_client.py │ ├── test_google.py │ └── test_server.py ├── requirements.txt └── teensy ├── BMP280 └── BMP280.ino ├── BNO055 └── BNO055.ino ├── piSerial.ino └── teensy_main └── teensy_main.ino /.github/workflows/CI_tests.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: Python application 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up Python 3.8 20 | uses: actions/setup-python@v1 21 | with: 22 | python-version: 3.7 23 | - name: Install dependencies 24 | run: | 25 | python -m pip install --upgrade pip 26 | pip install -r requirements.txt 27 | - name: Lint with flake8 28 | run: | 29 | pip install flake8 30 | # stop the build if there are Python syntax errors or undefined names 31 | # flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 32 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 33 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 34 | - name: Test Nav 35 | run: | 36 | python -m nav.test.main 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/* 2 | .idea 3 | nav/design_diagram.key 4 | .DS_Store 5 | *.pyc 6 | nav/test/__pycache__/NavMain_test.cpython-37.pyc 7 | -------------------------------------------------------------------------------- /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 internspaceprogram@gmail.com. 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 Avionics 2 | 3 | ## Purpose of Standardized Contributions 4 | ISP Avionics has struggled to achieve many goals as of Fall 2019. This repo is an effort to create easily understood, used, and developed code for future tours. 5 | 6 | Standardized development allows others to easily use this repo in the future. "Get it working" is a wonderful philosophy when developing something for yourself, or when learning how something works, but contributors to this repo should take the next step of implementing a "Can others get it working?" philosophy. 7 | 8 | ## Standards 9 | 10 | ### Learn 11 | This is a learning experience, and contributors are expected to want to step outside their traditional wheelhouses. If you don't know how to tackle a problem, ask someone who does. If your code could be cleaner, ask for design advice. Don't procrastinate because you can't handle a task. Ask someone for help, or rescope your goals to match your group's skillset. This is a for-fun project, so don't stress yourself for not knowing everything right out of the gate. 12 | 13 | ### Development 14 | * **Most importantly**: Keep the file sizes manageable. If a script is doing 10 different things, split it into 10 smaller scripts and one main script that calls the rest. Keep functions to doing *one* thing. 15 | * Document your code. 16 | * Make a comment at the top of every file describing what's in it. 17 | * Make a docstring for every function describing what it does, what its parameters are and their types, and what it returns. 18 | * Name your scripts in a fashion that describes what's in it. 19 | * Don't let a directory look like this: 20 | * `script.py` 21 | * `script_v2.py` 22 | * `script_final.py` 23 | * `script_FINAL_FINAL.py` 24 | Just wait until it's not buggy, then push. If there's an issue, make a new commit. The previous versions can still be retrieved from past commits. It's not gone--that's the point of git. 25 | * Ideally, a user should only need to call a handful of functions, if not just a single one, from your capability in order to use it. Requiring users to know the nitty-gritty details of your capability in order to use it is a great way to ensure your feature never flies. 26 | 27 | ### Issues 28 | Make use of the Issues feature of git! Issues are **not** just for bugs, they're for any development ideas or questions you have about the repo. It allows you to notify others when a bug affects them, allows you to track progress in developing new features, and allows you to create development timelines. When you make an issue: 29 | * Tag it appropriately (make tags if needed). 30 | * Assign someone to it (unless it is tagged "future"). 31 | * If multiple things need to be resolved to close the issue, create a checklist in the issue description so progress can be tracked. 32 | 33 | ### Bug Fixes 34 | If you spot a bug, and can easily fix it, 35 | * Create an issue about the bug and tag it appropriately. 36 | * Fix the bug in a clone of the repo. 37 | * Test to make sure your fix doesn't cause other issues. 38 | * Push the fix to the repo. 39 | * Close the issue. 40 | 41 | This might seem tedious, and for tiny bugs this will not be necessary. However, this creates a documentation trail for how functionality of the code has changed, and will allow others to see why you've made the changes you have. 42 | 43 | ### Forks, Branches, and Pull Requests 44 | * In general, Pull Requests from Forks of the repo will not be accepted. This is to prevent the need to merge two vastly different versions of the repo together. This is not to discourage forking the repo for personal use--please do so. 45 | * If your fork has features you *really* want to see implemented, branch off the master branch, implement your changes, and make a Pull Request from that branch. 46 | * Branches are an excellent way to make sure broken code never ends up on the master branch. If you're developing a new feature or fixing a tricky bug, make a new branch, and make sure its name begins with the Issue number it is trying to resolve. Make a a Pull Request when your branch is ready to merge. 47 | * Pull Requests should: 48 | * Have at least one assigned reviewer. They should never be merged until approved by a reviewer who **did not** contribute to the changes made in the Pull Request. 49 | * Have code that is not far behind the master branch. It's a pain to handle that in the Pull Request process. Sync your branch with the master branch before making a Pull Request. 50 | 51 | ### Releases 52 | Once a version of the repo flies, denote that version of the master branch as a Release! This lets future tours know which versions flew, and allows them to see exactly where issues were and what new features to build. 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Intern Space Program 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Avionics 2 | 3 | ## What's This? 4 | Avionics is the neural system of the vehicle. On a real rocket, the avionics control the vehicle during flight, and handle all communication with the ground systems. In the Intern Space Program, the avionics software does things like estimate the vehicle's position throughout the flight, stream a live video feed from the rocket, and controls the detonation charge for the parachutes. 5 | 6 | ## How to Contribute to Avionics 7 | Current projects can be found under the Projects tab. In each project, you can find descriptive notes and related issues. All issues can be found in the Issues tab. You should look for ones tagged "Good First Issue" if you're new to programming/git, or ones related to your team's capability. When you think you've solved an issue or a few related ones, make a Pull Request and ask the avionics leads for a review. They'll give you changes to make or merge your Pull Request into the master branch. 8 | 9 | If you're new to git, and you're having trouble figuring out how to get started, feel free to ask the avionics lead for instructions. 10 | 11 | ## Design Philosophy 12 | As with all projects, avionics began with a pile of rag-tag code that barely functioned. That kind of code is really difficult for new people to understand and use, and even more difficult to build on. 13 | 14 | The design philosophy is simple: keep things bite-sized. Try to avoid thousand-line scripts. Keep things modular, split across functions and scripts, and well-documented. 15 | 16 | ## Development Process 17 | 1. Generally describe a new thing you want avionics to do. 18 | 2. Specifically describe what **kind** of data (not specific datatype) you want the new system to receive, and what it'll output. 19 | 3. Look at the existing modules. What kind of data do they output? Can you design your system to use the existing datatypes they output? If so, do so. 20 | 4. Try to design your system so that it can be used in just a few lines of code in the master script. (I.e. we don't want to call every function in your system in the master script. Have a main function in your system that does all that, and we'll call just that function from the master script.) 21 | 5. Work with the avionics lead to decide how the main avionics control script will call on your system. 22 | 6. Create issues for your system on git. Make as many as you want. Tag them appropriately, and assign yourself to them. 23 | 7. Write integration tests first. Yes, that's right: don't write the system code yet. If you can write tests for your system, then you definitely know what you want it to get and give back. 24 | 8. Program your system. Try to chunk it up into as many small functions as possible, maybe across multiple scripts if necessary. The goal is to make the code as readable as possible in bite-sized chunks. For every function you write, write a unit test or two so you can validate that it works correctly and won't crash (e.g. does it have a division-by-zero checker?). 25 | 9. Create a Pull Request for your system and request that the avionics lead review it. 26 | 10. Every flight, create a "release" of the flight version of the repo. This enables future teams to redesign anything they'd like without losing older functional versions of the code. 27 | 28 | ## Capabilities 29 | - **Sensor Suite**: Parse data produced by sensors. 30 | - **Camera**: Record live video of the flight. 31 | - **Navigation**: Estimate the vehicle's current position, velocity, and attitude. 32 | - **Telemetry Streaming**: Communication between the vehicle and the ground station. 33 | - **Ground Station**: Display the flight telemetry and video feed in a nice-looking way. 34 | 35 | See each capability's README file for more specific information. 36 | 37 | ## Hardware List Fall 2019 38 | - [Raspberry Pi 3 Model B](https://www.adafruit.com/product/3775?src=raspberrypi) 39 | - [Raspberry Pi Zero](https://www.adafruit.com/product/3708) 40 | - [Raspberry Pi Camera](https://www.amazon.com/Raspberry-Pi-Camera-Module-Megapixel/dp/B01ER2SKFS) 41 | - [Adafruit GPS Sensor](https://www.adafruit.com/product/746) 42 | - [Adafruit Temp/Press Altitude Sensor](https://www.adafruit.com/product/2651) 43 | - [Adafruit IMU](https://learn.adafruit.com/adafruit-bno055-absolute-orientation-sensor/overview) 44 | -------------------------------------------------------------------------------- /archive/ISPKivStatus.py: -------------------------------------------------------------------------------- 1 | from kivy.app import App 2 | from kivy.uix.gridlayout import GridLayout 3 | from kivy.uix.label import Label 4 | from kivy.uix.textinput import TextInput 5 | from kivy.uix.button import Button 6 | from kivy.graphics import Color 7 | from kivy.clock import Clock 8 | import random 9 | 10 | class Status(GridLayout): 11 | 12 | def __init__(self, **kwargs): 13 | instDict = {"GPS":['values',[1, 0, 0, 1]],"IMU":['values',[0, 1, 0, 1]],"ALT":['values',[0, 0, 1, 1]],"Airspeed":['values',[1, 0, 0, 1]],"LTE":['values',[1, 0, 0, 1]]} 14 | 15 | super(Status, self).__init__(**kwargs) 16 | self.cols = 1 17 | self.rows = 6 18 | self.add_widget(Label(text='STATUS')) 19 | #RED = color=[1, 0, 0, 1] 20 | #GREEN = [0, 1, 0, 1] 21 | for eachInstrument in instDict: 22 | self.add_widget(Label(text=eachInstrument + ': [' + instDict[eachInstrument][0] + ']', color=instDict[eachInstrument][1])) 23 | Clock.schedule_interval(lambda a:self.update(self), 1) 24 | 25 | def update(self,obj): 26 | obj.clear_widgets() 27 | #check to see status and store in [1] 28 | instDict = {"GPS":['values',[random.random(), 1, 0, 1]],"IMU":['values',[0, 0, 1, 1]],"ALT":['values',[0, 0, 1, 1]],"Airspeed":['values',[1, 0, 0, 1]],"LTE":['values',[1, 0, 0, 1]]} 29 | self.add_widget(Label(text='STATUS')) 30 | for eachInstrument in instDict: 31 | self.add_widget(Label(text=eachInstrument + ': ' + instDict[eachInstrument][0] + '', color=instDict[eachInstrument][1])) 32 | 33 | 34 | class ISPStatusApp(App): 35 | 36 | def build(self): 37 | return Status() 38 | 39 | 40 | if __name__ == '__main__': 41 | ISPStatusApp().run() 42 | -------------------------------------------------------------------------------- /archive/simulation/README.md: -------------------------------------------------------------------------------- 1 | # WRITE A README PLEASE :) 2 | -------------------------------------------------------------------------------- /archive/simulation/matrix_trajectory.py: -------------------------------------------------------------------------------- 1 | from math import cos 2 | from math import sin 3 | from math import sqrt 4 | import math 5 | import time 6 | start_time = time.time() 7 | import matplotlib.pyplot as plt 8 | from mpl_toolkits.mplot3d import Axes3D 9 | import numpy as np 10 | 11 | #============== MACROS =============================== 12 | 13 | #state variable 'macros' 14 | ZEROTH_ORDER = 0 15 | FIRST_ORDER = 1 16 | SECOND_ORDER = 2 17 | 18 | X = 0 19 | Y = 1 20 | Z = 2 21 | 22 | #Constant Macros 23 | PI = 3.1415926535 24 | 25 | #============== FUNCTIONS ============================= 26 | def roll(angle): 27 | print("ROLL %.2f degrees"%(angle*180/PI)) 28 | trans = np.zeros((3,3)) 29 | 30 | trans[X, X] = 1 31 | trans[X, Y] = 0 32 | trans[X, Z] = 0 33 | 34 | trans[Y, X] = 0 35 | trans[Y, Y] = cos(angle) 36 | trans[Y, Z] = sin(angle) 37 | 38 | trans[Z, X] = 0 39 | trans[Z, Y] = -sin(angle) 40 | trans[Z, Z] = cos(angle) 41 | 42 | return trans 43 | 44 | def pitch(angle): 45 | print("PITCH %.2f degrees"%(angle*180/PI)) 46 | 47 | trans = np.zeros((3,3)) 48 | 49 | trans[X, X] = cos(angle) 50 | trans[X, Y] = 0 51 | trans[X, Z] = -sin(angle) 52 | 53 | trans[Y, X] = 0 54 | trans[Y, Y] = 1 55 | trans[Y, Z] = 0 56 | 57 | trans[Z, X] = sin(angle) 58 | trans[Z, Y] = 0 59 | trans[Z, Z] = cos(angle) 60 | 61 | return trans 62 | 63 | def yaw(angle): 64 | print("YAW %.2f degrees"%(angle*180/PI)) 65 | 66 | trans = np.zeros((3,3)) 67 | 68 | trans[X, X] = cos(angle) 69 | trans[X, Y] = sin(angle) 70 | trans[X, Z] = 0 71 | 72 | trans[Y, X] = -sin(angle) 73 | trans[Y, Y] = cos(angle) 74 | trans[Y, Z] = 0 75 | 76 | trans[Z, X] = 0 77 | trans[Z, Y] = 0 78 | trans[Z, Z] = 1 79 | 80 | return trans 81 | 82 | def rotate(angle): 83 | print("ROTATE %.2f degrees"%(angle*180/PI)) 84 | 85 | trans = yaw(angle) 86 | trans = trans.T 87 | 88 | return trans 89 | 90 | def theta(angle): 91 | print("THETA %.2f degrees"%(angle*180/PI)) 92 | 93 | trans = yaw(angle) 94 | return trans 95 | 96 | def phi(angle): 97 | print("PHI %.2f degrees"%(angle*180/PI)) 98 | 99 | trans = pitch(angle) 100 | trans = trans.T 101 | 102 | return trans 103 | 104 | 105 | #============== CLASSES =============================== 106 | 107 | class model_params: 108 | 109 | def __init__(self): 110 | self.max_phi = 0.524 #radians (about 30 degrees) 111 | self.max_phi_accel = 0.1 # rad/s/time_step 112 | self.max_theta_accel = 0.1 #rad/s/time_step 113 | self.time_step = 0.01 #seconds 114 | self.gravity = 9.8 #m/s^2 115 | 116 | class rocket: 117 | 118 | def __init__(self, start_v, start_a, start_phi, start_theta): 119 | 120 | #Rocket Frame Variables 121 | self.velocity = start_v #(m/s) magnitude which always points in direction of rocket 122 | self.accel = start_a #(m/s^2) magnitude which always points in direction of rocket 123 | #rocket_pos = [0, start_v, start_a] # (m, m/s, m/s^2) 3 DOF magnitude list | 0th is flight path distance, 1st is vel magnitude, 2nd is accel magnitude 124 | self.phi = [start_phi, 0, 0] #(radians) 3 order list | 0th order phi is clockwise angle from Z axis 125 | self.theta = [start_theta, 0, 0] #(radians) 3 order list | 0th order theta is clockwise angle from X axis 126 | self.thrust = 0.0 # (Newtons) magnitude force always in direction of the rocket 127 | 128 | #Rocket model properties 129 | self.mass = 1 #(kg) 130 | self.drag_const = 0.01 #0.5*Cd*A*p all lumped together | reasonable is around 0.01 or less 131 | 132 | #Overall Inertial Frame Variables 133 | self.position = [[0,0,0],[0,0,0],[0,0,0]] # (m, m/s, m/s^2) 9DOF freedom position: 3DOF position, 3DOF velocity, 3DOF accel 134 | self.position_name = ["Position", "Velocity", "Acceleration"] 135 | self.angle = [[0,0,0],[0,0,0],[0,0,0]] # (rad, rad/s, rad/s^2) 9DOF freedom angle: 3DOF angle, 3DOF angular velocity, 3DOF angular acceleration 136 | self.angle_name = ["Angle", "Angular Velocity", "Angular Acceleration"] 137 | self.overall_time = 0.0 138 | 139 | #Flight Metrics 140 | self.max_height = 0 141 | self.max_speed = 0 142 | self.max_velocity = 0 143 | self.max_acceleration_mag = 0 144 | self.max_acceleration_DOF = 0 145 | self.flight_time = 0 146 | self.distance_from_pad = 0 147 | 148 | self.model = model_params() 149 | self.print_each_step = False -------------------------------------------------------------------------------- /archive/simulation/trajectory_calc.py: -------------------------------------------------------------------------------- 1 | #model assumes thruster in a specific location and the rocket always points toward the resultant of the velocity 2 | #thrust and drag always act in opposite direction and follow the resultant of velocity 3 | 4 | from math import cos 5 | from math import sin 6 | from math import sqrt 7 | import math 8 | import time 9 | start_time = time.time() 10 | import matplotlib.pyplot as plt 11 | from mpl_toolkits.mplot3d import Axes3D 12 | import numpy as np 13 | 14 | #============== MACROS =============================== 15 | 16 | #state variable 'macros' 17 | ZEROTH_ORDER = 0 18 | FIRST_ORDER = 1 19 | SECOND_ORDER = 2 20 | 21 | X = 0 22 | Y = 1 23 | Z = 2 24 | 25 | #Constant Macros 26 | PI = 3.1415926535 27 | 28 | 29 | #============== CLASSES =============================== 30 | 31 | class model_params: 32 | 33 | def __init__(self): 34 | self.max_phi = 0.524 #radians (about 30 degrees) 35 | self.max_phi_accel = 0.1 # rad/s/time_step 36 | self.max_theta_accel = 0.1 #rad/s/time_step 37 | self.time_step = 0.01 #seconds 38 | self.gravity = 9.8 #m/s^2 39 | 40 | class rocket: 41 | 42 | def __init__(self, start_v, start_a, start_phi, start_theta): 43 | 44 | #Rocket Frame Variables 45 | self.velocity = start_v #(m/s) magnitude which always points in direction of rocket 46 | self.accel = start_a #(m/s^2) magnitude which always points in direction of rocket 47 | #rocket_pos = [0, start_v, start_a] # (m, m/s, m/s^2) 3 DOF magnitude list | 0th is flight path distance, 1st is vel magnitude, 2nd is accel magnitude 48 | self.phi = [start_phi, 0, 0] #(radians) 3 order list | 0th order phi is clockwise angle from Z axis 49 | self.theta = [start_theta, 0, 0] #(radians) 3 order list | 0th order theta is clockwise angle from X axis 50 | self.thrust = 0.0 # (Newtons) magnitude force always in direction of the rocket 51 | 52 | #Rocket model properties 53 | self.mass = 1 #(kg) 54 | self.drag_const = 0.01 #0.5*Cd*A*p all lumped together | reasonable is around 0.01 or less 55 | 56 | #Overall Inertial Frame Variables 57 | self.position = [[0,0,0],[0,0,0],[0,0,0]] # (m, m/s, m/s^2) 9DOF freedom position: 3DOF position, 3DOF velocity, 3DOF accel 58 | self.position_name = ["Position", "Velocity", "Acceleration"] 59 | self.angle = [[0,0,0],[0,0,0],[0,0,0]] # (rad, rad/s, rad/s^2) 9DOF freedom angle: 3DOF angle, 3DOF angular velocity, 3DOF angular acceleration 60 | self.angle_name = ["Angle", "Angular Velocity", "Angular Acceleration"] 61 | self.overall_time = 0.0 62 | 63 | #Flight Metrics 64 | self.max_height = 0 65 | self.max_speed = 0 66 | self.max_velocity = 0 67 | self.max_acceleration_mag = 0 68 | self.max_acceleration_DOF = 0 69 | self.flight_time = 0 70 | self.distance_from_pad = 0 71 | 72 | self.model = model_params() 73 | self.print_each_step = False 74 | 75 | def __bool__(self): 76 | if self.position[ZEROTH_ORDER][Z] > -0.001: 77 | return True 78 | else: 79 | return False 80 | 81 | def transpose_ground_frame_to_rocket_frame(self): 82 | self.velocity = sqrt(math.pow(self.position[FIRST_ORDER][X],2)+math.pow(self.position[FIRST_ORDER][Y],2)+math.pow(self.position[FIRST_ORDER][Z],2)) 83 | self.accel = sqrt(math.pow(self.position[SECOND_ORDER][X],2)+math.pow(self.position[SECOND_ORDER][Y],2)+math.pow(self.position[SECOND_ORDER][Z],2)) 84 | 85 | for order in range(0,3): 86 | self.phi[order] = math.acos(angle[order][Z]) 87 | if (angle[order][Y] >= 0): 88 | self.theta[order] = math.acos(angle[order][X]/sin(self.phi[order])) 89 | if (angle[order][Y] < 0): 90 | self.theta[order] = 2*PI - math.acos(angle[order][X]/sin(self.phi[order])) 91 | 92 | def transpose_rocket_frame_to_ground_frame(self): 93 | 94 | for order in range(0,3): 95 | self.angle[order][Z] = cos(self.phi[order]) 96 | self.angle[order][X] = sin(self.phi[order])*cos(self.theta[order]) 97 | self.angle[order][Y] = sin(self.phi[order])*sin(self.theta[order]) 98 | 99 | for DOF in range(0,3): 100 | self.position[FIRST_ORDER][DOF] = self.velocity*self.angle[ZEROTH_ORDER][DOF] 101 | self.position[SECOND_ORDER][DOF] = self.accel*self.angle[ZEROTH_ORDER][DOF] 102 | 103 | """self.position[FIRST_ORDER][Z] = self.velocity*cos(self.phi[ZEROTH_ORDER]) 104 | self.position[FIRST_ORDER][X] = self.velocity*sin(self.phi[ZEROTH_ORDER])cos(self.theta[ZEROTH_ORDER]) 105 | self.position[FIRST_ORDER][Z] = self.velocity*sin(self.phi[ZEROTH_ORDER])sin(self.theta[ZEROTH_ORDER]) 106 | self.position[SECOND_ORDER][Z] = self.accel*cos(self.phi[ZEROTH_ORDER]) 107 | self.position[SECOND_ORDER][X] = self.accel*sin(self.phi[ZEROTH_ORDER])cos(self.theta[ZEROTH_ORDER]) 108 | self.position[SECOND_ORDER][Z] = self.accel*sin(self.phi[ZEROTH_ORDER])sin(self.theta[ZEROTH_ORDER])""" 109 | 110 | def print_position_vec(self, order): 111 | print("%s:\n\tX: %f | Y: %f | Z: %f"%(self.position_name[order], self.position[order][X], self.position[order][Y], self.position[order][Z])) 112 | 113 | def print_angle_vec(self, order): 114 | print("%s:\n\tX: %f | Y: %f | Z: %f"%(self.angle_name[order], self.angle[order][X], self.angle[order][Y], self.angle[order][Z])) 115 | 116 | def print_rocket_frame(self): 117 | print("Positional:\n\tVelocity: %f\n\tAcceleration: %f"%(self.velocity, self.accel)) 118 | print("Angular:\n\tPhi: %f | %f\n\tTheta: %f | %f"%(self.phi[ZEROTH_ORDER], (self.phi[ZEROTH_ORDER]*180/PI), self.theta[ZEROTH_ORDER], (self.theta[ZEROTH_ORDER]*180/PI))) 119 | 120 | def print_flight_metrics(self): 121 | print("Max Height: %f m"%(self.max_height)) 122 | print("Max Speed: %f m/s"%(self.max_speed)) 123 | print("Max Velocity: %f m/s"%(self.max_velocity)) 124 | print("Max Acceleration(mag): %f m/s^2"%(self.max_acceleration_mag)) 125 | print("Max Acceleration(DOF): %f m/s^2"%(self.max_acceleration_DOF)) 126 | print("\nFlight Time: %f s"%(self.flight_time)) 127 | print("Distance from Pad: %f m"%(self.distance_from_pad)) 128 | 129 | def time_step_print(self): 130 | print("Time: %f"%(self.overall_time)) 131 | for order in range(0,3): 132 | self.print_position_vec(order) 133 | self.print_rocket_frame() 134 | print("----------------------------------------------------------------------------") 135 | time.sleep(5*self.model.time_step) 136 | 137 | def update_metrics(self): 138 | 139 | self.flight_time = self.overall_time 140 | 141 | if self.velocity > self.max_speed: 142 | self.max_speed = self.velocity 143 | 144 | if self.position[ZEROTH_ORDER][Z] > self.max_height: 145 | self.max_height = self.position[ZEROTH_ORDER][Z] 146 | 147 | for DOF in range(0,3): 148 | if abs(self.position[FIRST_ORDER][DOF]) > self.max_velocity: 149 | self.max_velocity = abs(self.position[FIRST_ORDER][DOF]) 150 | if abs(self.position[SECOND_ORDER][DOF]) > self.max_acceleration_DOF: 151 | self.max_acceleration_DOF = abs(self.position[SECOND_ORDER][DOF]) 152 | 153 | accel_result = sqrt(math.pow(self.position[SECOND_ORDER][X],2)+math.pow(self.position[SECOND_ORDER][Y],2)+math.pow(self.position[SECOND_ORDER][Z],2)) 154 | if accel_result > self.max_acceleration_mag: 155 | self.max_acceleration_mag = accel_result 156 | 157 | self.distance_from_pad = sqrt(math.pow(self.position[ZEROTH_ORDER][X], 2) + math.pow(self.position[ZEROTH_ORDER][Y], 2)) 158 | 159 | def update_state(self): 160 | 161 | self.update_metrics() 162 | 163 | #update 1 Dimension acceleration for forces in direction of the rocket 164 | self.accel = (-self.drag_const*self.velocity*self.velocity + self.thrust)/self.mass 165 | 166 | #add random angle noise here 167 | #propgate that through accel and vel to position 168 | 169 | #update 3DOF angle based on phi and theta at time t 170 | for order in range(0,1): 171 | self.angle[order][Z] = cos(self.phi[order]) 172 | self.angle[order][X] = sin(self.phi[order])*cos(self.theta[order]) 173 | self.angle[order][Y] = sin(self.phi[order])*sin(self.theta[order]) 174 | 175 | #break acceleration magnitude into 3 DOF accel and add 3DOF forces/accelerations 176 | for DOF in range(0,3): 177 | self.position[SECOND_ORDER][DOF] = self.accel*self.angle[ZEROTH_ORDER][DOF] 178 | self.position[SECOND_ORDER][Z] -= self.model.gravity 179 | 180 | #Acceleration(t) and velocity(t) update position(t+1) 181 | #Acceleration(t) updates velocity(t+1) 182 | for DOF in range(0,3): 183 | self.position[ZEROTH_ORDER][DOF] += self.position[FIRST_ORDER][DOF]*self.model.time_step + 0.5*self.position[SECOND_ORDER][DOF]*self.model.time_step*self.model.time_step 184 | self.position[FIRST_ORDER][DOF] += self.position[SECOND_ORDER][DOF]*self.model.time_step 185 | 186 | #Get Velocity(t+1) magnitude 187 | self.velocity = sqrt(math.pow(self.position[FIRST_ORDER][X],2)+math.pow(self.position[FIRST_ORDER][Y],2)+math.pow(self.position[FIRST_ORDER][Z],2)) 188 | 189 | #Update phi(t+1) and theta(t+1) based on velocity(t+1) resultant 190 | temp_theta = math.atan((self.position[FIRST_ORDER][Y]/(self.position[FIRST_ORDER][X]+0.0000001))) 191 | if (self.position[FIRST_ORDER][X] < 0): 192 | #Quadrant II and III 193 | temp_theta = PI + temp_theta 194 | if (self.position[FIRST_ORDER][X] >= 0): 195 | #Quadrant I and IV 196 | temp_theta = 2*PI + temp_theta 197 | self.theta[ZEROTH_ORDER] = temp_theta 198 | 199 | self.phi[ZEROTH_ORDER] = math.acos((self.position[FIRST_ORDER][Z]/(self.velocity+0.000001))) 200 | 201 | self.overall_time += self.model.time_step 202 | 203 | if self.print_each_step: 204 | self.time_step_print() 205 | 206 | test_rocket = rocket(9.8, 0, 0.01, 0.0) 207 | test_rocket.transpose_rocket_frame_to_ground_frame() 208 | list_x = [] 209 | list_y = [] 210 | list_z = [] 211 | while (test_rocket): 212 | list_x.append(test_rocket.position[ZEROTH_ORDER][X]) 213 | list_y.append(test_rocket.position[ZEROTH_ORDER][Y]) 214 | list_z.append(test_rocket.position[ZEROTH_ORDER][Z]) 215 | 216 | if (test_rocket.overall_time < 2): 217 | test_rocket.thrust = 80.0 218 | else: 219 | test_rocket.thrust = 0.0 220 | test_rocket.update_state() 221 | 222 | print("Simulation Ended") 223 | test_rocket.print_flight_metrics() 224 | print("PROGRAM TIME: %f s"%(time.time()-start_time)) -------------------------------------------------------------------------------- /archive/simulation/transformation_matrices.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from math import cos 3 | from math import sin 4 | import matplotlib.pyplot as plt 5 | from mpl_toolkits.mplot3d import Axes3D 6 | 7 | 8 | X = 0 9 | Y = 1 10 | Z = 2 11 | PI = 3.1415926535 12 | 13 | def roll(angle): 14 | print("ROLL %.2f degrees"%(angle*180/PI)) 15 | trans = np.array([[1, 0, 0],[0,cos(angle),-sin(angle)],[0,sin(angle),cos(angle)]]) 16 | print(trans) 17 | return trans 18 | 19 | def pitch(angle): 20 | print("PITCH %.2f degrees"%(angle*180/PI)) 21 | trans = np.array([[cos(angle), 0, sin(angle)],[0,1,0],[-sin(angle),0,cos(angle)]]) 22 | print(trans) 23 | return trans 24 | 25 | def yaw(angle): 26 | print("YAW %.2f degrees"%(angle*180/PI)) 27 | trans = np.array([[cos(angle),-sin(angle), 0],[sin(angle),cos(angle),0],[0,0,1]]) 28 | print(trans) 29 | return trans 30 | 31 | def clean_matrix(matrix_in): 32 | mat_out = matrix_in 33 | rows = np.size(matrix_in, 0) 34 | cols = np.size(matrix_in, 1) 35 | 36 | for r in range(0,rows): 37 | for c in range(0,cols): 38 | mat_out[r,c] = round(matrix_in[r,c], 3) 39 | return mat_out 40 | 41 | def rotate_around_normal(angle, normal_vec): 42 | c = cos(angle) 43 | s = sin(angle) 44 | C = 1-c 45 | 46 | x = normal_vec[0] 47 | y = normal_vec[1] 48 | z = normal_vec[2] 49 | 50 | row1 = [x*x*C+c, x*y*C-z*s, x*z*C+y*s] 51 | row2 = [y*x*C+z*s, y*y*C+c, y*z*C-x*s] 52 | row3 = [z*x*C-y*s, z*y*C+x*s, z*z*C+c] 53 | 54 | trans = np.array([row1, row2, row3]) 55 | print (clean_matrix(trans)) 56 | return trans 57 | def rotate(angle, ground_to_rocket): 58 | trans = rotate_around_normal(angle, ground_to_rocket[:,Z]) 59 | return trans 60 | 61 | def theta(angle, ground_to_rocket): 62 | trans = yaw(angle) 63 | return trans 64 | 65 | def phi(angle, ground_to_rocket): 66 | a = ground_to_rocket[0,Y] 67 | b = ground_to_rocket[1,Y] 68 | if (abs(a) < 0.0000001 and abs(b) < 0.0000001): 69 | norm = np.array([0,1,0]) 70 | else: 71 | norm = np.array([a, b, 0]) #Rocket Z vector crossed with ground Z vector 72 | norm = norm/np.linalg.norm(norm) 73 | trans = rotate_around_normal(angle, norm) 74 | return trans 75 | 76 | #def calculate_rocket_angles(r_to_g_trans): 77 | 78 | 79 | gravity = np.array([0,0,-1]) 80 | gravity = gravity.T 81 | thrust = np.array([0,0,1]) 82 | thrust = thrust.T 83 | drag = np.array([0,0,-1]) 84 | drag = drag.T 85 | 86 | static_forces = [gravity] 87 | dynamic_forces = [thrust, drag] 88 | 89 | ground_to_rocket = np.eye(3) 90 | rocket_to_ground = np.eye(3) 91 | 92 | rocket_angle_rate = np.array([0, -1, 1]) 93 | while True: 94 | print 95 | cmd = input("Enter the angle manipulation: ") 96 | elements = cmd.split(" ") 97 | action = elements[0] 98 | angle = float(elements[1]) 99 | angle = angle*PI/180 100 | transformation = np.eye(3) 101 | good_cmd = True 102 | if action == 'r': 103 | transformation = rotate(angle, ground_to_rocket) 104 | elif action == 't': 105 | transformation = theta(angle, ground_to_rocket) 106 | elif action == 'p': 107 | transformation = phi(angle, ground_to_rocket) 108 | else: 109 | print("Bad command; Interpreted as:\n\tAction: %s\n\tAngle: %f"%(action, angle)) 110 | good_cmd = False 111 | if (good_cmd): 112 | ground_to_rocket = np.dot(transformation, ground_to_rocket) 113 | rocket_to_ground = np.dot(rocket_to_ground, transformation.T) 114 | 115 | print("Ground-to-Rocket Matrix (Z is direction of Rocket)") 116 | print(clean_matrix(ground_to_rocket)) 117 | print("Rocket-to-Ground Matrix (Inverse to Rocket)") 118 | print(clean_matrix(rocket_to_ground)) 119 | 120 | print("G-R and R-G multiplied") 121 | print(clean_matrix(np.dot(ground_to_rocket, rocket_to_ground))) 122 | 123 | forces = np.array([0,0,0]) 124 | forces = forces.T 125 | thrust = ground_to_rocket[:,2] 126 | print(thrust) 127 | drag = -ground_to_rocket[:,2] 128 | print(drag) 129 | print(gravity) 130 | forces = np.add(thrust, drag) 131 | forces = np.add(forces, gravity) 132 | print(forces) 133 | 134 | IMU_reading = np.dot(ground_to_rocket, forces) 135 | print("IMU reading with static and dynamic forces") 136 | print(IMU_reading) 137 | 138 | 139 | x_list = [] 140 | y_list = [] 141 | z_list = [] 142 | x_test = [] 143 | y_test = [] 144 | z_test = [] 145 | forces_x_list = [] 146 | forces_y_list = [] 147 | forces_z_list = [] 148 | 149 | res = 10 150 | for i in range(1,res+1): 151 | forces_x_list.append(i*thrust[0]*0.9/res) 152 | forces_y_list.append(i*thrust[1]*0.9/res) 153 | forces_z_list.append(i*thrust[2]*0.9/res) 154 | 155 | forces_x_list.append(i*drag[0]*0.9/res) 156 | forces_y_list.append(i*drag[1]*0.9/res) 157 | forces_z_list.append(i*drag[2]*0.9/res) 158 | 159 | forces_x_list.append(i*gravity[0]*0.9/res) 160 | forces_y_list.append(i*gravity[1]*0.9/res) 161 | forces_z_list.append(i*gravity[2]*0.9/res) 162 | 163 | for axe in range(0,3): 164 | x_list.append(i*ground_to_rocket[0,axe]/res) 165 | y_list.append(i*ground_to_rocket[1,axe]/res) 166 | z_list.append(i*ground_to_rocket[2,axe]/res) 167 | 168 | if axe == 0: 169 | x_test.append(i*0.95/res) 170 | y_test.append(0) 171 | z_test.append(0) 172 | if axe == 1: 173 | x_test.append(0) 174 | y_test.append(i*0.95/res) 175 | z_test.append(0) 176 | if axe == 2: 177 | x_test.append(0) 178 | y_test.append(0) 179 | z_test.append(i*0.95/res) 180 | 181 | fig = plt.figure() 182 | ax = fig.add_subplot(111, projection='3d') 183 | ax.scatter(x_list, y_list, z_list, c='r', marker='o') 184 | ax.scatter(x_test, y_test, z_test, c='b', marker='o') 185 | ax.scatter(forces_x_list, forces_y_list, forces_z_list, c='g', marker='o') 186 | ax.set_xlabel('X (m)') 187 | ax.set_ylabel('Y (m)') 188 | ax.set_zlabel('Z (m)') 189 | ax.set_xlim3d(-1.0, 1.0) 190 | ax.set_ylim3d(-1.0, 1.0) 191 | ax.set_zlim3d(-1.0, 1.0) 192 | plt.show() -------------------------------------------------------------------------------- /data_streaming/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data_streaming/ground_server/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data_streaming/ground_server/find-my-ip.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import time 3 | 4 | interval = 5 5 | 6 | while True: 7 | ip = subprocess.Popen(['curl', 'ifconfig.me'], stdout=subprocess.PIPE).communicate()[0] 8 | f = open('testip.txt', 'a') 9 | f.truncate(0) 10 | f.write(ip.decode('utf-8') + '\n') 11 | f.close() 12 | time.sleep(interval * 60) 13 | -------------------------------------------------------------------------------- /data_streaming/ground_server/multi_connection_server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import selectors 3 | from io import BytesIO 4 | import types 5 | import struct 6 | from pathlib import Path 7 | import os 8 | 9 | home = str(Path.home()) 10 | print("Home Directory: %s"%(home)) 11 | store_dir = home + "/rocket_data" 12 | cmd = "mkdir " + store_dir 13 | os.system(cmd) 14 | 15 | SERVER_IP = '' 16 | SERVER_VID_PORT = 5000 17 | SERVER_TELEM_PORT = 5001 18 | 19 | video_ports = [SERVER_VID_PORT] 20 | telem_ports = [SERVER_TELEM_PORT] 21 | 22 | def name_source(socket_obj): 23 | port = socket_obj.getsockname()[1] 24 | named = False 25 | for ports in video_ports: 26 | if port == ports: 27 | named = True 28 | return 'VIDEO' 29 | for ports in telem_ports: 30 | if port == ports: 31 | named = True 32 | return 'TELEMETRY' 33 | if (not(named)): 34 | return False 35 | 36 | class data_point: 37 | def __init__(self, packet_num, time_stamp, status_list, pos_list, vel_list, orientation_list): 38 | self.packet_num = packet_num 39 | self.time_stamp = time_stamp 40 | self.status = status_list #[imu, gps, alt, teensy, raspi, LTE, serial] 41 | self.position = pos_list #[X, Y, Z] 42 | self.velocity = vel_list #[X, Y, Z] 43 | self.orientation = orientation_list #[Roll, Pitch, Yaw] 44 | 45 | def print_data_point(self): 46 | print("Data Point:") 47 | print("\tPacket Number: %d"%(self.packet_num)) 48 | print("\tTime Stamp: %d"%(self.time_stamp)) 49 | print("\t IMU GPS ALT Teensy Raspi LTE Serial") 50 | print("\tStatus: %d %d %d %d %d %d %d"%(self.status[0], self.status[1], self.status[2], self.status[3], self.status[4], self.status[5], self.status[6])) 51 | print("\t X Y Z") 52 | print("\tPosition: %.2f %.2f %.2f"%(self.position[0], self.position[1], self.position[2])) 53 | print("\t X Y Z") 54 | print("\tVelocity: %.2f %.2f %.2f"%(self.velocity[0], self.velocity[1], self.velocity[2])) 55 | print("\t X Y Z") 56 | print("\tOrientat: %.2f %.2f %.2f"%(self.orientation[0], self.orientation[1], self.orientation[2])) 57 | 58 | class stream: 59 | def __init__(self, name, server_socket, client_socket, store_file): 60 | self.name = name 61 | self.server_socket = server_socket 62 | self.client_socket = client_socket 63 | self.packet_cnt = 0 64 | self.total_bytes = 0 65 | self.store_file = store_file 66 | self.file_handle = open(store_file, 'wb') 67 | self.buffer = BytesIO() 68 | self.alive = True 69 | 70 | def __bool__(self): 71 | return self.alive 72 | 73 | def store_buffer(self): 74 | print("%s: Storing Buffer"%(self.name)) 75 | self.file_handle.write(self.buffer.getvalue()) 76 | 77 | def get_buffer_size(self): 78 | return self.buffer.getbuffer().nbytes 79 | 80 | def clear_buffer(self): 81 | print("%s: Clearing Buffer"%(self.name)) 82 | self.buffer.truncate(0) 83 | self.buffer.seek(0) 84 | 85 | def close(self): 86 | self.store_buffer() 87 | self.clear_buffer() 88 | self.file_handle.close() 89 | self.server_socket.close() 90 | 91 | def recv_new_packet(self): 92 | packet = self.client_socket.recv(4096) 93 | if (not(packet)): 94 | print("%s: Stream ended, storing, then closing connection and file"%(self.name)) 95 | self.alive = False 96 | self.close() 97 | return 98 | print("%s: New Packet | Size: %d Bytes"%(self.name, len(packet))) 99 | self.buffer.write(packet) 100 | self.packet_cnt += 1 101 | self.total_bytes += len(packet) 102 | 103 | 104 | def parse_telemetry(telem_packets, data_point_buffer): 105 | code_word = bytearray([192,222]) #0xC0DE (Beginning of Packet) 106 | EOP = bytearray([237,12]) #0xED0C (End of Packet) 107 | packet_list = telem_packets.split(code_word) 108 | for packets in packet_list: 109 | good_packet = False 110 | try: 111 | data = struct.unpack('>ii???????fffffffffh', packets) 112 | if packets[51:53] == EOP: 113 | good_packet = True 114 | else: 115 | print("End of Packet check failed") 116 | print("\tPacket End: %s"%(packets[51:53])) 117 | print("\tEOP: %s"%(EOP)) 118 | except: 119 | print("BAD PACKET") 120 | 121 | if (good_packet): 122 | status_list = [data[2], data[3], data[4], data[5], data[6], data[7], data[8]] 123 | pos_list = [data[9], data[10], data[11]] 124 | vel_list = [data[12], data[13], data[14]] 125 | orient_list = [data[15], data[16], data[17]] 126 | new_data = data_point(data[0], data[1], status_list, pos_list, vel_list, orient_list) 127 | new_data.print_data_point() 128 | data_point_buffer.append(new_data) 129 | 130 | return data_point_buffer 131 | 132 | 133 | server_vid_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 134 | server_vid_sock.bind((SERVER_IP, SERVER_VID_PORT)) 135 | server_vid_sock.listen() 136 | print("%s Server Created and Listening on (%s, %d)"%(name_source(server_vid_sock), server_vid_sock.getsockname()[0], server_vid_sock.getsockname()[1])) 137 | 138 | 139 | server_telem_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 140 | server_telem_sock.bind((SERVER_IP, SERVER_TELEM_PORT)) 141 | server_telem_sock.listen() 142 | print("%s Server Created and Listening on (%s, %d)"%(name_source(server_telem_sock), server_telem_sock.getsockname()[0], server_telem_sock.getsockname()[1])) 143 | 144 | sel = selectors.DefaultSelector() 145 | sel.register(server_vid_sock, selectors.EVENT_READ, data=None) 146 | sel.register(server_telem_sock, selectors.EVENT_READ, data=None) 147 | 148 | video_file = store_dir + '/video_stream_recording.h264' 149 | telem_file = store_dir + '/telemetry_stream.txt' 150 | 151 | 152 | #Wait for Video connection (First one to Initiate) 153 | vid_client, addr = server_vid_sock.accept() 154 | print("VIDEO Stream connected from (%s, %d)"%(addr[0], addr[1])) 155 | vid_client.setblocking(False) 156 | video_ports.append(addr[1]) 157 | data = types.SimpleNamespace(addr=addr, inb=b'', outb = b'') 158 | events = selectors.EVENT_READ | selectors.EVENT_WRITE 159 | sel.register(vid_client,events, data=data) 160 | 161 | 162 | #Wait for Telemetry connection(Second one to Initiate) 163 | telem_client, addr = server_telem_sock.accept() 164 | print("TELEMETRY Stream connected from (%s, %d)"%(addr[0], addr[1])) 165 | telem_client.setblocking(False) 166 | telem_ports.append(addr[1]) 167 | data = types.SimpleNamespace(addr=addr, inb=b'', outb = b'') 168 | events = selectors.EVENT_READ | selectors.EVENT_WRITE 169 | sel.register(telem_client, events, data=data) 170 | 171 | #Now that connection order doesn't matter, set blocking to false 172 | server_vid_sock.setblocking(False) 173 | server_telem_sock.setblocking(False) 174 | 175 | #Create stream objects 176 | video_stream = stream('VIDEO', server_vid_sock, vid_client, video_file) 177 | telem_stream = stream('TELEMETRY', server_telem_sock, telem_client, telem_file) 178 | 179 | data_point_buffer = [] 180 | 181 | while video_stream or telem_stream: 182 | events = sel.select(timeout=0.1)#BLOCKING, can set timeout to not block 183 | for key, mask in events: 184 | if key.data is None: 185 | print("CONNECTION ATTEMPT") 186 | if key.data is not(None) and mask == selectors.EVENT_READ | selectors.EVENT_WRITE: 187 | socket_obj = key.fileobj 188 | if (name_source(socket_obj) == 'VIDEO' and video_stream): 189 | video_stream.recv_new_packet() 190 | if video_stream.get_buffer_size() > 10000: 191 | #Buffer Reached Threshold 192 | 193 | #Process Buffer data 194 | 195 | #Store Buffer to file 196 | video_stream.store_buffer() 197 | 198 | #Clear Buffer 199 | video_stream.clear_buffer() 200 | 201 | if (name_source(socket_obj) == 'TELEMETRY' and telem_stream): 202 | telem_stream.recv_new_packet() 203 | if telem_stream.get_buffer_size() > 500: 204 | #Buffer Reached Threshold 205 | 206 | #Process Buffer data 207 | data_point_buffer = parse_telemetry(telem_stream.buffer.getvalue(), data_point_buffer) 208 | 209 | #Store Buffer to file 210 | telem_stream.store_buffer() 211 | 212 | #Clear Buffer 213 | telem_stream.clear_buffer() 214 | 215 | print("Stream Ended") 216 | print("Stream Statistics: \n\tVIDEO Bytes: %d\n\tTELEM Bytes: %d"%(video_stream.total_bytes, telem_stream.total_bytes)) 217 | 218 | 219 | 220 | 221 | -------------------------------------------------------------------------------- /data_streaming/ground_server/server_testing/server_test_recv.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import selectors 3 | import threading 4 | import os 5 | import sys 6 | import subprocess 7 | 8 | vid_sock_alive = True 9 | telem_sock_alive = True 10 | 11 | def get_ip_addrs(): 12 | 13 | get_ip = subprocess.Popen(['ifconfig'], stdout=subprocess.PIPE) 14 | 15 | lines = get_ip.communicate()[0].split(b'\n') 16 | interface = b'' 17 | ip_addresses = [] #list of tuples containing interface and IPv4 for that interface 18 | 19 | for line in lines: 20 | if line.find(b' ') > 0: 21 | interface = line.split(b':')[0].decode('utf-8') 22 | 23 | if line.find(b'inet ') > -1: 24 | ip_address = line.split(b' ')[9].decode('utf-8') 25 | ip_addresses.append((interface, ip_address)) 26 | return ip_addresses 27 | 28 | ip_addresses = get_ip_addrs() 29 | 30 | def find_ip_for_interface(interface): 31 | for addr in ip_addresses: 32 | if addr[0].find(interface) != -1: 33 | return addr[1] 34 | return None 35 | 36 | def print_wireless_interfaces(): 37 | print("List of Active Wireless Interfaces") 38 | for addr in ip_addresses: 39 | print("\t%s: %s"%(addr[0], addr[1])) 40 | 41 | SERVER_IP = '73.115.48.151' 42 | SERVER_VIDEO_PORT = 5000 43 | SERVER_TELEM_PORT = 5001 44 | 45 | print_wireless_interfaces() 46 | 47 | while True: 48 | ip_address = input("Enter the local/global ip address or interface name: ") 49 | if ip_address.find('local') != -1: 50 | SERVER_IP = '127.0.0.1' 51 | elif (ip_address.find('wlo') != -1 or ip_address.find('wlan') != -1): 52 | SERVER_IP = find_ip_for_interface('wlo') 53 | if SERVER_IP is None: 54 | SERVER_IP = find_ip_for_interface('wlan') 55 | if SERVER_IP is None: 56 | print("Wireless Local interface not found -> it's either down or named differently") 57 | print_wireless_interfaces() 58 | elif find_ip_for_interface(ip_address) is not None: 59 | print("IP found by interface") 60 | SERVER_IP = find_ip_for_interface(ip_address) 61 | elif ip_address.find("serv") != -1: 62 | SERVER_IP = '73.115.48.151' 63 | else: 64 | SERVER_IP = ip_address 65 | 66 | try: 67 | video_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 68 | video_sock.connect((SERVER_IP, SERVER_VIDEO_PORT)) 69 | print("VIDEO SOCKET CONNECTED") 70 | 71 | telem_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 72 | telem_sock.connect((SERVER_IP, SERVER_TELEM_PORT)) 73 | print("TELEMETRY SOCKET CONNECTED") 74 | break 75 | except: 76 | print("Unable to connect. Check ip or interface name") 77 | 78 | print("Telem and Video Ports connected to server. Registering as SINKS") 79 | 80 | telem_sock.sendall(b'sink') 81 | video_sock.sendall(b'sink') 82 | 83 | print("Registered as sinks!") 84 | 85 | telem_bytes = 0 86 | vid_bytes = 0 87 | 88 | sel = selectors.DefaultSelector() 89 | sel.register(video_sock, selectors.EVENT_READ|selectors.EVENT_WRITE, data = 'VIDEO') 90 | sel.register(telem_sock, selectors.EVENT_READ|selectors.EVENT_WRITE, data = 'TELEMETRY') 91 | 92 | while vid_sock_alive or telem_sock_alive: 93 | events = sel.select(timeout=None)#BLOCKING, can set timeout to not block 94 | for key, mask in events: 95 | socket_obj = key.fileobj 96 | if key.data is not(None) and mask == selectors.EVENT_READ|selectors.EVENT_WRITE: 97 | if key.data == 'VIDEO': 98 | if vid_sock_alive: 99 | new_data = socket_obj.recv(4096) 100 | if not(new_data): 101 | print("%s: Pipe Broken, closing socket"%(key.data)) 102 | sel.unregister(socket_obj) 103 | socket_obj.close() 104 | vid_sock_alive = False 105 | 106 | else: 107 | search = "KILL STREAM" 108 | if new_data.find(search.encode('utf-8')) != -1: 109 | print("%s: KILL SWITCH RECIEVED -> CLOSING SOCKET"%(key.data)) 110 | sel.unregister(socket_obj) 111 | socket_obj.close() 112 | vid_sock_alive = False 113 | else: 114 | print("%s: %s"%(key.data, new_data)) 115 | vid_bytes += len(new_data) 116 | 117 | 118 | else: 119 | sel.unregister(socket_obj) 120 | socket_obj.close() 121 | 122 | if key.data == 'TELEMETRY': 123 | if telem_sock_alive: 124 | new_data = socket_obj.recv(4096) 125 | if not(new_data): 126 | print("%s: Pipe Broken, closing socket"%(key.data)) 127 | sel.unregister(socket_obj) 128 | socket_obj.close() 129 | telem_sock_alive = False 130 | 131 | 132 | else: 133 | search = "KILL STREAM" 134 | if new_data.find(search.encode('utf-8')) != -1: 135 | print("%s: KILL SWITCH RECIEVED -> CLOSING SOCKET"%(key.data)) 136 | sel.unregister(socket_obj) 137 | socket_obj.close() 138 | telem_sock_alive = False 139 | else: 140 | print("%s: %s"%(key.data, new_data)) 141 | telem_bytes += len(new_data) 142 | 143 | 144 | else: 145 | sel.unregister(socket_obj) 146 | socket_obj.close() 147 | print("Ending Program") 148 | print("VIDEO Recieved Bytes: %d"%(vid_bytes)) 149 | print("TELEMETRY Recieved Bytes: %d"%(telem_bytes)) 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /data_streaming/ground_server/server_testing/server_test_vector.py: -------------------------------------------------------------------------------- 1 | import socket 2 | from random import randint 3 | import time 4 | import subprocess 5 | 6 | get_ip = subprocess.Popen(['ifconfig'], stdout=subprocess.PIPE) 7 | 8 | lines = get_ip.communicate()[0].split(b'\n') 9 | interface = b'' 10 | ip_addresses = [] #list of tuples containing interface and IPv4 for that interface 11 | 12 | for line in lines: 13 | if line.find(b' ') > 0: 14 | interface = line.split(b':')[0].decode('utf-8') 15 | 16 | if line.find(b'inet ') > -1: 17 | ip_address = line.split(b' ')[9].decode('utf-8') 18 | ip_addresses.append((interface, ip_address)) 19 | 20 | def find_ip_for_interface(interface): 21 | for addr in ip_addresses: 22 | if addr[0].find(interface) != -1: 23 | return addr[1] 24 | return None 25 | 26 | def print_wireless_interfaces(): 27 | print("List of Active Wireless Interfaces") 28 | for addr in ip_addresses: 29 | print("\t%s: %s"%(addr[0], addr[1])) 30 | 31 | class stream_metrics: 32 | def __init__(self, name): 33 | self.name = name 34 | self.time_sum = 0.0 35 | self.max_time = 0.0 36 | self.min_time = 10000.0 37 | self.total_bytes_sent = 0 38 | 39 | def update_metrics(self, bytes_sent, single_time): 40 | self.time_sum += single_time 41 | self.total_bytes_sent += bytes_sent 42 | if single_time < self.min_time: 43 | self.min_time = single_time 44 | if single_time > self.max_time: 45 | self.max_time = single_time 46 | 47 | def print_metrics(self): 48 | print("%s Stream"%(self.name)) 49 | print("%d Bytes sent in %.5f seconds"%(self.total_bytes_sent, self.time_sum)) 50 | print("\tMAX Send Time: %f"%(self.max_time)) 51 | print("\tMIN Send Time: %f"%(self.min_time)) 52 | print("\tAverage Bit Rate (kbps): %d"%(int(self.total_bytes_sent*8/(self.time_sum*1000)))) 53 | 54 | SERVER_IP = '73.115.48.151' 55 | SERVER_VIDEO_PORT = 5000 56 | SERVER_TELEM_PORT = 5001 57 | 58 | print_wireless_interfaces() 59 | 60 | while True: 61 | ip_address = input("Enter the local/global ip address or interface name: ") 62 | if ip_address.find('local') != -1: 63 | SERVER_IP = '127.0.0.1' 64 | elif (ip_address.find('wlo') != -1 or ip_address.find('wlan') != -1): 65 | SERVER_IP = find_ip_for_interface('wlo') 66 | if SERVER_IP is None: 67 | SERVER_IP = find_ip_for_interface('wlan') 68 | if SERVER_IP is None: 69 | print("Wireless Local interface not found -> it's either down or named differently") 70 | print("Here is a list of active interfaces") 71 | for addr in ip_addresses: 72 | print("\t",addr[0]) 73 | elif find_ip_for_interface(ip_address) is not None: 74 | print("IP found by interface") 75 | SERVER_IP = find_ip_for_interface(ip_address) 76 | elif ip_address.find("serv") != -1: 77 | SERVER_IP = '73.115.48.151' 78 | else: 79 | SERVER_IP = ip_address 80 | 81 | try: 82 | video_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 83 | video_sock.connect((SERVER_IP, SERVER_VIDEO_PORT)) 84 | print("VIDEO SOCKET CONNECTED") 85 | 86 | telem_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 87 | telem_sock.connect((SERVER_IP, SERVER_TELEM_PORT)) 88 | print("TELEMETRY SOCKET CONNECTED") 89 | break 90 | except: 91 | print("Unable to connect. Check ip or interface name") 92 | 93 | print("Telem and Video Ports connected to server. Registering as Source") 94 | 95 | telem_sock.sendall(b'src') 96 | video_sock.sendall(b'src') 97 | 98 | #Test Packet Creation 99 | test_list = [] 100 | for j in range(0, 2): 101 | for i in range(0, 255): 102 | test_list.append(i) 103 | 104 | telem_packet = bytearray(test_list) 105 | 106 | test_list = [] 107 | for j in range(0, 23): 108 | for i in range(0, 255): 109 | test_list.append(i) 110 | video_packet = bytearray(test_list) 111 | 112 | #Network Metric Variables 113 | telem_metrics = stream_metrics("TELEMETRY") 114 | video_metrics = stream_metrics("VIDEO") 115 | 116 | 117 | packet_cnt = 0 118 | program_start = time.time() 119 | 120 | while packet_cnt < 100: 121 | packet_cnt += 1 122 | 123 | print("Sending packets %d"%packet_cnt) 124 | telem_start = time.time() 125 | telem_sock.sendall(telem_packet) 126 | telem_time = time.time()-telem_start 127 | telem_metrics.update_metrics(len(telem_packet), telem_time) 128 | time.sleep(0.05) 129 | 130 | video_start = time.time() 131 | video_sock.sendall(video_packet) 132 | video_time = time.time()-video_start 133 | video_metrics.update_metrics(len(video_packet), video_time) 134 | time.sleep(0.05) 135 | 136 | program_time = time.time() - program_start 137 | time.sleep(1.0) 138 | telem_sock.sendall(b'KILL STREAM') 139 | video_sock.sendall(b'KILL STREAM') 140 | 141 | print("Stream Ended") 142 | print("Full program time: %f"%(program_time)) 143 | print("Program Usage:") 144 | print("\tTelemetry Stream: %.2f%%"%(telem_metrics.time_sum*100/program_time)) 145 | print("\tVideo Stream: %.2f%%"%(video_metrics.time_sum*100/program_time)) 146 | 147 | telem_metrics.print_metrics() 148 | video_metrics.print_metrics() 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /data_streaming/ground_server/simple_client.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import selectors 3 | import threading 4 | import os 5 | import sys 6 | 7 | telem_sock_alive = True 8 | vid_sock_alive = True 9 | 10 | def get_user_input(vid_sock, telem_sock): 11 | global telem_sock_alive 12 | global vid_sock_alive 13 | while True: 14 | new_input = input("") 15 | if new_input.find("vid") != -1: 16 | new_input = new_input.replace("vid", "") 17 | if new_input.find("kill") != -1: 18 | vid_sock.sendall(b'KILL STREAM') 19 | print("Kill statement sent") 20 | else: 21 | try: 22 | vid_sock.sendall(new_input.encode('utf-8')) 23 | print("Message sent on VIDEO socket") 24 | except: 25 | vid_sock_alive = False 26 | vid_sock.close() 27 | 28 | elif new_input.find("telem") != -1: 29 | new_input = new_input.replace("telem", "") 30 | if new_input.find("kill") != -1: 31 | telem_sock.sendall(b'KILL STREAM') 32 | print("Kill statement sent") 33 | else: 34 | try: 35 | telem_sock.sendall(new_input.encode('utf-8')) 36 | print("Message sent on TELEMETRY socket") 37 | except: 38 | telem_sock_alive = False 39 | telem_sock.close() 40 | else: 41 | print("Invalid Command: please include 'vid' or 'telem' in message") 42 | 43 | 44 | SERVER_IP = '10.0.0.178' 45 | SERVER_VIDEO_PORT = 5000 46 | SERVER_TELEM_PORT = 5001 47 | 48 | while True: 49 | ip_address = input("Enter the local or global ip address: ") 50 | SERVER_IP = ip_address 51 | try: 52 | client_vid_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 53 | client_vid_sock.connect_ex((SERVER_IP, SERVER_VIDEO_PORT)) 54 | client_vid_sock.setblocking(False) 55 | print("VIDEO SOCKET CONNECTED") 56 | 57 | client_telem_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 58 | client_telem_sock.connect_ex((SERVER_IP, SERVER_TELEM_PORT)) 59 | client_telem_sock.setblocking(True) 60 | print("TELEMETRY SOCKET CONNECTED") 61 | break 62 | except: 63 | print("Unable to connect. Check ip") 64 | 65 | sel = selectors.DefaultSelector() 66 | sel.register(client_vid_sock, selectors.EVENT_READ|selectors.EVENT_WRITE, data = 'VIDEO') 67 | sel.register(client_telem_sock, selectors.EVENT_READ|selectors.EVENT_WRITE, data = 'TELEMETRY') 68 | 69 | message_send = threading.Thread(target = get_user_input, args = (client_vid_sock, client_telem_sock), daemon=True) 70 | message_send.start() 71 | print("Waiting for user input (specify 'vid' or 'telem' in message):\n") 72 | while vid_sock_alive or telem_sock_alive: 73 | events = sel.select(timeout=0.1)#BLOCKING, can set timeout to not block 74 | for key, mask in events: 75 | socket_obj = key.fileobj 76 | if key.data is not(None) and mask == selectors.EVENT_READ|selectors.EVENT_WRITE: 77 | if key.data == 'VIDEO': 78 | if vid_sock_alive: 79 | new_data = socket_obj.recv(4096) 80 | if not(new_data): 81 | print("%s: Pipe Broken, closing socket"%(key.data)) 82 | sel.unregister(socket_obj) 83 | socket_obj.close() 84 | vid_sock_alive = False 85 | 86 | else: 87 | search = "KILL STREAM" 88 | if new_data.find(search.encode('utf-8')) != -1: 89 | print("%s: KILL SWITCH RECIEVED -> CLOSING SOCKET"%(key.data)) 90 | sel.unregister(socket_obj) 91 | socket_obj.close() 92 | vid_sock_alive = False 93 | else: 94 | print("%s: %s"%(key.data, new_data)) 95 | 96 | 97 | else: 98 | sel.unregister(socket_obj) 99 | socket_obj.close() 100 | 101 | if key.data == 'TELEMETRY': 102 | if telem_sock_alive: 103 | new_data = socket_obj.recv(4096) 104 | if not(new_data): 105 | print("%s: Pipe Broken, closing socket"%(key.data)) 106 | sel.unregister(socket_obj) 107 | socket_obj.close() 108 | telem_sock_alive = False 109 | 110 | 111 | else: 112 | search = "KILL STREAM" 113 | if new_data.find(search.encode('utf-8')) != -1: 114 | print("%s: KILL SWITCH RECIEVED -> CLOSING SOCKET"%(key.data)) 115 | sel.unregister(socket_obj) 116 | socket_obj.close() 117 | telem_sock_alive = False 118 | else: 119 | print("%s: %s"%(key.data, new_data)) 120 | 121 | 122 | else: 123 | sel.unregister(socket_obj) 124 | socket_obj.close() 125 | print("Ending Program") 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /ground_station/GUI/2019_GUI.py: -------------------------------------------------------------------------------- 1 | from kivy.app import App 2 | #from kivy.garden.matplotlib.backend_kivyagg import FigureCanvasKivyAgg 3 | from kivy.uix.label import Label 4 | from kivy.uix.screenmanager import ScreenManager,Screen 5 | from kivy.lang import Builder 6 | from kivy.uix.widget import Widget 7 | from kivy.uix.label import Label 8 | from mpl_toolkits import mplot3d 9 | from kivy.clock import Clock 10 | import numpy as np 11 | from mpl_toolkits.mplot3d import Axes3D 12 | from matplotlib import pyplot as plt 13 | import random 14 | 15 | class Quad(Widget): 16 | def update_line(hl, new_data): 17 | xdata, ydata, zdata = hl._verts3d 18 | hl.set_xdata(list(np.append(xdata, new_data[0]))) 19 | hl.set_ydata(list(np.append(ydata, new_data[1]))) 20 | hl.set_3d_properties(list(np.append(zdata, new_data[2]))) 21 | plt.draw() 22 | def update(self,dt): 23 | self.ids.telemetry1.text = 'telemetry1: ' + str(random.randint(0,200)) 24 | self.ids.telemetry2.text = 'telemetry2: ' + str(random.randint(0,200)) 25 | self.ids.telemetry3.text = 'telemetry3: ' + str(random.randint(0,200)) 26 | self.ids.telemetry4.text = 'telemetry4: ' + str(random.randint(0,200)) 27 | #print(self.ids.telemetry1.text) 28 | #print(self) 29 | def on_touch_up(self, touch): 30 | Clock.schedule_interval(self.update,0.1) 31 | 32 | #map = plt.figure() 33 | #map_ax = Axes3D(map) 34 | #map_ax.autoscale(enable=True, axis='both', tight=True) 35 | ## Setting the axes properties 36 | #map_ax.set_xlim3d([0.0, 10.0]) 37 | #map_ax.set_ylim3d([0.0, 10.0]) 38 | #map_ax.set_zlim3d([0.0, 10.0]) 39 | #hl, = map_ax.plot3D([0], [0], [0]) 40 | # 41 | #update_line(hl, (2,2, 1)) 42 | #plt.show(block=False) 43 | #plt.pause(1) 44 | # 45 | #update_line(hl, (5,5, 5)) 46 | #plt.show(block=False) 47 | #plt.pause(2) 48 | # 49 | #update_line(hl, (8,1, 4)) 50 | #plt.show(block=True) 51 | #plt.pause(2) 52 | 53 | pass 54 | 55 | class QuadsApp(App): 56 | def build(self): 57 | #Clock.schedule_interval(Quad.update, 0.5) 58 | return Quad() 59 | 60 | QuadsApp().run() -------------------------------------------------------------------------------- /ground_station/GUI/location.txt: -------------------------------------------------------------------------------- 1 | 2,4,3 -------------------------------------------------------------------------------- /ground_station/GUI/matt.kv: -------------------------------------------------------------------------------- 1 | #:kivy 1.0 2 |