├── .gitignore ├── .gitmodules ├── .travis.yml ├── Makefile ├── README.md ├── build_for_flight.sh ├── cg.conf ├── doc ├── .gitignore ├── Makefile ├── conf.py ├── hardware.rst ├── index.rst ├── requirements.txt └── structure.rst ├── fc_bin ├── FC ├── Makefile └── README.markdown ├── health-monitor ├── README.markdown ├── monitor.py └── requirements.txt ├── main.miml ├── requirements.txt └── src ├── arm.c ├── arm.h ├── arm.miml ├── devices ├── gps.c ├── gps.h ├── gps.miml ├── mpl.h ├── rnh.c ├── rnh.h ├── rnh.miml ├── rollservo.c ├── rollservo.h └── rollservo.miml ├── ethmux.c ├── ethmux.h ├── ethmux.miml ├── filter.c ├── filter.h ├── filter.miml ├── logger.c ├── logger.h ├── logger.miml ├── rollcontrol.c ├── rollcontrol.h ├── rollcontrol.miml ├── state.c ├── state.h ├── state.miml └── utilities ├── net_addrs.c ├── net_addrs.h ├── net_addrs.miml ├── psas_packet.h ├── utils.h ├── utils_sockets.c ├── utils_sockets.h ├── utils_sockets.miml ├── utils_time.c ├── utils_time.h └── utils_time.miml /.gitignore: -------------------------------------------------------------------------------- 1 | # Packages 2 | *.egg 3 | *.egg-info 4 | dist 5 | build 6 | eggs 7 | parts 8 | sdist 9 | develop-eggs 10 | .installed.cfg 11 | 12 | # Installer logs 13 | pip-log.txt 14 | 15 | # Object files 16 | *.o 17 | *.pyc 18 | *.d 19 | 20 | # Libraries 21 | *.lib 22 | *.a 23 | 24 | # Shared objects (inc. Windows DLLs) 25 | *.dll 26 | *.so 27 | *.so.* 28 | *.dylib 29 | 30 | # Executables 31 | *.exe 32 | *.out 33 | *.app 34 | 35 | # Backup files 36 | *.bak 37 | *~ 38 | 39 | /fc 40 | /logs/ 41 | 42 | # Generated files 43 | /fcfmain.c 44 | /fcfmain.h 45 | logfile-* 46 | /miml.mk 47 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "elderberry"] 2 | path = elderberry 3 | url = https://github.com/psas/elderberry.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.2" 4 | 5 | before_install: 6 | - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test 7 | - sudo apt-get -qq update 8 | - sudo apt-get install gcc-4.8 9 | - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 50 10 | - gcc --version 11 | - git submodule update --init --recursive 12 | 13 | install: 14 | - pip install -r requirements.txt 15 | - make all 16 | 17 | script: true 18 | 19 | notifications: 20 | irc: 21 | channels: "irc.psas.pdx.edu#psas" 22 | template: 23 | - "%{repository}@%{branch}: %{message} (%{build_url})" 24 | on_success: change 25 | on_failure: change 26 | use_notice: true 27 | email: false 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | INCLUDE_DIRS := -Isrc -Isrc/devices -Isrc/utilities -I. 2 | OPTSLIVE := -flto -D FCF_FC_NETWORK 3 | OPTSDEV := -flto 4 | OPTSPROF := -pg 5 | OPTS := -Ofast 6 | WARNINGS := -Wall -Wextra -Wwrite-strings -Wno-missing-field-initializers -Wno-unused-parameter 7 | CFLAGS := -MD -std=gnu99 -g $(OPTS) $(WARNINGS) -fno-strict-aliasing $(INCLUDE_DIRS) 8 | LDFLAGS := -g 9 | LDLIBS := -lrt -lm 10 | .DEFAULT_GOAL := all 11 | OBJECTS += elderberry/fcfutils.o fcfmain.o 12 | 13 | MAINMIML ?= main.miml 14 | MIMLMK ?= miml.mk 15 | 16 | -include $(MIMLMK) 17 | 18 | all: debug 19 | 20 | live: CFLAGS += $(OPTSLIVE) 21 | live: LDFLAGS += $(OPTSLIVE) 22 | live: fc 23 | 24 | debug: CFLAGS += $(OPTSDEV) 25 | debug: LDFLAGS += $(OPTSDEV) 26 | debug: fc 27 | 28 | prof: CFLAGS += $(OPTSPROF) 29 | prof: LDFLAGS += $(OPTSPROF) 30 | prof: fc 31 | 32 | fc: miml $(OBJECTS) 33 | $(CC) $(LDFLAGS) -o $@ $(OBJECTS) $(LOADLIBES) $(LDLIBS) 34 | 35 | miml: $(MIMLMK) 36 | 37 | $(MIMLMK): $(MAINMIML) 38 | python ./elderberry/codeGen.py -mch $^ 39 | 40 | packet: 41 | gen-psas-types > src/utilities/psas_packet.h 42 | 43 | cleanbuild: 44 | rm `find . -name '*.o'` -f 45 | rm `find . -name '*.d'` -f 46 | 47 | clean: cleanbuild 48 | rm -f $(MIMLMK) fcfmain.c fcfmain.h 49 | rm -f fc 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PSAS Flight Computer 2 | 3 | [![Build Status](https://travis-ci.org/psas/av3-fc.png)](https://travis-ci.org/psas/av3-fc) 4 | 5 | [Portland State Aerospace Society](http://psas.pdx.edu/) is an open source 6 | "space program" in Portland Oregon. We build and fly state-of-the-art 7 | rocket avionics systems. We've been testing ways to fly code on small-ish 8 | rockets for over 15 years. 9 | 10 | This repo contains our current primary flight computer (an Intel Atom processor 11 | at the heart of a full stack of sensors, batteries, radios, and other electronics. This 12 | all fits in our 5.5 inch diameter, 12 foot tall, high power rocket that flies 13 | over straight up over 4 kilometers. 14 | 15 | We've been refining the code to fly a roll control experiment in July 2014. 16 | 17 | - [More information about upcoming launch](https://github.com/psas/Launch-11) 18 | - [Watch a video from past launches](https://www.youtube.com/user/psasrockets) 19 | - [Follow us on twitter @pdxaerospace](https://twitter.com/pdxaerospace) 20 | 21 | 22 | # Documentation 23 | 24 | The flight code is written in C, and features a generated `main` function and a 25 | callback-based event loop. The [event loop code generator was written by a senior 26 | CS student group in 2013](https://github.com/psas/elderberry). 27 | 28 | This abstraction lets us write small pieces of behavior with very little 29 | boilerplate. The goal is to be able to hand out well defined projects such as 30 | "write the parser for the GPS messages" to members without having to expect them 31 | to maintain the complicated interconnections to the rest of the system. 32 | 33 | - [PSAS Flight computer documentation (WIP)](http://psas-flight-computer.readthedocs.org/). 34 | 35 | This idea is very similar system to NASA's 36 | '[Core Flight Executive](http://code.nasa.gov/project/core-flight-executive-cfe/)' 37 | system for spacecraft software abstraction. 38 | 39 | 40 | # Building the Flight Computer 41 | 42 | ## Requirements 43 | 44 | The code generator runs on python3 using the pyyaml package. It's recommended to 45 | use a python virtual environment like this: 46 | 47 | $ sudo apt-get install python3 libyaml-0-2 python-pip virtualenvwrapper 48 | # Close the tab and re-open it if virtualenvwrapper was installed. 49 | $ mkvirtualenv -p `which python3` av3fc 50 | $ 51 | (av3fc)$ pip install -r requirements.txt 52 | 53 | Use `deactivate` to get out of the virtual environment. To get back into the virtual environment: 54 | 55 | $ workon av3fc 56 | 57 | Don't forget to initialize your submodules: 58 | 59 | $ git submodule update --init 60 | 61 | ## Building 62 | 63 | Due to the need for user module abstraction, the build process for the framework 64 | is a little more complicated than that of a typical C application. Here is the 65 | general build process: 66 | 67 | 1. User module MIML files and the Main.miml file are passed into the code 68 | generator. 69 | 1. The code generator, upon successful parsing and validation, creates the 70 | fcfmain.c file that include the intermodular data handlers and a Miml.mk 71 | Makefile include file. 72 | 1. The Makefile imports the Miml.mk and should successfully compile, link and 73 | build the executable "fc." 74 | 75 | To help uncomplicate this process, however, the Makefile has been created so 76 | that the user only needs to run: 77 | 78 | $ make 79 | 80 | BUT don't forget you need to be in your virtual environment for this to work (`workon av3fc`). 81 | 82 | ### Using the Makefile 83 | 84 | As discussed in the introduction to this section, the easiest way to use the 85 | Makefile is to just type `make`. 86 | 87 | Here are some other possible uses: `make miml` generates Miml.mk. 88 | `make` builds the project. Then, every repeated use of `make` rebuilds 89 | the project. If one of the ".miml" files changes, make automatically runs 90 | the code generator to rebuild fcfmain.c and fcfmain.h. If the miml files change 91 | so that modules are added or removed, one would have to rebuild the Miml.mk 92 | manually by rerunning `make miml`. 93 | -------------------------------------------------------------------------------- /build_for_flight.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | make clean 3 | make live 4 | -------------------------------------------------------------------------------- /cg.conf: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | code_filename: fcfmain.c 4 | header_filename: fcfmain.h 5 | make_filename: miml.mk 6 | 7 | allowed_types: ['int', 'unsigned int', 'char', 'unsigned char', 'unsigned short', 'void', 'int8_t', 'uint8_t', 'int16_t', 'uint16_t', 'int32_t', 'uint32_t', ROLLMessage] 8 | 9 | include_dirs: ['src', 'src/devices', 'src/utilities'] 10 | framework_dir: elderberry 11 | 12 | # Sources link module MIML files to tokens used in the master binding file. A sequence of sequences. 13 | # Also constructs miml files in make and include/finalze functions. 14 | parse_sources: {path: '/sources', type: 'list'} 15 | 16 | # Messages in master MIML file create message functions in code generated space. 17 | parse_messages: {path: '/messages', type: 'dict'} 18 | 19 | # Modules are loaded from sources. 20 | parse_modules: {path: '/modules', type: 'dict'} 21 | 22 | # Handle header file information 23 | parse_includes: {path: '/modules/*/include', type: 'str'} 24 | 25 | # Object file information 26 | parse_objects: {path: '/modules/*/object', type: 'str'} 27 | 28 | # Init function handler 29 | validate_inits: {path: 'init', type: 'str'} 30 | 31 | # Final function handler 32 | validate_finals: {path: 'final', type: 'str'} 33 | 34 | # creates initialize and finalize function. 35 | parse_init_final: {path: '/source_order', type: 'list'} 36 | 37 | # Paramater validator 38 | validate_senders: {path: '/modules/*/senders/*', type: 'list'} 39 | 40 | # Paramater validator 41 | validate_receivers: {path: '/modules/*/receivers/*', type: 'list'} 42 | 43 | # Miml Make files to track 44 | make_miml: {path: '/make_miml', type: 'list'} 45 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 16 | 17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 18 | 19 | help: 20 | @echo "Please use \`make ' where is one of" 21 | @echo " html to make standalone HTML files" 22 | @echo " dirhtml to make HTML files named index.html in directories" 23 | @echo " singlehtml to make a single large HTML file" 24 | @echo " pickle to make pickle files" 25 | @echo " json to make JSON files" 26 | @echo " htmlhelp to make HTML files and a HTML help project" 27 | @echo " qthelp to make HTML files and a qthelp project" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 31 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 32 | @echo " text to make text files" 33 | @echo " man to make manual pages" 34 | @echo " texinfo to make Texinfo files" 35 | @echo " info to make Texinfo files and run them through makeinfo" 36 | @echo " gettext to make PO message catalogs" 37 | @echo " changes to make an overview of all changed/added/deprecated items" 38 | @echo " linkcheck to check all external links for integrity" 39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 40 | 41 | clean: 42 | -rm -rf $(BUILDDIR)/* 43 | 44 | html: 45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 48 | 49 | dirhtml: 50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 51 | @echo 52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 53 | 54 | singlehtml: 55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 56 | @echo 57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 58 | 59 | pickle: 60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 61 | @echo 62 | @echo "Build finished; now you can process the pickle files." 63 | 64 | json: 65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 66 | @echo 67 | @echo "Build finished; now you can process the JSON files." 68 | 69 | htmlhelp: 70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 71 | @echo 72 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 73 | ".hhp project file in $(BUILDDIR)/htmlhelp." 74 | 75 | qthelp: 76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 77 | @echo 78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PSASTelemetryServer.qhcp" 81 | @echo "To view the help file:" 82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PSASTelemetryServer.qhc" 83 | 84 | devhelp: 85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 86 | @echo 87 | @echo "Build finished." 88 | @echo "To view the help file:" 89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/PSASTelemetryServer" 90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PSASTelemetryServer" 91 | @echo "# devhelp" 92 | 93 | epub: 94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 95 | @echo 96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 97 | 98 | latex: 99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 100 | @echo 101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 103 | "(use \`make latexpdf' here to do that automatically)." 104 | 105 | latexpdf: 106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 107 | @echo "Running LaTeX files through pdflatex..." 108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 110 | 111 | text: 112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 113 | @echo 114 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 115 | 116 | man: 117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 118 | @echo 119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 120 | 121 | texinfo: 122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 123 | @echo 124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 125 | @echo "Run \`make' in that directory to run these through makeinfo" \ 126 | "(use \`make info' here to do that automatically)." 127 | 128 | info: 129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 130 | @echo "Running Texinfo files through makeinfo..." 131 | make -C $(BUILDDIR)/texinfo info 132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 133 | 134 | gettext: 135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 136 | @echo 137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 138 | 139 | changes: 140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 141 | @echo 142 | @echo "The overview file is in $(BUILDDIR)/changes." 143 | 144 | linkcheck: 145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 146 | @echo 147 | @echo "Link check complete; look for any errors in the above output " \ 148 | "or in $(BUILDDIR)/linkcheck/output.txt." 149 | 150 | doctest: 151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 152 | @echo "Testing of doctests in the sources finished, look at the " \ 153 | "results in $(BUILDDIR)/doctest/output.txt." 154 | -------------------------------------------------------------------------------- /doc/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys, os 3 | 4 | # -- General configuration ----------------------------------------------------- 5 | extensions = ['sphinx.ext.autodoc', 6 | 'sphinx.ext.mathjax', 7 | 'sphinx.ext.viewcode' 8 | ] 9 | 10 | # Add any paths that contain templates here, relative to this directory. 11 | templates_path = ['_templates'] 12 | 13 | # The suffix of source filenames. 14 | source_suffix = '.rst' 15 | 16 | # The encoding of source files. 17 | source_encoding = 'utf-8-sig' 18 | 19 | # The master toctree document. 20 | master_doc = 'index' 21 | 22 | # General information about the project. 23 | project = u'PSAS Flight Computer' 24 | copyright = u'2013, Jamey Sharp, Theo Hill, Nathan Bergey, et. al.' 25 | 26 | version = 'L11-dev' 27 | 28 | add_module_names = True 29 | 30 | html_use_smartypants = True 31 | html_use_modindex = True 32 | html_use_index = True 33 | 34 | 35 | # The language for content autogenerated by Sphinx. Refer to documentation 36 | # for a list of supported languages. 37 | #language = None 38 | 39 | exclude_patterns = ['_build'] 40 | 41 | # The reST default role (used for this markup: `text`) to use for all documents. 42 | #default_role = None 43 | 44 | # If true, '()' will be appended to :func: etc. cross-reference text. 45 | #add_function_parentheses = True 46 | 47 | # If true, the current module name will be prepended to all description 48 | # unit titles (such as .. function::). 49 | #add_module_names = True 50 | 51 | # If true, sectionauthor and moduleauthor directives will be shown in the 52 | # output. They are ignored by default. 53 | #show_authors = False 54 | 55 | # The name of the Pygments (syntax highlighting) style to use. 56 | pygments_style = 'sphinx' 57 | 58 | # A list of ignored prefixes for module index sorting. 59 | #modindex_common_prefix = [] 60 | 61 | 62 | # -- Options for HTML output --------------------------------------------------- 63 | 64 | on_rtd = os.environ.get('READTHEDOCS', None) == 'True' 65 | if on_rtd: 66 | html_theme = 'default' 67 | else: 68 | import sphinx_rtd_theme 69 | html_theme = "sphinx_rtd_theme" 70 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 71 | 72 | # The name of an image file (relative to this directory) to place at the top 73 | # of the sidebar. 74 | #html_logo = None 75 | 76 | # The name of an image file (within the static path) to use as favicon of the 77 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 78 | # pixels large. 79 | #html_favicon = None 80 | 81 | # Add any paths that contain custom static files (such as style sheets) here, 82 | # relative to this directory. They are copied after the builtin static files, 83 | # so a file named "default.css" will overwrite the builtin "default.css". 84 | html_static_path = ['_static'] 85 | 86 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 87 | # using the given strftime format. 88 | #html_last_updated_fmt = '%b %d, %Y' 89 | 90 | # If true, SmartyPants will be used to convert quotes and dashes to 91 | # typographically correct entities. 92 | #html_use_smartypants = True 93 | 94 | # Custom sidebar templates, maps document names to template names. 95 | #html_sidebars = {} 96 | 97 | # Additional templates that should be rendered to pages, maps page names to 98 | # template names. 99 | #html_additional_pages = {} 100 | 101 | # If false, no module index is generated. 102 | #html_domain_indices = True 103 | 104 | # If false, no index is generated. 105 | #html_use_index = True 106 | 107 | # If true, the index is split into individual pages for each letter. 108 | #html_split_index = False 109 | 110 | # If true, links to the reST sources are added to the pages. 111 | #html_show_sourcelink = True 112 | 113 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 114 | #html_show_sphinx = True 115 | 116 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 117 | #html_show_copyright = True 118 | 119 | # If true, an OpenSearch description file will be output, and all pages will 120 | # contain a tag referring to it. The value of this option must be the 121 | # base URL from which the finished HTML is served. 122 | #html_use_opensearch = '' 123 | 124 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 125 | #html_file_suffix = None 126 | 127 | # Output file base name for HTML help builder. 128 | htmlhelp_basename = 'PSASTelemetryServerdoc' 129 | 130 | 131 | # -- Options for LaTeX output -------------------------------------------------- 132 | 133 | latex_elements = { 134 | # The paper size ('letterpaper' or 'a4paper'). 135 | #'papersize': 'letterpaper', 136 | 137 | # The font size ('10pt', '11pt' or '12pt'). 138 | #'pointsize': '10pt', 139 | 140 | # Additional stuff for the LaTeX preamble. 141 | #'preamble': '', 142 | } 143 | 144 | # Grouping the document tree into LaTeX files. List of tuples 145 | # (source start file, target name, title, author, documentclass [howto/manual]). 146 | latex_documents = [ 147 | ('index', 'PSASTelemetryServer.tex', u'PSAS Telemetry Server Documentation', 148 | u'Nathan Bergey, Ashley DeSimone, Bogdan Kovch, Karl Hiner, Emily Uhde, Brandon Bras, et. al.', 'manual'), 149 | ] 150 | 151 | # The name of an image file (relative to this directory) to place at the top of 152 | # the title page. 153 | #latex_logo = None 154 | 155 | # For "manual" documents, if this is true, then toplevel headings are parts, 156 | # not chapters. 157 | #latex_use_parts = False 158 | 159 | # If true, show page references after internal links. 160 | #latex_show_pagerefs = False 161 | 162 | # If true, show URL addresses after external links. 163 | #latex_show_urls = False 164 | 165 | # Documents to append as an appendix to all manuals. 166 | #latex_appendices = [] 167 | 168 | # If false, no module index is generated. 169 | #latex_domain_indices = True 170 | 171 | 172 | # -- Options for manual page output -------------------------------------------- 173 | 174 | # One entry per manual page. List of tuples 175 | # (source start file, name, description, authors, manual section). 176 | man_pages = [ 177 | ('index', 'psastelemetryserver', u'PSAS Telemetry Server Documentation', 178 | [u'Nathan Bergey, Ashley DeSimone, Bogdan Kovch, Karl Hiner, Emily Uhde, Brandon Bras, et. al.'], 1) 179 | ] 180 | 181 | # If true, show URL addresses after external links. 182 | #man_show_urls = False 183 | 184 | 185 | # -- Options for Texinfo output ------------------------------------------------ 186 | 187 | # Grouping the document tree into Texinfo files. List of tuples 188 | # (source start file, target name, title, author, 189 | # dir menu entry, description, category) 190 | texinfo_documents = [ 191 | ('index', 'PSASTelemetryServer', u'PSAS Telemetry Server Documentation', 192 | u'Nathan Bergey, Ashley DeSimone, Bogdan Kovch, Karl Hiner, Emily Uhde, Brandon Bras, et. al.', 'PSASTelemetryServer', 'One line description of project.', 193 | 'Miscellaneous'), 194 | ] 195 | 196 | # Documents to append as an appendix to all manuals. 197 | #texinfo_appendices = [] 198 | 199 | # If false, no module index is generated. 200 | #texinfo_domain_indices = True 201 | 202 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 203 | #texinfo_show_urls = 'footnote' 204 | 205 | 206 | # -- Options for Epub output --------------------------------------------------- 207 | 208 | # Bibliographic Dublin Core info. 209 | epub_title = u'PSAS Telemetry Server' 210 | epub_author = u'Nathan Bergey, Ashley DeSimone, Bogdan Kovch, Karl Hiner, Emily Uhde, Brandon Bras, et. al.' 211 | epub_publisher = u'Nathan Bergey, Ashley DeSimone, Bogdan Kovch, Karl Hiner, Emily Uhde, Brandon Bras, et. al.' 212 | epub_copyright = u'2013, Nathan Bergey, Ashley DeSimone, Bogdan Kovch, Karl Hiner, Emily Uhde, Brandon Bras, et. al.' 213 | 214 | # The language of the text. It defaults to the language option 215 | # or en if the language is not set. 216 | #epub_language = '' 217 | 218 | # The scheme of the identifier. Typical schemes are ISBN or URL. 219 | #epub_scheme = '' 220 | 221 | # The unique identifier of the text. This can be a ISBN number 222 | # or the project homepage. 223 | #epub_identifier = '' 224 | 225 | # A unique identification for the text. 226 | #epub_uid = '' 227 | 228 | # A tuple containing the cover image and cover page html template filenames. 229 | #epub_cover = () 230 | 231 | # HTML files that should be inserted before the pages created by sphinx. 232 | # The format is a list of tuples containing the path and title. 233 | #epub_pre_files = [] 234 | 235 | # HTML files shat should be inserted after the pages created by sphinx. 236 | # The format is a list of tuples containing the path and title. 237 | #epub_post_files = [] 238 | 239 | # A list of files that should not be packed into the epub file. 240 | #epub_exclude_files = [] 241 | 242 | # The depth of the table of contents in toc.ncx. 243 | #epub_tocdepth = 3 244 | 245 | # Allow duplicate toc entries. 246 | #epub_tocdup = True 247 | -------------------------------------------------------------------------------- /doc/hardware.rst: -------------------------------------------------------------------------------- 1 | ############### 2 | Flight Hardware 3 | ############### 4 | 5 | All flight data is handled via Ethernet. On the flight computer 6 | we have a hard-coded set of network addresses, recorded here. 7 | 8 | When running locally the ports are the same, but localhost is used 9 | for all IP addresses. 10 | 11 | 12 | Avionics Network Information 13 | ============================ 14 | 15 | Flight Computer 16 | ^^^^^^^^^^^^^^^ 17 | 18 | - IP Addr: ``10.0.0.10`` 19 | - MAC Addr: ? 20 | - Data Listen Port: ``36000`` 21 | - Arm Message Port: ``35666`` 22 | - RC Servo Message Port: ``35667`` 23 | 24 | 25 | Sensor Board 26 | ^^^^^^^^^^^^ 27 | 28 | - IP Addr: ``10.0.0.20`` 29 | - MAC Addr: ``E6:10:20:30:40:11`` 30 | - Listen Port: ``35001`` 31 | - Transmit Ports: 32 | 33 | - ADIS: ``35020`` 34 | - MPL: ``35010`` 35 | - MPU: ``35002`` 36 | 37 | 38 | Roll Board 39 | ^^^^^^^^^^ 40 | 41 | - IP Addr: ``10.0.0.30`` 42 | - MAC Addr: ``E6:10:20:30:40:aa`` 43 | - Listen Port: ``35003`` 44 | - Transmit Port: ``35004`` 45 | 46 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | #################### 2 | PSAS Flight Computer 3 | #################### 4 | 5 | .. sidebar:: Contents 6 | 7 | .. toctree:: 8 | :maxdepth: 1 9 | 10 | structure 11 | hardware 12 | 13 | 14 | PSAS_ is a student aerospace engineering project at Portland State University. 15 | We are building ultra-low-cost, open source rockets that feature some of the 16 | most sophisticated amateur rocket avionics systems out there today. 17 | 18 | The primary flight computer is the brains of the rocket, and our playground for 19 | testing future rocket technology. It's written in C with an event loop model. 20 | 21 | We made a custom code generator to do all the event glue and boilerplate, 22 | allowing us to focus on writing functionality. 23 | 24 | 25 | Install 26 | ======= 27 | 28 | Requirements 29 | ----------- 30 | 31 | The code generator runs on python3 using the pyyaml_ package. It's recommended 32 | to use a python virtual environment like this: :: 33 | 34 | $ sudo apt-get install python3 libyaml-0-2 python-pip virtualenvwrapper 35 | $ mkvirtualenv -p av3fc 36 | (av3fc)$ pip install -r requirements.txt 37 | 38 | .. note:: If this is your first time using python virtual environments, 39 | remember to kill your shell and open a new one after installing 40 | virtualenvwrapper for the first time (you only have to do this 41 | once). 42 | 43 | 44 | Building 45 | -------- 46 | 47 | Due to the need for user module abstraction, the build process for the framework 48 | is a little more complicated than that of a typical C application. Here is the 49 | general build process: 50 | 51 | #. User module MIML files and the Main.miml file are passed into the code 52 | generator. 53 | #. The code generator, upon successful parsing and validation, creates the 54 | fcfmain.c file that include the intermodular data handlers and a Miml.mk 55 | Makefile include file. 56 | #. The Makefile imports the Miml.mk and should successfully compile, link and 57 | build the executable "fc." 58 | 59 | To help uncomplicate this process, however, the Makefile has been created so 60 | that the user only needs to run:: 61 | 62 | (av3fc)$ make 63 | 64 | For the project to complete all three steps. 65 | 66 | 67 | Running 68 | ======= 69 | 70 | The finished executable will be in the top level directory and is called 'fc'. 71 | To start the flight computer run:: 72 | 73 | $ ./fc 74 | 75 | It will start and wait for some kind of sensor input or command. 76 | 77 | .. _PSAS: http://psas.pdx.edu/ 78 | .. _pyyaml: http://pyyaml.org/ 79 | -------------------------------------------------------------------------------- /doc/requirements.txt: -------------------------------------------------------------------------------- 1 | -r ../requirements.txt 2 | Sphinx==1.2b3 3 | sphinx-rtd-theme==0.1.5 4 | -------------------------------------------------------------------------------- /doc/structure.rst: -------------------------------------------------------------------------------- 1 | ################# 2 | Project Structure 3 | ################# 4 | 5 | :: 6 | 7 | . 8 | |-- build_for_flight.sh (use on flight computer) 9 | |-- cg.conf (code gen configuation) 10 | |-- doc (project documentation) 11 | | |-- ... 12 | |-- elderberry (Event loop code gen submodule) 13 | | |-- ... 14 | |-- fc_bin (Executable commands like ARM and SAFE) 15 | | |-- ... 16 | |-- main.miml (project configuation) 17 | |-- Makefile 18 | |-- README.md 19 | |-- requirements.txt 20 | `-- src (FC source code) 21 | |-- devices (device data handling) 22 | | |-- ... 23 | | -- utilities (helpers and includes) 24 | | |-- ... 25 | | -- main code 26 | `-- ... 27 | 28 | -------------------------------------------------------------------------------- /fc_bin/FC: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import print_function 3 | import argparse 4 | from contextlib import contextmanager 5 | import socket 6 | 7 | # Defines: 8 | FC_IP = b'127.0.0.1' 9 | FC_LISTEN_PORT = 36000 10 | ARM_PORT = 35666 11 | SERVO_PORT = 35667 12 | 13 | # List of FC commands. Stores a function pointer 'do' that can be called 14 | COMMANDS = { 15 | 'arm': {'help': "Put FC in arm state", 'do': lambda: talk("#YOLO", ARM_PORT)}, 16 | 'safe': {'help': "Safe the FC", 'do': lambda: talk("#SAFE", ARM_PORT)}, 17 | 'disable-servo': {'help': "Disable the servo", 'do': lambda: talk("DISABLE", SERVO_PORT)}, 18 | 'enable-servo': {'help': "Enable the servo", 'do': lambda: talk("ENABLE", SERVO_PORT)}, 19 | 'override': {'help': "Override the FC ARM sensor lock", 'do': lambda: talk("DI_SLOCK", ARM_PORT)}, 20 | 'slock': {'help': "Enable FC ARM sensor lock", 'do': lambda: talk("EN_SLOCK", ARM_PORT)}, 21 | } 22 | 23 | 24 | @contextmanager 25 | def udp(bind_ip, bind_port): 26 | """UDP Socket creator as a context.""" 27 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 28 | sock.bind((bind_ip, bind_port)) 29 | sock.settimeout(0.25) 30 | yield sock 31 | sock.close() 32 | 33 | 34 | def talk(msg, from_port): 35 | """FC Talker. Binds a UDP socket and writes a message to it""" 36 | with udp(b'0.0.0.0', from_port) as sock: 37 | sock.sendto(msg.encode('ascii', 'ignore'), (FC_IP, FC_LISTEN_PORT)) 38 | print("Sent", msg, "To FCF") 39 | 40 | try: 41 | data, remote_addr = sock.recvfrom(1024) 42 | print("Received", repr(data)) 43 | except socket.timeout: 44 | print ("No responce") 45 | 46 | 47 | # Create argparser 48 | parser = argparse.ArgumentParser(prog='FC') 49 | subparsers = parser.add_subparsers(title='Flight computer commands', dest="command") 50 | for command, data in COMMANDS.items(): 51 | subparsers.add_parser(command, help=data['help']) 52 | 53 | # Parse args 54 | args = parser.parse_args() 55 | 56 | # Run the command that was sent 57 | COMMANDS[args.command]['do']() 58 | 59 | -------------------------------------------------------------------------------- /fc_bin/Makefile: -------------------------------------------------------------------------------- 1 | 2 | install: 3 | cp ./FC /usr/local/bin/ 4 | -------------------------------------------------------------------------------- /fc_bin/README.markdown: -------------------------------------------------------------------------------- 1 | # Flight Comptuer bin tools 2 | 3 | Since we can send signals to the flight computer, sometimes it's useful to 4 | create those signals while in a terminal on the flight comptuer. 5 | 6 | The python program `FC` takes an action as a paramter and sends the 7 | apropriate signal. 8 | 9 | 10 | ## FC Manual 11 | 12 | usage: FC [-h] {disable-servo,enable-servo,safe,slock,override,arm} ... 13 | 14 | optional arguments: 15 | -h, --help show this help message and exit 16 | 17 | Flight computer commands: 18 | {disable-servo,enable-servo,safe,slock,override,arm} 19 | disable-servo Disable the servo 20 | enable-servo Enable the servo 21 | safe Safe the FC 22 | slock Enable FC ARM sensor lock 23 | override Override the FC ARM sensor lock 24 | arm Put FC in arm state 25 | 26 | -------------------------------------------------------------------------------- /health-monitor/README.markdown: -------------------------------------------------------------------------------- 1 | # Flight Computer Health Monitor 2 | 3 | One requirement for the flight computer is having some kind of health data in 4 | the telemetry stream. In particular we'd like to know about how hard the CPU 5 | is working and something about disk space and performance as well as network 6 | performance. 7 | 8 | Most of the reads needed to get this information are blocking, so we have 9 | a separate process (i.e. this one) to collect these values and forward them 10 | in a network packet to the main flight computer process. 11 | 12 | See [issue #2](https://github.com/psas/av3-fc/issues/2) 13 | 14 | 15 | ## Install 16 | 17 | pip install -r requirements.txt 18 | 19 | 20 | ## Run 21 | 22 | ./monitor.py 23 | -------------------------------------------------------------------------------- /health-monitor/monitor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import psutil 3 | import time 4 | from psas_packet import messages 5 | from psas_packet import network 6 | 7 | # setup 8 | rate = 0.5 # 2 Hz 9 | 10 | last_disk = psutil.disk_io_counters() 11 | last_nics = psutil.net_io_counters(pernic=True) 12 | 13 | def read(): 14 | global last_disk, last_nics 15 | pack = {} 16 | cpu = psutil.cpu_times_percent(interval=rate) 17 | ram = psutil.virtual_memory() 18 | pids = len(psutil.pids()) 19 | disk_used = psutil.disk_usage('/').used 20 | disk = psutil.disk_io_counters() 21 | nics = psutil.net_io_counters(pernic=True) 22 | 23 | pack['CPU_User'] = float(cpu.user) 24 | pack['CPU_System'] = float(cpu.system) 25 | pack['CPU_Nice'] = float(cpu.nice) 26 | pack['CPU_IOWait'] = float(cpu.iowait) 27 | pack['CPU_IRQ'] = float(cpu.irq) 28 | pack['CPU_SoftIRQ'] = float(cpu.softirq) 29 | 30 | pack['RAM_Used'] = int(ram.used) 31 | pack['RAM_Buffer'] = int(ram.buffers) 32 | pack['RAM_Cached'] = int(ram.cached) 33 | 34 | pack['PID'] = pids 35 | 36 | pack['Disk_Used'] = int(disk_used) 37 | 38 | # Since last interval 39 | pack['Disk_Read'] = int(disk.read_bytes - last_disk.read_bytes) 40 | pack['Disk_Write'] = int(disk.write_bytes - last_disk.write_bytes) 41 | 42 | lo = nics.get('lo') 43 | last_lo = last_nics.get('lo') 44 | if lo is not None and last_lo is not None: 45 | pack['IO_lo_Bytes_Sent'] = int(lo.bytes_sent - last_lo.bytes_sent) 46 | pack['IO_lo_Bytes_Recv'] = int(lo.bytes_recv - last_lo.bytes_recv) 47 | pack['IO_lo_Packets_Sent'] = int(lo.packets_sent - last_lo.packets_sent) 48 | pack['IO_lo_Packets_Recv'] = int(lo.packets_recv - last_lo.packets_recv) 49 | 50 | eth0 = nics.get('eth0') 51 | last_eth0 = last_nics.get('eth0') 52 | if eth0 is not None and last_eth0 is not None: 53 | pack['IO_eth0_Bytes_Sent'] = int(eth0.bytes_sent - last_eth0.bytes_sent) 54 | pack['IO_eth0_Bytes_Recv'] = int(eth0.bytes_recv - last_eth0.bytes_recv) 55 | pack['IO_eth0_Packets_Sent'] = int(eth0.packets_sent - last_eth0.packets_sent) 56 | pack['IO_eth0_Packets_Recv'] = int(eth0.packets_recv - last_eth0.packets_recv) 57 | 58 | wlan0 = nics.get('wlan0') 59 | last_wlan0 = last_nics.get('wlan0') 60 | if wlan0 is not None and last_wlan0 is not None: 61 | pack['IO_wlan0_Bytes_Sent'] = int(wlan0.bytes_sent - last_wlan0.bytes_sent) 62 | pack['IO_wlan0_Bytes_Recv'] = int(wlan0.bytes_recv - last_wlan0.bytes_recv) 63 | pack['IO_wlan0_Packets_Sent'] = int(wlan0.packets_sent - last_wlan0.packets_sent) 64 | pack['IO_wlan0_Packets_Recv'] = int(wlan0.packets_recv - last_wlan0.packets_recv) 65 | 66 | # temp 67 | try: 68 | with open('/sys/bus/platform/drivers/coretemp/coretemp.0/temp2_input', 'r') as tempfile: 69 | temp = int(tempfile.read().strip()) 70 | pack['Core_Temp'] = temp 71 | except: 72 | pass 73 | 74 | last_disk = disk 75 | last_nics = nics 76 | 77 | return pack 78 | 79 | 80 | if __name__ == '__main__': 81 | time.sleep(rate) 82 | 83 | seq = 0 84 | with network.SendUDP('127.0.0.1', 36000, from_port=36201) as udp: 85 | while True: 86 | # Includes a blocking read so it should run at globally defined rate 87 | udp.send_seq_message(messages.FCFH, seq, read()) 88 | seq = seq + 1 89 | -------------------------------------------------------------------------------- /health-monitor/requirements.txt: -------------------------------------------------------------------------------- 1 | argparse==1.2.1 2 | psas-packet==0.1.14 3 | psutil==2.1.0 4 | wsgiref==0.1.2 5 | -------------------------------------------------------------------------------- /main.miml: -------------------------------------------------------------------------------- 1 | sources: 2 | - [TIME, utils_time.miml] # Time utilities 3 | - [SOCK, utils_sockets.miml] # Socket utilities 4 | - [ADDR, net_addrs.miml] # RocketNet addresses 5 | - [FILTER, filter.miml] # Log filtering 6 | - [LOG, logger.miml] # Logger code Module 7 | - [GPS, gps.miml] # GPS Sensor 8 | - [ARM, arm.miml] # Arm Code Module 9 | - [RC, rollcontrol.miml] # Roll Control Code Module 10 | - [RS, rollservo.miml] # Roll Servo Control Interface 11 | - [STATE, state.miml] # State Estimation Module 12 | - [ETH, ethmux.miml] # Central Ethernet receiver 13 | - [RNH, rnh.miml] # RocketNet Hub 14 | 15 | messages: 16 | ETH.demuxed_ADIS: 17 | - LOG.log_write 18 | - ARM.arm_receive_imu 19 | - STATE.state_receive_imu 20 | 21 | ETH.demuxed_ARM: 22 | - ARM.arm_raw_in 23 | 24 | ETH.demuxed_MPU: 25 | - LOG.log_write 26 | 27 | ETH.demuxed_MPL: 28 | - LOG.log_write 29 | 30 | ETH.demuxed_BMP: 31 | - LOG.log_write 32 | 33 | ETH.demuxed_RC: 34 | - RC.rc_raw_testrc 35 | 36 | ETH.demuxed_RNH: 37 | - LOG.log_write 38 | 39 | ETH.demuxed_RNHUMB: 40 | - RC.rc_raw_umb 41 | - LOG.log_write 42 | 43 | ETH.demuxed_FCFH: 44 | - LOG.log_write 45 | 46 | ETH.demuxed_JGPS: 47 | - FILTER.filter_receive 48 | 49 | ETH.demuxed_COTS: 50 | - GPS.cots_raw_in 51 | 52 | ETH.sequenced_error: 53 | - LOG.log_write 54 | 55 | STATE.state_send_message: 56 | - RC.rc_receive_state 57 | 58 | GPS.gps_data_out: 59 | - ARM.arm_receive_gps 60 | - FILTER.filter_receive 61 | 62 | FILTER.filter_to_ground: 63 | - LOG.log_write 64 | 65 | FILTER.filter_to_disk: 66 | - LOG.log_write_disk_only 67 | 68 | ARM.arm_send_signal: 69 | - RC.rc_receive_arm 70 | - LOG.log_receive_arm 71 | 72 | RC.rc_send_servo: 73 | - RS.rs_receive_adj 74 | - LOG.log_receive_rc 75 | 76 | RNH.rnh_version_out: 77 | - LOG.log_receive_rnh_version 78 | 79 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | setuptools>=0.7 2 | PyYAML==3.10 3 | psas_packet==0.2.5 4 | -------------------------------------------------------------------------------- /src/arm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "elderberry/fcfutils.h" 7 | #include "utilities/net_addrs.h" 8 | #include "utilities/utils_sockets.h" 9 | #include "devices/gps.h" 10 | #include "arm.h" 11 | 12 | #define COMPARE_BUFFER_TO_CMD(a, b, len)\ 13 | !strncmp((char*)a, b, sizeof(b) > len? len: sizeof(b)) 14 | 15 | static int sd; 16 | 17 | bool slock_enable; 18 | bool GPS_locked; 19 | 20 | #define ACCEL_NOISE_BOUND 0.1 21 | static int upright = 0; 22 | 23 | int about(double a, double b){ 24 | return fabs(a-b) < ACCEL_NOISE_BOUND; 25 | } 26 | 27 | void arm_receive_imu(const char *ID, uint8_t *timestamp, uint16_t len, void *buf){ 28 | // does the acceleration vector == -1g over the last 100 samples? 29 | // 3.3mg per bit 30 | ADIS16405Data *data = buf; 31 | double x = data->acc_x * 0.00333; 32 | double y = data->acc_y * 0.00333; 33 | double z = data->acc_z * 0.00333; 34 | if(!about(x, -1) || !about(y, 0) || !about(z, 0)){ 35 | upright = 0; 36 | } 37 | else if(upright < 100){ 38 | ++upright; 39 | } 40 | } 41 | 42 | 43 | /** 44 | * Receive data from the COTS GPS to determine fix status. 45 | * If we don't have GPS laock we shouldn't fly. 46 | */ 47 | void arm_receive_gps(const char ID[4], uint8_t timestamp[6], uint16_t data_length, void *buffer) { 48 | // We only care about one Venus message type; ignore all others. 49 | if (memcmp("V8A8", ID, 4)) 50 | return; 51 | 52 | Venus8NavigationData *fixdata = buffer; 53 | 54 | // We must at least have a 3D fix 55 | if (fixdata->fix_mode >= VENUS_A8_FIX_MODE_3D) { 56 | GPS_locked = true; 57 | } 58 | else { 59 | GPS_locked = false; 60 | } 61 | } 62 | 63 | 64 | static void send_arm_response(const char * message){ 65 | if(write(sd, message, strlen(message)) < 0){ 66 | perror("send_arm_response: write() failed"); 67 | } 68 | } 69 | 70 | void arm_raw_in(unsigned char *buffer, int unsigned len, unsigned char * timestamp){ 71 | /* Commands: 72 | * #YOLO - (You Only Launch Once) Arm the rocket for launch 73 | * #SAFE - Disarm the rocket (default) 74 | * EN_SLOCK - Enable sensors lock required to arm (default) 75 | * DI_SLOCK - Disable sensors lock required to arm 76 | */ 77 | 78 | char ARM[] = "#YOLO"; 79 | char ARM_response[] = "successful ARM"; 80 | char ARM_decline_sensors[] = "ARM failed due to sensor lock"; 81 | char SAFE[] ="#SAFE"; 82 | char SAFE_response[] = "successful SAFE"; 83 | char EN_SLOCK []= "EN_SLOCK"; 84 | char EN_SLOCK_response[] = "successful EN_SLOCK"; 85 | char DI_SLOCK []= "DI_SLOCK"; 86 | char DI_SLOCK_response[] = "Sensor Lock Overridden"; 87 | char UNKNOWN_COMMAND[] = "UNKNOWN COMMAND"; 88 | 89 | if(COMPARE_BUFFER_TO_CMD(buffer, ARM, len)){ 90 | //send arm 91 | bool accel_locked = upright == 100; 92 | bool sensors_allow_launch = (GPS_locked && accel_locked) || !slock_enable; 93 | 94 | if(sensors_allow_launch){ 95 | arm_send_signal("ARM"); 96 | send_arm_response(ARM_response); 97 | } 98 | else{ 99 | send_arm_response(ARM_decline_sensors); 100 | } 101 | } 102 | else if(COMPARE_BUFFER_TO_CMD(buffer, SAFE, len)){ 103 | //send safe 104 | arm_send_signal("SAFE"); 105 | send_arm_response(SAFE_response); 106 | } 107 | else if(COMPARE_BUFFER_TO_CMD(buffer, EN_SLOCK, len)){ 108 | //enable slock 109 | slock_enable = true; 110 | send_arm_response(EN_SLOCK_response); 111 | } 112 | else if(COMPARE_BUFFER_TO_CMD(buffer, DI_SLOCK, len)){ 113 | //disable slock 114 | slock_enable = false; 115 | send_arm_response(DI_SLOCK_response); 116 | } 117 | else{ 118 | //unknown command 119 | send_arm_response(UNKNOWN_COMMAND); 120 | } 121 | } 122 | 123 | void arm_init(void){ 124 | slock_enable = true; 125 | sd = udp_socket(); 126 | if(sd < 0){ 127 | return; 128 | } 129 | if(connect(sd, ARM_ADDR, sizeof(struct sockaddr_in)) < 0){ 130 | perror("arm_init: connect() failed"); 131 | close(sd); 132 | } 133 | } 134 | 135 | void arm_final(void){ 136 | close(sd); 137 | } 138 | -------------------------------------------------------------------------------- /src/arm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ARM = ARM Rocket Manager 3 | * Manages global rocket state 4 | */ 5 | 6 | #ifndef ARM_H_ 7 | #define ARM_H_ 8 | #include 9 | #include "psas_packet.h" 10 | 11 | void arm_init(void); 12 | void arm_final(void); 13 | 14 | void arm_receive_imu(const char *ID, uint8_t *timestamp, uint16_t len, void *buf); 15 | void arm_receive_gps(const char *, uint8_t *, uint16_t, void *); 16 | void arm_raw_in(unsigned char *, unsigned int, unsigned char *); 17 | 18 | void arm_send_signal(const char *); 19 | #endif /* ARM_H_ */ 20 | -------------------------------------------------------------------------------- /src/arm.miml: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | include: arm.h 4 | object: src/arm.o 5 | init: arm_init(); 6 | final: arm_final(); 7 | 8 | senders: 9 | arm_send_signal: 10 | - [code, const char*] 11 | 12 | receivers: 13 | arm_raw_in: 14 | - [buffer, unsigned char*] 15 | - [len, unsigned int] 16 | - [timestamp, unsigned char*] 17 | 18 | arm_receive_imu: 19 | - [ID, const char*] 20 | - [timestamp, uint8_t*] 21 | - [data_length, uint16_t] 22 | - [data, void*] 23 | 24 | arm_receive_gps: 25 | - [ID, const char*] 26 | - [timestamp, uint8_t*] 27 | - [data_length, uint16_t] 28 | - [data, void*] 29 | -------------------------------------------------------------------------------- /src/devices/gps.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #ifndef TESTING 6 | #include "gps.h" 7 | 8 | #else 9 | 10 | #include 11 | void gps_data_out(const char ID[4], uint8_t timestamp[6], uint16_t data_length, void *buffer) 12 | { 13 | uint8_t seq = *(uint8_t *)buffer; 14 | printf("GPS packet: %s#%u @ %u len %u\n", ID, seq, timestamp[5], data_length); 15 | } 16 | void dump(uint8_t *buf, size_t len) 17 | { 18 | for (size_t i=0; i cur) 69 | break; // wait for more data 70 | 71 | if (frame[1] != 0xA1) 72 | { 73 | frame += 1; // skip A0 74 | continue; 75 | } 76 | 77 | uint16_t packet_len = (frame[2] << 8) | frame[3]; 78 | 79 | /* sanity check */ 80 | if (packet_len > sizeof buf / 2) 81 | { 82 | fprintf(stderr, "GPS packet with ID %02X too long: %u bytes.\n", frame[4], packet_len); 83 | frame += 2; // skip A0A1 84 | continue; 85 | } 86 | 87 | if (frame + 7 + packet_len > cur) 88 | break; // wait for more data 89 | 90 | if (memcmp(frame + 5 + packet_len, "\r\n", 2)) 91 | { 92 | fprintf(stderr, "GPS packet with ID %02X does not end with frame trailer.\n", frame[4]); 93 | frame += 2; // skip A0A1 94 | continue; 95 | } 96 | 97 | if (chexxor(frame+4, packet_len) != frame[4+packet_len]) 98 | { 99 | fprintf(stderr, "Bad GPS packet checksum for ID %02X.\n", frame[4]); 100 | frame += 2; // skip A0A1 101 | continue; 102 | } 103 | 104 | /* full packet, transmit ID "V8XX" where XX is the hex packet ID */ 105 | char ID[5]; 106 | sprintf(ID, "V8%02X", frame[4]); 107 | gps_data_out(ID, timestamp, packet_len-1, frame+5); 108 | 109 | frame += packet_len + 7; 110 | } 111 | 112 | /* not a full packet, move remaining data to start of buf */ 113 | memmove(buf, frame, cur - frame); 114 | cur -= frame - buf; 115 | } 116 | 117 | /** 118 | * Recieves a COTS GPS message from the network 119 | */ 120 | void cots_raw_in(const char ID[4], uint8_t timestamp[6], uint16_t data_length, void *buffer) 121 | { 122 | if (cur + data_length >= buf + sizeof(buf)) 123 | { 124 | fprintf(stderr, "GPS buffer overrun! Dropped %ld bytes of data from GPS stream.\n", cur - buf); 125 | cur = buf; 126 | } 127 | memcpy(cur, buffer, data_length); 128 | cur += data_length; 129 | send_venus_messages(timestamp); 130 | 131 | // TODO: instead of logging the raw byte stream, identify frame boundaries and emit only valid frames. 132 | //gps_data_out(ID, timestamp, data_length, buffer); 133 | } 134 | 135 | #ifdef TESTING 136 | #include 137 | 138 | struct packetA8 { 139 | uint8_t head[2]; 140 | uint16_t len; // NOTE: big-endian 141 | uint8_t ID; 142 | uint8_t seq; // for testing, add a sequence # to check dropped packets 143 | uint8_t data[57]; 144 | uint8_t checksum; 145 | uint8_t tail[2]; 146 | } __attribute__((packed)) pA8 = { { 0xA0, 0xA1 }, 0, 0xA8, 0, { }, 0, { '\r', '\n' } }; 147 | 148 | uint8_t input[0x10000]; 149 | 150 | int main(int argc, char *argv[]) 151 | { 152 | char ID[5] = "GPSV"; //??? 153 | uint8_t timestamp[6] = { 0,0,0,0,0,0 } ; 154 | size_t chunk = 100; 155 | 156 | pA8.len = htons(59); 157 | 158 | /* fill input */ 159 | uint8_t *p = input; 160 | while (p + sizeof(pA8) < input + sizeof(input)) 161 | { 162 | /* random data payload */ 163 | for (size_t i=0; i 8 | #include "psas_packet.h" 9 | 10 | // Venus A8 message fix mode bitmask: 11 | #define VENUS_A8_FIX_MODE_NO_FIX 0 12 | #define VENUS_A8_FIX_MODE_2D 1 13 | #define VENUS_A8_FIX_MODE_3D 2 14 | #define VENUS_A8_FIX_MODE_3D_DGPS 3 15 | 16 | void cots_raw_in(const char *, uint8_t *, uint16_t, void *); 17 | void gps_data_out(const char *, uint8_t *, uint16_t, void *); 18 | 19 | #endif /* GPS_H_ */ 20 | -------------------------------------------------------------------------------- /src/devices/gps.miml: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | include: gps.h 4 | object: src/devices/gps.o 5 | 6 | senders: 7 | gps_data_out: 8 | - [ID, const char*] 9 | - [timestamp, uint8_t*] 10 | - [data_length, uint16_t] 11 | - [data, void*] 12 | 13 | receivers: 14 | cots_raw_in: 15 | - [ID, const char*] 16 | - [timestamp, uint8_t*] 17 | - [data_length, uint16_t] 18 | - [data, void*] 19 | 20 | -------------------------------------------------------------------------------- /src/devices/mpl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Interprets messages from the MPL311A52 pressure sensor 3 | */ 4 | 5 | #ifndef MPL_H_ 6 | #define MPL_H_ 7 | 8 | #include 9 | 10 | typedef struct { 11 | uint32_t pressure; 12 | int16_t temperature; 13 | } __attribute__((packed)) MPLData; 14 | 15 | #endif /* MPL_H_ */ 16 | -------------------------------------------------------------------------------- /src/devices/rnh.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "elderberry/fcfutils.h" 6 | #include "utilities/utils_sockets.h" 7 | #include "utilities/psas_packet.h" 8 | #include "utilities/net_addrs.h" 9 | #include "rnh.h" 10 | 11 | static void version_callback(struct pollfd *pfd){ 12 | uint8_t buffer[50]; 13 | 14 | int length = read(pfd->fd, buffer, sizeof(buffer)); 15 | if(length < 0){ 16 | perror("rnh version callback: read() failed"); 17 | } else { 18 | rnh_version_out(buffer, length); 19 | } 20 | fcf_remove_fd(pfd->fd); 21 | close(pfd->fd); 22 | } 23 | 24 | void rnh_init(void){ 25 | int s = socket(AF_INET, SOCK_STREAM, 0); 26 | if(s < 0){ 27 | perror("Couldn't get rnh socket"); 28 | return; 29 | } 30 | if(connect(s, RNH_RCI_ADDR, sizeof(struct sockaddr_in)) < 0){ 31 | perror("rnh_init: connect() failed"); 32 | close(s); 33 | return; 34 | } 35 | fcf_add_fd(s, POLLIN, version_callback); 36 | if(write(s, "#VERS\r\n", 7) < 0){ 37 | perror("rnh_init: write failed"); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/devices/rnh.h: -------------------------------------------------------------------------------- 1 | #ifndef RNH_H_ 2 | #define RNH_H_ 3 | #include "psas_packet.h" 4 | 5 | typedef struct { 6 | char ID[4]; 7 | uint8_t timestamp[6]; 8 | uint16_t data_length; 9 | uint8_t raw[50]; 10 | } __attribute__((packed)) RNHMessage; 11 | 12 | 13 | typedef struct { 14 | uint16_t alarm1; 15 | uint16_t alarm2; 16 | uint16_t alarm3; 17 | } __attribute__((packed)) RNHAlarms; 18 | 19 | typedef struct { 20 | uint8_t detect; 21 | } __attribute__((packed)) RNHUmbdet; 22 | 23 | void rnh_version_out(uint8_t * buffer, unsigned int length); 24 | void rnh_init(void); 25 | #endif 26 | -------------------------------------------------------------------------------- /src/devices/rnh.miml: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | include: rnh.h 4 | object: src/devices/rnh.o 5 | init: rnh_init(); 6 | 7 | # Functions that handle outgoing data 8 | senders: 9 | rnh_version_out: 10 | - [ARG1, uint8_t*] 11 | - [ARG2, unsigned int] 12 | 13 | # Functions that handle incoming data 14 | receivers: 15 | -------------------------------------------------------------------------------- /src/devices/rollservo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "net_addrs.h" 4 | #include "utils.h" 5 | #include "utils_sockets.h" 6 | #include "rollservo.h" 7 | 8 | static int sd; 9 | 10 | void rollservo_init(void){ 11 | sd = udp_socket(); 12 | if(sd < 0){ 13 | return; 14 | } 15 | 16 | if(connect(sd, ROLL_ADDR, sizeof(struct sockaddr_in)) < 0){ 17 | perror("rollservo_init: connect() failed"); 18 | close(sd); 19 | } 20 | } 21 | 22 | void rollservo_final(void){ 23 | close(sd); 24 | } 25 | 26 | 27 | #define PWM_TICKS_MAX 56000 28 | #define PWM_TICKS_MIN 0 29 | #define PWM_US_MAX 3333 30 | #define PWM_US_MIN 0 31 | 32 | #define MAX_SERVO_POSITION_US 1900 33 | #define MIN_SERVO_POSITION_US 1100 34 | 35 | #define MAX_SERVO_POSITION_TICKS ( MAX_SERVO_POSITION_US * PWM_TICKS_MAX / PWM_US_MAX ) 36 | #define MIN_SERVO_POSITION_TICKS ( MIN_SERVO_POSITION_US * PWM_TICKS_MAX / PWM_US_MAX ) 37 | 38 | 39 | void rs_receive_adj(ROLLMessage* adj){ 40 | static uint32_t seq = 0; 41 | /* Slope */ 42 | const double PWM_TICKS_PER_DEGREE = (MAX_SERVO_POSITION_TICKS - MIN_SERVO_POSITION_TICKS) / (MAX_CANARD_ANGLE - MIN_CANARD_ANGLE); 43 | /* Intercept */ 44 | const double PWM_TICKS_CENTER = MIN_SERVO_POSITION_TICKS - PWM_TICKS_PER_DEGREE * MIN_CANARD_ANGLE; 45 | 46 | uint16_t ticks = PWM_TICKS_PER_DEGREE * adj->data.angle + PWM_TICKS_CENTER; 47 | ticks = CLAMP(ticks, MIN_SERVO_POSITION_TICKS, MAX_SERVO_POSITION_TICKS); 48 | 49 | char data[7]; 50 | data[0] = seq >> 24; 51 | data[1] = seq >> 16; 52 | data[2] = seq >> 8; 53 | data[3] = seq; 54 | data[4] = ticks >> 8; 55 | data[5] = ticks; 56 | data[6] = adj->data.disable; 57 | if(write(sd, data, sizeof(data)) != sizeof(data)){ 58 | perror("rs_receive_adj: write failed"); 59 | } 60 | ++seq; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /src/devices/rollservo.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Handles initializing and formatting messages for the roll servo board 3 | */ 4 | 5 | 6 | #ifndef ROLLSERVO_H_ 7 | #define ROLLSERVO_H_ 8 | 9 | #include "rollcontrol.h" 10 | 11 | void rollservo_init(void); 12 | void rollservo_final(void); 13 | 14 | void rs_receive_adj(ROLLMessage*); 15 | 16 | 17 | 18 | #endif /* ROLLSERVO_H_ */ 19 | -------------------------------------------------------------------------------- /src/devices/rollservo.miml: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | include: rollservo.h 4 | object: src/devices/rollservo.o 5 | init: rollservo_init(); 6 | final: rollservo_final(); 7 | 8 | receivers: 9 | rs_receive_adj: 10 | - [data, ROLLMessage*] 11 | -------------------------------------------------------------------------------- /src/ethmux.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "devices/mpl.h" 8 | #include "devices/rnh.h" 9 | #include "elderberry/fcfutils.h" 10 | #include "utilities/net_addrs.h" 11 | #include "utilities/psas_packet.h" 12 | #include "utilities/utils_sockets.h" 13 | #include "utilities/utils_time.h" 14 | #include "ethmux.h" 15 | 16 | static uint8_t buffer[ETH_MTU]; 17 | 18 | struct demux_type { 19 | const char ID[4]; 20 | const uint16_t data_length; 21 | void (*const handler)(const char ID[4], uint8_t timestamp[6], uint16_t data_length, void *buffer); 22 | uint32_t next_sequence; 23 | }; 24 | 25 | static int is_expected_length(struct demux_type *type, unsigned int len) { 26 | if (type->data_length) 27 | return len == sizeof(uint32_t) + type->data_length; 28 | return len >= sizeof(uint32_t) && len <= UINT16_MAX - sizeof(uint32_t); 29 | } 30 | 31 | void format_sequenced_error(unsigned short port, uint8_t * timestamp, uint32_t expected, uint32_t received) { 32 | SequenceErrorData err = { 33 | .port = htons(port), 34 | .expected = htonl(expected), 35 | .received = htonl(received), 36 | }; 37 | 38 | sequenced_error("SEQE", timestamp, sizeof err, &err); 39 | } 40 | 41 | void sequenced_receive(unsigned short port, uint8_t * buffer, unsigned int len, uint8_t* timestamp, struct demux_type *type) { 42 | if (!is_expected_length(type, len)) { 43 | format_sequenced_error(port, timestamp, 0, 0); 44 | return; 45 | } 46 | 47 | uint32_t rcvseq = ntohl(*(uint32_t*)buffer); 48 | buffer += sizeof(uint32_t); 49 | len -= sizeof(uint32_t); 50 | 51 | if (rcvseq < type->next_sequence) { 52 | format_sequenced_error(port, timestamp, type->next_sequence, rcvseq); 53 | } 54 | if (rcvseq > type->next_sequence) { 55 | format_sequenced_error(port, timestamp, type->next_sequence, rcvseq); 56 | type->handler(type->ID, timestamp, len, buffer); 57 | } 58 | if (rcvseq == type->next_sequence) { 59 | type->handler(type->ID, timestamp, len, buffer); 60 | } 61 | 62 | type->next_sequence = rcvseq + 1; 63 | } 64 | 65 | void demux(struct pollfd *pfd){ 66 | static struct demux_type seq_ADIS = { "ADIS", sizeof(ADIS16405Data), demuxed_ADIS }; 67 | static struct demux_type seq_MPU = { "MPU9", UINT16_MAX, demuxed_MPU }; 68 | static struct demux_type seq_MPL = { "MPL3", sizeof(MPLData), demuxed_MPL }; 69 | static struct demux_type seq_BMP = { "BMP1", sizeof(BMP180Data), demuxed_BMP }; 70 | static struct demux_type seq_RNH = { "RNHH", sizeof(RNHHealthData), demuxed_RNH }; 71 | static struct demux_type seq_RNHPORT = { "RNHP", sizeof(RNHPowerData), demuxed_RNH }; 72 | static struct demux_type seq_RNHALARM = { "RNHA", sizeof(RNHAlarms), demuxed_RNH }; 73 | static struct demux_type seq_RNHUMBDET = { "RNHU", sizeof(RNHUmbdet), demuxed_RNHUMB }; 74 | static struct demux_type seq_FCFH = { "FCFH", sizeof(FCFHealthData), demuxed_FCFH }; 75 | static struct demux_type seq_JGPS = { "JGPS", 0, demuxed_JGPS }; 76 | static struct demux_type seq_GPS_COTS = { "V8BS", 0, demuxed_COTS }; 77 | struct sockaddr_in packet_info; 78 | struct timespec ts; 79 | 80 | socklen_t len = sizeof(packet_info); 81 | int bytes = readsocketfromts(pfd->fd, buffer, sizeof(buffer), &packet_info, len, &ts); 82 | 83 | unsigned short port = ntohs(packet_info.sin_port); 84 | uint8_t timestamp[6]; 85 | to_psas_time(&ts, timestamp); 86 | 87 | if(bytes > 0){ 88 | switch(port){ 89 | case ADIS_PORT: 90 | sequenced_receive(port, buffer, bytes, timestamp, &seq_ADIS); 91 | break; 92 | case ARM_PORT: 93 | demuxed_ARM(buffer, bytes, timestamp); 94 | break; 95 | case MPU_PORT: 96 | sequenced_receive(port, buffer, bytes, timestamp, &seq_MPU); 97 | break; 98 | case MPL_PORT: 99 | sequenced_receive(port, buffer, bytes, timestamp, &seq_MPL); 100 | break; 101 | case BMP_PORT: 102 | sequenced_receive(port, buffer, bytes, timestamp, &seq_BMP); 103 | break; 104 | case RC_SERVO_ENABLE_PORT: 105 | demuxed_RC(buffer, bytes, timestamp); 106 | break; 107 | case RNH_BATTERY: 108 | sequenced_receive(port, buffer, bytes, timestamp, &seq_RNH); 109 | break; 110 | case RNH_PORT: 111 | sequenced_receive(port, buffer, bytes, timestamp, &seq_RNHPORT); 112 | break; 113 | case RNH_ALARM: 114 | sequenced_receive(port, buffer, bytes, timestamp, &seq_RNHALARM); 115 | break; 116 | case RNH_UMBDET: 117 | sequenced_receive(port, buffer, bytes, timestamp, &seq_RNHUMBDET); 118 | break; 119 | case FCF_HEALTH_PORT: 120 | sequenced_receive(port, buffer, bytes, timestamp, &seq_FCFH); 121 | break; 122 | case JGPS_PORT: 123 | sequenced_receive(port, buffer, bytes, timestamp, &seq_JGPS); 124 | break; 125 | case GPS_COTS: 126 | sequenced_receive(port, buffer, bytes, timestamp, &seq_GPS_COTS); 127 | break; 128 | default: 129 | break; 130 | // TODO: add a counter or debug logging of unknown ports 131 | } 132 | } 133 | } 134 | 135 | static int fd; 136 | 137 | void ethmux_init(void){ 138 | fd = timestamped_bound_udp_socket(FC_LISTEN_PORT); 139 | if(fd < 0){ 140 | return; 141 | } 142 | fcf_add_fd(fd, POLLIN, demux); 143 | } 144 | 145 | void ethmux_final(void){ 146 | //We really don't need to do this but just to be pedantic 147 | fcf_remove_fd(fd); 148 | close(fd); 149 | } 150 | 151 | -------------------------------------------------------------------------------- /src/ethmux.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Demuxes ethernet messages sent to the listen port by source and routes 3 | * them to the appropriate module. 4 | */ 5 | 6 | #ifndef ETHMUX_H_ 7 | #define ETHMUX_H_ 8 | 9 | extern void ethmux_init(void); 10 | extern void ethmux_final(void); 11 | //senders: 12 | extern void demuxed_ARM(unsigned char*, unsigned int, unsigned char*); 13 | extern void demuxed_ADIS(const char *, uint8_t *, uint16_t, void *); 14 | extern void demuxed_MPU(const char *, uint8_t *, uint16_t, void *); 15 | extern void demuxed_MPL(const char *, uint8_t *, uint16_t, void *); 16 | extern void demuxed_BMP(const char *, uint8_t *, uint16_t, void *); 17 | extern void demuxed_RC(unsigned char*, unsigned int, unsigned char*); 18 | extern void demuxed_RNH(const char *, uint8_t *, uint16_t, void *); 19 | extern void demuxed_RNHUMB(const char *, uint8_t *, uint16_t, void *); 20 | extern void demuxed_FCFH(const char *, uint8_t *, uint16_t, void *); 21 | extern void demuxed_JGPS(const char *, uint8_t *, uint16_t, void *); 22 | extern void demuxed_COTS(const char *, uint8_t *, uint16_t, void *); 23 | void sequenced_error(const char *, uint8_t *, uint16_t, void *); 24 | 25 | #endif /* ETHMUX_H_ */ 26 | -------------------------------------------------------------------------------- /src/ethmux.miml: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | include: ethmux.h 4 | object: src/ethmux.o 5 | init: ethmux_init(); 6 | final: ethmux_final(); 7 | 8 | senders: 9 | demuxed_ARM: 10 | - [data, unsigned char*] 11 | - [len, unsigned int] 12 | - [timestamp, unsigned char*] 13 | 14 | demuxed_ADIS: 15 | - [ID, const char*] 16 | - [timestamp, uint8_t*] 17 | - [data_length, uint16_t] 18 | - [data, void*] 19 | 20 | demuxed_MPU: 21 | - [ID, const char*] 22 | - [timestamp, uint8_t*] 23 | - [data_length, uint16_t] 24 | - [data, void*] 25 | 26 | demuxed_MPL: 27 | - [ID, const char*] 28 | - [timestamp, uint8_t*] 29 | - [data_length, uint16_t] 30 | - [data, void*] 31 | 32 | demuxed_BMP: 33 | - [ID, const char*] 34 | - [timestamp, uint8_t*] 35 | - [data_length, uint16_t] 36 | - [data, void*] 37 | 38 | demuxed_RC: 39 | - [data, unsigned char*] 40 | - [len, unsigned int] 41 | - [timestamp, unsigned char*] 42 | 43 | demuxed_RNH: 44 | - [ID, const char*] 45 | - [timestamp, uint8_t*] 46 | - [data_length, uint16_t] 47 | - [data, void*] 48 | 49 | demuxed_RNHUMB: 50 | - [ID, const char*] 51 | - [timestamp, uint8_t*] 52 | - [data_length, uint16_t] 53 | - [data, void*] 54 | 55 | demuxed_FCFH: 56 | - [ID, const char*] 57 | - [timestamp, uint8_t*] 58 | - [data_length, uint16_t] 59 | - [data, void*] 60 | 61 | demuxed_JGPS: 62 | - [ID, const char*] 63 | - [timestamp, uint8_t*] 64 | - [data_length, uint16_t] 65 | - [data, void*] 66 | 67 | demuxed_COTS: 68 | - [ID, const char*] 69 | - [timestamp, uint8_t*] 70 | - [data_length, uint16_t] 71 | - [data, void*] 72 | 73 | sequenced_error: 74 | - [ID, const char*] 75 | - [timestamp, uint8_t*] 76 | - [data_length, uint16_t] 77 | - [data, void*] 78 | 79 | receivers: 80 | -------------------------------------------------------------------------------- /src/filter.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "filter.h" 3 | 4 | void filter_receive(const char ID[4], uint8_t timestamp[6], uint16_t data_length, void *data) 5 | { 6 | // We log most GPS data to disk only. 7 | if (!memcmp(ID, "V8", 2) || !memcmp(ID, "JGPS", 4)) { 8 | // ...with the exception of V8A8, which we want to see in real time. 9 | if (!memcmp(ID, "V8A8", 4)) 10 | filter_to_ground(ID, timestamp, data_length, data); 11 | else 12 | filter_to_disk(ID, timestamp, data_length, data); 13 | } 14 | else // Everything else we want in real time. 15 | filter_to_ground(ID, timestamp, data_length, data); 16 | } 17 | -------------------------------------------------------------------------------- /src/filter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Policy for whether messages should be logged only to disk or also over Wi-Fi. 3 | */ 4 | 5 | #ifndef _FILTER_H_ 6 | #define _FILTER_H_ 7 | 8 | #include 9 | 10 | void filter_receive(const char ID[4], uint8_t timestamp[6], uint16_t data_length, void *data); 11 | void filter_to_ground(const char ID[4], uint8_t timestamp[6], uint16_t data_length, void *data); 12 | void filter_to_disk(const char ID[4], uint8_t timestamp[6], uint16_t data_length, void *data); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/filter.miml: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | include: filter.h 4 | object: src/filter.o 5 | 6 | senders: 7 | filter_to_ground: 8 | - [ID, const char*] 9 | - [timestamp, uint8_t*] 10 | - [data_length, uint16_t] 11 | - [data, void*] 12 | 13 | filter_to_disk: 14 | - [ID, const char*] 15 | - [timestamp, uint8_t*] 16 | - [data_length, uint16_t] 17 | - [data, void*] 18 | 19 | receivers: 20 | filter_receive: 21 | - [ID, const char*] 22 | - [timestamp, uint8_t*] 23 | - [data_length, uint16_t] 24 | - [data, void*] 25 | -------------------------------------------------------------------------------- /src/logger.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "../elderberry/fcfutils.h" 13 | #include "utilities/utils_sockets.h" 14 | #include "utilities/utils_time.h" 15 | #include "utilities/psas_packet.h" 16 | #include "utilities/net_addrs.h" 17 | #include "logger.h" 18 | 19 | 20 | /* 21 | * because of preprocessor shenanigans, macro defined constants need to pass 22 | * through two layers of macro function to correctly stringify. 23 | */ 24 | 25 | #define _PASTE(a, b) a##b 26 | #define PASTE(a, b) _PASTE(a, b) 27 | #define _STRINGIFY(x) #x 28 | #define STRINGIFY(x) _STRINGIFY(x) 29 | 30 | #define LOGFILE_DIGITS 3 31 | #define LOGFILE_BASE "logfile-" 32 | 33 | #define LINK_MTU 1500 34 | #define UDP_HEADER_SIZE 8 35 | #define IPv4_MAX_HEADER_SIZE 60 36 | #define P_LIMIT LINK_MTU - UDP_HEADER_SIZE - IPv4_MAX_HEADER_SIZE - sizeof(uint32_t) 37 | 38 | #define LOG_TIMEOUT_NS 100e6 //100ms 39 | 40 | static char log_buffer[P_LIMIT]; // Global so destructor can flush final data 41 | static unsigned int log_buffer_size = 0; 42 | static char disk_log_buffer[65536]; 43 | static unsigned int disk_log_buffer_size = 0; 44 | static int disk_fd; 45 | static int net_fd; 46 | 47 | // sequence number, each UDP packet gets a number 48 | uint32_t sequence; 49 | 50 | 51 | static void open_logfile(void) 52 | { 53 | /* Compute 10 ** LOGFILE_DIGITS at compile time. */ 54 | const int attempt_count = PASTE(1e, LOGFILE_DIGITS); 55 | 56 | char buf[sizeof LOGFILE_BASE + LOGFILE_DIGITS]; 57 | 58 | int i; 59 | for(i = 0; i < attempt_count; ++i) 60 | { 61 | snprintf(buf, sizeof buf, LOGFILE_BASE "%0" STRINGIFY(LOGFILE_DIGITS) "d", i); 62 | int fd = open(buf, O_WRONLY | O_CREAT | O_EXCL, 0444); 63 | if(fd == -1) 64 | { 65 | if(errno == EEXIST || errno == EISDIR) 66 | continue; 67 | fprintf(stderr, "permanent failure creating logfile %s: %s\n", buf, strerror(errno)); 68 | exit(1); 69 | } 70 | 71 | disk_fd = fd; 72 | return; 73 | } 74 | 75 | fprintf(stderr, "tried %d filenames but couldn't create any logfile\n", i); 76 | exit(1); 77 | } 78 | 79 | static void open_socket(void) 80 | { 81 | net_fd = socket(AF_INET, SOCK_DGRAM, 0); 82 | if(net_fd < 0) 83 | { 84 | perror("socket(AF_INET, SOCK_DGRAM, 0)"); 85 | exit(1); 86 | } 87 | 88 | int broadcast_flag = 1; 89 | /* ignore errors from allowing broadcast addresses; we'll detect the error at connect, below */ 90 | setsockopt(net_fd, SOL_SOCKET, SO_BROADCAST, &broadcast_flag, sizeof(broadcast_flag)); 91 | 92 | if(connect(net_fd, WIFI_ADDR, sizeof(struct sockaddr_in)) < 0) 93 | { 94 | perror("could not connect to WIFI"); 95 | exit(1); 96 | } 97 | } 98 | 99 | static void log_timeout(struct pollfd * pfd); 100 | void logger_init() { 101 | open_logfile(); 102 | 103 | // Outgoing socket (WiFi) 104 | open_socket(); 105 | 106 | // Initialize sequence number 107 | sequence = 0; 108 | log_buffer_size = 0; 109 | disk_log_buffer_size = 0; 110 | 111 | 112 | int tfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); 113 | struct itimerspec newval; 114 | newval.it_interval.tv_sec = 0; 115 | newval.it_interval.tv_nsec = LOG_TIMEOUT_NS; 116 | newval.it_value.tv_sec = 0; 117 | newval.it_value.tv_nsec = LOG_TIMEOUT_NS; 118 | timerfd_settime(tfd, 0, &newval, NULL); 119 | fcf_add_fd(tfd, POLLIN, log_timeout); 120 | } 121 | 122 | 123 | static void flush_log() 124 | { 125 | if (!disk_log_buffer_size) 126 | return; 127 | 128 | // Send current buffer to disk 129 | // for the log file, convert the sequence number to a SEQN message 130 | message_header header = { 131 | .ID={"SEQN"}, 132 | .data_length=htons(sizeof(SequenceNoData)) 133 | }; 134 | get_psas_time(header.timestamp); 135 | 136 | uint32_t swapped_sequence = htonl(sequence); 137 | 138 | const struct iovec disk_iov[] = { 139 | { .iov_base = &header, .iov_len = sizeof header }, 140 | { .iov_base = &swapped_sequence, .iov_len = sizeof swapped_sequence }, 141 | { .iov_base = disk_log_buffer, .iov_len = disk_log_buffer_size }, 142 | }; 143 | writev(disk_fd, disk_iov, sizeof disk_iov / sizeof *disk_iov); 144 | 145 | const struct iovec net_iov[] = { 146 | { .iov_base = &swapped_sequence, .iov_len = sizeof swapped_sequence }, 147 | { .iov_base = log_buffer, .iov_len = log_buffer_size }, 148 | }; 149 | writev(net_fd, net_iov, sizeof net_iov / sizeof *net_iov); 150 | 151 | // Reset buffer size 152 | log_buffer_size = 0; 153 | disk_log_buffer_size = 0; 154 | 155 | // Increment sequence number 156 | sequence++; 157 | } 158 | 159 | void logger_final() { 160 | flush_log(); 161 | close(disk_fd); 162 | close(net_fd); 163 | } 164 | 165 | static void ensure_disk_log_space(unsigned int len) { 166 | if (disk_log_buffer_size + len > sizeof disk_log_buffer) 167 | flush_log(); 168 | } 169 | 170 | static void ensure_net_log_space(unsigned int len) { 171 | ensure_disk_log_space(len); 172 | if (log_buffer_size + len > sizeof log_buffer) 173 | flush_log(); 174 | } 175 | 176 | void log_write_disk_only(const char ID[4], const uint8_t timestamp[6], uint16_t data_length, const void *data) 177 | { 178 | unsigned int len = sizeof(message_header) + data_length; 179 | ensure_disk_log_space(len); 180 | 181 | message_header *header = (message_header *) (disk_log_buffer + disk_log_buffer_size); 182 | memcpy(header->ID, ID, sizeof header->ID); 183 | memcpy(header->timestamp, timestamp, sizeof header->timestamp); 184 | header->data_length = htons(data_length); 185 | 186 | memcpy(disk_log_buffer + disk_log_buffer_size + sizeof(message_header), data, data_length); 187 | 188 | disk_log_buffer_size += len; 189 | } 190 | 191 | void log_write(const char ID[4], const uint8_t timestamp[6], uint16_t data_length, const void *data) 192 | { 193 | unsigned int len = sizeof(message_header) + data_length; 194 | ensure_net_log_space(len); 195 | 196 | unsigned int disk_start = disk_log_buffer_size; 197 | log_write_disk_only(ID, timestamp, data_length, data); 198 | 199 | memcpy(log_buffer + log_buffer_size, disk_log_buffer + disk_start, len); 200 | log_buffer_size += len; 201 | } 202 | 203 | static void log_timeout(struct pollfd * pfd){ 204 | char buf[8]; 205 | if(read(pfd->fd, buf, 8)<0){ //clears timerfd 206 | perror("log_timeout: read() failed"); 207 | } 208 | flush_log(); 209 | } 210 | 211 | void log_receive_arm(const char* code){ 212 | uint8_t timestamp[6]; 213 | get_psas_time(timestamp); 214 | log_write("MESG", timestamp, strlen(code), code); 215 | } 216 | 217 | void log_receive_rc(ROLLMessage* data) { 218 | union { 219 | uint64_t uint; 220 | double doub; 221 | } convert; 222 | 223 | convert.doub = data->data.angle; 224 | convert.uint = __builtin_bswap64(convert.uint); 225 | data->data.angle = convert.doub; 226 | log_write(data->ID, data->timestamp, data->data_length, &data->data); 227 | } 228 | 229 | void log_receive_rnh_version(uint8_t * message, unsigned int length){ 230 | uint8_t timestamp[6]; 231 | get_psas_time(timestamp); 232 | log_write("VERS", timestamp, length, message); 233 | } 234 | -------------------------------------------------------------------------------- /src/logger.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Handles handles formatting and logging messages to disk and over WiFi 3 | */ 4 | 5 | #ifndef _LOGGER_H_ 6 | #define _LOGGER_H_ 7 | 8 | #include "gps.h" 9 | #include "rollcontrol.h" 10 | 11 | typedef struct{ 12 | char ID[4]; 13 | uint8_t timestamp[6]; 14 | uint16_t data_length; 15 | } __attribute__((packed)) message_header; 16 | 17 | void logger_init(void); // [miml:init] 18 | void logger_final(void); // [miml:final] 19 | 20 | void log_write_disk_only(const char ID[4], const uint8_t timestamp[6], uint16_t data_length, const void *data); 21 | void log_write(const char ID[4], const uint8_t timestamp[6], uint16_t data_length, const void *data); 22 | void log_receive_rc(ROLLMessage*); 23 | void log_receive_rnh_version(uint8_t * buffer, unsigned int length); 24 | void log_receive_arm(const char*); 25 | 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/logger.miml: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | include: logger.h 4 | object: src/logger.o 5 | init: logger_init(); 6 | final: logger_final(); 7 | 8 | # Functions that handle outgoing data 9 | senders: 10 | 11 | # Functions that handle incoming data 12 | receivers: 13 | log_write_disk_only: 14 | - [ID, const char*] 15 | - [timestamp, uint8_t*] 16 | - [data_length, uint16_t] 17 | - [data, void*] 18 | 19 | log_write: 20 | - [ID, const char*] 21 | - [timestamp, uint8_t*] 22 | - [data_length, uint16_t] 23 | - [data, void*] 24 | 25 | log_receive_arm: 26 | - [code, const char*] 27 | 28 | log_receive_rc: 29 | - [data, ROLLMessage*] 30 | 31 | log_receive_rnh_version: 32 | - [ARG1, uint8_t*] 33 | - [ARG2, unsigned int] 34 | -------------------------------------------------------------------------------- /src/rollcontrol.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "elderberry/fcfutils.h" 7 | #include "utilities/utils.h" 8 | #include "utilities/utils_time.h" 9 | #include "utilities/net_addrs.h" 10 | #include "utilities/utils_sockets.h" 11 | #include "devices/rnh.h" 12 | #include "rollcontrol.h" 13 | 14 | /* Important Stuff for PID loop! */ 15 | 16 | /* PID gain constants */ 17 | #define Kp 5.0 18 | #define Ki 0.01 19 | #define Kd 0 20 | 21 | /* PID target */ 22 | #define pidTarget 0 23 | 24 | /* for clamping integrator term in PID */ 25 | #define integrator_max (100 / Ki) 26 | #define integrator_min (-100 / Ki) 27 | 28 | /* global variables needed for PID controller */ 29 | static double lastError; 30 | static double integrator; 31 | 32 | /* End Important Stuff for PID loop! */ 33 | 34 | 35 | /* Fin charachterization constants */ 36 | #define FINFIT_A 0.0006 37 | #define FINFIT_B 0.045 38 | #define FIN_CBASE 3.2 39 | #define FIN_AREA 1.13e-3 40 | #define FIN_ARM 0.085 41 | #define I_INIT 0.086 42 | #define I_BO 0.077 43 | #define EXPECTED_BURN_TIME 5.7 44 | #define SUBSONIC 265 45 | #define SUPERSONIC 330 46 | 47 | // Converts degrees to radians. 48 | #define degreesToRadians(angleDegrees) (angleDegrees * M_PI / 180.0) 49 | 50 | // Converts radians to degrees. 51 | #define radiansToDegrees(angleRadians) (angleRadians * 180.0 / M_PI) 52 | 53 | static int sd; 54 | 55 | static bool enable_servo; 56 | static bool armed; 57 | 58 | #define TIMEOUT (120 * UINT64_C(1000000000)) // two minutes in nanoseconds 59 | 60 | static bool launched = false; 61 | static uint64_t timeout; 62 | 63 | static void set_servo_enable(bool enable) 64 | { 65 | enable_servo = enable; 66 | } 67 | 68 | static void set_armed(bool arm) 69 | { 70 | armed = arm; 71 | set_servo_enable(arm); 72 | } 73 | 74 | void rollcontrol_init(void){ 75 | set_armed(false); 76 | 77 | sd = udp_socket(); 78 | if(sd < 0){ 79 | return; 80 | } 81 | if(connect(sd, RC_SERVO_ENABLE_ADDR, sizeof(struct sockaddr_in)) < 0){ 82 | perror("rollcontrol_init: connect() failed"); 83 | close(sd); 84 | } 85 | 86 | } 87 | 88 | static void set_canard_angle(double degrees) 89 | { 90 | degrees = CLAMP(degrees, MIN_CANARD_ANGLE, MAX_CANARD_ANGLE); 91 | ROLLMessage out = { 92 | .ID = {"ROLL"}, 93 | .data_length = sizeof(RollServoData), 94 | .data = { 95 | .angle = degrees, 96 | .disable = !enable_servo, 97 | }, 98 | }; 99 | get_psas_time(out.timestamp); 100 | 101 | rc_send_servo(&out); 102 | } 103 | 104 | 105 | /** 106 | * Subsonic fin estimation 107 | */ 108 | static double subsonic_fin(double set_aa_rad, double I, double rd, StateData state) { 109 | double v = state.vel_up; 110 | double alpha = sqrt(fabs(2*set_aa_rad*I*FINFIT_A)/(rd*v*v*FIN_AREA*FIN_ARM) + FINFIT_B*FINFIT_B) - FINFIT_B; 111 | alpha = alpha / (2*FINFIT_A); 112 | return alpha; // Orginial fit variables include degress/radians, so this return is in degrees 113 | } 114 | 115 | /** 116 | * Supersonic fin estimation 117 | */ 118 | static double supersonic_fin(double set_aa_rad, double I, double rd, StateData state) { 119 | double v = state.vel_up; 120 | double alpha = (set_aa_rad*I)/(2*rd*v*v*FIN_AREA*FIN_ARM*FIN_CBASE); 121 | return radiansToDegrees(alpha); 122 | } 123 | 124 | 125 | /** 126 | * Given a correction from control system, estimate correct angle of attack for canard 127 | */ 128 | static double estimate_alpha(double set_aa, StateData state) { 129 | 130 | double velocity = state.vel_up; 131 | double time = state.time; 132 | 133 | // obvious cases (and avoid divide by 0): 134 | if ((fabs(set_aa) < 1) || (velocity < 1)) 135 | return 0; 136 | 137 | double aa = fabs(degreesToRadians(set_aa)); 138 | 139 | // Compute inertia 140 | double I = I_INIT; 141 | if (time < EXPECTED_BURN_TIME) { 142 | I = I_INIT + (I_BO - I_INIT)*(time / EXPECTED_BURN_TIME); 143 | } else { 144 | I = I_BO; 145 | } 146 | 147 | // Compute atmosphere 148 | double rd = 1.2250 * exp((-9.80665 * 0.0289644 * state.altitude)/(8.31432*288.15)); 149 | 150 | 151 | // default is 0 152 | double output = 0; 153 | 154 | // Subsonic, transonic, and supersonic cases: 155 | if (velocity <= SUBSONIC) 156 | output = subsonic_fin(aa, I, rd, state); 157 | else if (velocity < SUPERSONIC) { 158 | double y0 = subsonic_fin(aa, I, rd, state); 159 | double y1 = supersonic_fin(aa, I, rd, state); 160 | output = y0 + (y1-y0)*(velocity - SUBSONIC)/(SUPERSONIC-SUBSONIC); 161 | } 162 | else 163 | output = supersonic_fin(aa, I, rd, state); 164 | 165 | // Make negative if nessisary 166 | if (set_aa < 0) 167 | return -output; 168 | return output; 169 | } 170 | 171 | // rnhumb message, assume disconnect is launch detect 172 | void rc_raw_umb(const char *ID, unsigned char* timestamp, unsigned int len, void* data) 173 | { 174 | if (memcmp(ID, "RNHU", 4)) 175 | return; 176 | 177 | RNHUmbdet *umb = (RNHUmbdet *)data; 178 | if (!launched && !umb->detect) // did umbilical just now disconnect? 179 | timeout = from_psas_time(timestamp) + TIMEOUT; 180 | 181 | launched = !umb->detect; 182 | } 183 | 184 | static void check_timeout(const uint8_t* timestamp) 185 | { 186 | if (!launched || !enable_servo) return; 187 | 188 | if (from_psas_time(timestamp) > timeout) 189 | { 190 | // timeout: disable the canards 191 | // NOTE: won't center them, disabling has precedence 192 | // TODO: make a little state machine to center, wait, then disable 193 | set_servo_enable(false); 194 | set_canard_angle(0); 195 | } 196 | } 197 | 198 | void rc_receive_state(const char *ID, uint8_t *timestamp, uint16_t len, void *buf) { 199 | 200 | check_timeout(timestamp); 201 | 202 | if (!enable_servo) 203 | return; 204 | 205 | if (memcmp(ID, "VSTE", 4)) 206 | return; 207 | 208 | StateData *state = buf; 209 | 210 | /* begin PID Controller */ 211 | 212 | /* 213 | determine the error by taking the difference of the target 214 | and the current value 215 | */ 216 | double error = pidTarget - state->roll_rate; 217 | 218 | /* proportional stage */ 219 | double proportional = Kp * error; 220 | 221 | /* integral stage */ 222 | double integral = Ki * integrator; 223 | 224 | /* derivative stage */ 225 | double derivative = Kd * (error - lastError); 226 | 227 | /* output of the PID controller */ 228 | /* sum each stage together */ 229 | double correction = proportional + integral + derivative; 230 | 231 | /* remember the error for the derivative stage */ 232 | lastError = error; 233 | 234 | /* add the error to the integral stage for next step */ 235 | integrator += error; 236 | 237 | /* clamp the integrator! */ 238 | if(integrator > integrator_max){ 239 | integrator = integrator_max; 240 | } 241 | else if(integrator < integrator_min){ 242 | integrator = integrator_min; 243 | } 244 | 245 | // Look normilized fin angle based on requested angular acceleration 246 | double output = estimate_alpha(correction, *state); 247 | 248 | /* end PID controller */ 249 | 250 | set_canard_angle(output); 251 | } 252 | 253 | void rc_receive_arm(const char * signal){ 254 | if(!strcmp(signal, "ARM")){ 255 | set_armed(true); 256 | }else if(!strcmp(signal, "SAFE")){ 257 | set_armed(false); 258 | set_canard_angle(0); 259 | } 260 | } 261 | 262 | static void send_servo_response(const char * message){ 263 | int len = strlen(message); 264 | if(write(sd, message, len) != len){ 265 | perror("send_servo_response: write failed"); 266 | } 267 | } 268 | 269 | 270 | #define COMPARE_BUFFER_TO_CMD(a, b, len)\ 271 | !strncmp((char*)a, b, sizeof(b) > len? len: sizeof(b)) 272 | 273 | 274 | void rc_raw_testrc(unsigned char * data, unsigned int len, unsigned char* timestamp){ 275 | if(!armed){ 276 | if(COMPARE_BUFFER_TO_CMD(data, "ENABLE", len)){ 277 | set_servo_enable(true); 278 | send_servo_response("Roll control servos enabled"); 279 | } 280 | else if(COMPARE_BUFFER_TO_CMD(data, "DISABLE", len)){ 281 | set_servo_enable(false); 282 | set_canard_angle(0); 283 | send_servo_response("Roll control servos disabled"); 284 | } 285 | else{ 286 | send_servo_response("Unknown servo command"); 287 | } 288 | } 289 | else{ 290 | send_servo_response("Roll control servos state not changed due to being armed"); 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /src/rollcontrol.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Handles the logic of turning sensor data into roll control fin position 3 | */ 4 | 5 | #ifndef ROLLCONTROL_H_ 6 | #define ROLLCONTROL_H_ 7 | 8 | #include "utilities/psas_packet.h" 9 | 10 | #define MAX_CANARD_ANGLE 15.0 // Canard position in Degrees 11 | #define MIN_CANARD_ANGLE -15.0 // Canard position in Degrees 12 | 13 | void rollcontrol_init(void); // [miml:init] 14 | void rc_receive_state(const char *ID, uint8_t *timestamp, uint16_t len, void *buf); // [miml:receiver] 15 | void rc_receive_arm(const char *); // [miml:receiver] 16 | void rc_raw_umb(const char *, unsigned char*, unsigned int, void*); // [miml:receiver] 17 | void rc_raw_testrc(unsigned char *, unsigned int, unsigned char*); // [miml:receiver] 18 | 19 | 20 | void rc_send_servo(ROLLMessage*); // [miml:sender] 21 | 22 | #endif /* ROLLCONTROL_H_ */ 23 | 24 | -------------------------------------------------------------------------------- /src/rollcontrol.miml: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | include: rollcontrol.h 4 | object: src/rollcontrol.o 5 | init: rollcontrol_init(); 6 | 7 | senders: 8 | rc_send_servo: 9 | - [data, ROLLMessage*] 10 | 11 | receivers: 12 | rc_receive_state: 13 | - [ID, const char*] 14 | - [timestamp, uint8_t*] 15 | - [data_length, uint16_t] 16 | - [data, void*] 17 | 18 | rc_receive_arm: 19 | - [code, const char*] 20 | 21 | rc_raw_umb: 22 | - [ID, const char*] 23 | - [timestamp, uint8_t*] 24 | - [data_length, uint16_t] 25 | - [data, void*] 26 | 27 | rc_raw_testrc: 28 | - [code, unsigned char*] 29 | - [len, unsigned int] 30 | - [timestamp, unsigned char*] 31 | -------------------------------------------------------------------------------- /src/state.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "state.h" 7 | #include "elderberry/fcfutils.h" 8 | #include "utilities/utils_time.h" 9 | 10 | static StateData current_state; 11 | static uint64_t last_time; 12 | static bool has_launched; 13 | static uint64_t launch_time; 14 | 15 | 16 | void state_init(void) { 17 | has_launched = false; 18 | current_state.time = 0; 19 | current_state.acc_up = 0; 20 | current_state.vel_up = 0; 21 | current_state.altitude = LAUNCH_ALTITUDE; 22 | current_state.roll_rate = 0; 23 | current_state.roll_angle = 0; 24 | } 25 | 26 | 27 | /** 28 | * Recieve data from the IMU, and integrate to a state estimation 29 | */ 30 | void state_receive_imu(const char *ID, uint8_t *timestamp, uint16_t len, void *buf) { 31 | const uint64_t now = from_psas_time(timestamp); 32 | ADIS16405Data *imu = buf; 33 | 34 | // Convert raw IMU data to MKS/degrees unit system 35 | const double accel = ADIS_GLSB * (int16_t) ntohs(imu->acc_x); 36 | const double roll_rate = ADIS_RLSB * (int16_t) ntohs(imu->gyro_x); 37 | 38 | if (!has_launched && fabs(accel) > 40) { 39 | has_launched = true; 40 | launch_time = now; 41 | } 42 | 43 | if (last_time == 0) 44 | last_time = now; 45 | if (has_launched) { 46 | const double dt = (now - last_time) / 1.0e9; 47 | // Integrate sensors 48 | current_state.time = (now - launch_time) / 1.0e9; 49 | current_state.acc_up = accel - 9.81; // Subtract gravity (Remember, IMU is upside down) 50 | current_state.vel_up += current_state.acc_up*dt; 51 | current_state.altitude += current_state.vel_up*dt; 52 | current_state.roll_rate = -roll_rate; //Sensor coordinate frame is rotated 180 53 | current_state.roll_angle += roll_rate*dt; //This is wrong (negative) but we don't care, nothing uses it. 54 | } 55 | 56 | // Send data 57 | state_send_message("VSTE", timestamp, sizeof(StateData), ¤t_state); 58 | 59 | last_time = now; 60 | } 61 | 62 | 63 | void state_raw_ld_in(unsigned char *signal, unsigned int len, unsigned char *timestamp) { 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/state.h: -------------------------------------------------------------------------------- 1 | /** 2 | * State estimation on the rocket 3 | */ 4 | 5 | #ifndef STATE_H_ 6 | #define STATE_H_ 7 | 8 | #include "utilities/psas_packet.h" 9 | 10 | #define EARTHG 9.80665 11 | #define ADIS_GLSB 0.00333 * EARTHG 12 | #define ADIS_RLSB 0.05 13 | #define LAUNCH_ALTITUDE 1390.0 14 | 15 | void state_init(void); // [miml:init] 16 | void state_receive_imu(const char *, uint8_t *, uint16_t, void *); // [miml:receiver] 17 | void state_raw_ld_in(unsigned char *, unsigned int, unsigned char*); // [miml:receiver] 18 | void state_send_message(const char *, uint8_t *, uint16_t, void *); // [miml:sender] 19 | 20 | 21 | #endif /* STATE_H_ */ 22 | -------------------------------------------------------------------------------- /src/state.miml: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | include: state.h 4 | object: src/state.o 5 | init: state_init(); 6 | 7 | senders: 8 | 9 | state_send_message: 10 | - [ID, const char*] 11 | - [timestamp, uint8_t*] 12 | - [data_length, uint16_t] 13 | - [data, void*] 14 | 15 | receivers: 16 | state_receive_imu: 17 | - [ID, const char*] 18 | - [timestamp, uint8_t*] 19 | - [data_length, uint16_t] 20 | - [data, void*] 21 | 22 | state_raw_ld_in: 23 | - [code, unsigned char*] 24 | - [len, unsigned int] 25 | - [timestamp, unsigned char*] 26 | 27 | -------------------------------------------------------------------------------- /src/utilities/net_addrs.c: -------------------------------------------------------------------------------- 1 | #include "net_addrs.h" 2 | 3 | /* Redefinition of htons() and htonl() because using the ones in endian.h result in 4 | * error: braced-group within expression allowed only inside a function 5 | */ 6 | 7 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 8 | #define HTONS(n) __builtin_bswap16(n) 9 | #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 10 | #define htons(n) (n) 11 | #endif 12 | 13 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 14 | #define HTONL(n) __builtin_bswap32(n) 15 | #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 16 | #define htonl(n) (n) 17 | #endif 18 | 19 | /** Set an IP address given by the four byte-parts */ 20 | #define IPv4(a,b,c,d) \ 21 | HTONL(((uint32_t)((a) & 0xff) << 24) | \ 22 | ((uint32_t)((b) & 0xff) << 16) | \ 23 | ((uint32_t)((c) & 0xff) << 8) | \ 24 | (uint32_t)((d) & 0xff) \ 25 | ) 26 | /* Returns a pointer to a filled anonymous struct sockaddr_in 27 | * suitable for direct use in bind() and connect() 28 | */ 29 | #define make_addr(addr, port) \ 30 | (const struct sockaddr *) &(const struct sockaddr_in){ \ 31 | .sin_family = AF_INET, \ 32 | .sin_port = HTONS((port)), \ 33 | .sin_addr = { (addr) } \ 34 | } 35 | 36 | 37 | #ifdef FCF_FC_NETWORK 38 | #define RNH_RCI_PORT RCI_PORT 39 | #define SENSOR_RCI_PORT RCI_PORT 40 | #define GPS_RCI_PORT RCI_PORT 41 | #define ROLL_RCI_PORT RCI_PORT 42 | 43 | #else 44 | #define RNH_RCI_PORT 37001 45 | #define SENSOR_RCI_PORT 37002 46 | #define GPS_RCI_PORT 37003 47 | #define ROLL_RCI_PORT 37004 48 | #endif 49 | 50 | const struct sockaddr * RCI_ADDR = make_addr(FC_IP, RCI_PORT); 51 | const struct sockaddr * RNH_RCI_ADDR = make_addr(RNH_IP, RNH_RCI_PORT); 52 | const struct sockaddr * SENSOR_RCI_ADDR = make_addr(SENSOR_IP, SENSOR_RCI_PORT); 53 | const struct sockaddr * GPS_RCI_ADDR = make_addr(GPS_IP, GPS_RCI_PORT); 54 | const struct sockaddr * ROLL_RCI_ADDR = make_addr(ROLL_IP, ROLL_RCI_PORT); 55 | 56 | const struct sockaddr * RNH_BATTERY_ADDR = make_addr(RNH_IP, RNH_BATTERY); 57 | const struct sockaddr * RNH_PORT_ADDR = make_addr(RNH_IP, RNH_PORT); 58 | const struct sockaddr * RNH_ALARM_ADDR = make_addr(RNH_IP, RNH_ALARM); 59 | const struct sockaddr * RNH_UMBDET_ADDR = make_addr(RNH_IP, RNH_UMBDET); 60 | 61 | const struct sockaddr * FC_ADDR = make_addr(FC_IP, FC_LISTEN_PORT); 62 | 63 | const struct sockaddr * ADIS_ADDR = make_addr(SENSOR_IP, ADIS_PORT); 64 | const struct sockaddr * MPU_ADDR = make_addr(SENSOR_IP, MPU_PORT); 65 | const struct sockaddr * MPL_ADDR = make_addr(SENSOR_IP, MPL_PORT); 66 | const struct sockaddr * BMP_ADDR = make_addr(SENSOR_IP, BMP_PORT); 67 | 68 | const struct sockaddr * ROLL_ADDR = make_addr(ROLL_IP, ROLL_PORT); 69 | 70 | const struct sockaddr * WIFI_ADDR = make_addr(WIFI_IP, WIFI_PORT); 71 | 72 | const struct sockaddr * ARM_ADDR = make_addr(ARM_IP, ARM_PORT); 73 | const struct sockaddr * RC_SERVO_ENABLE_ADDR = make_addr(ARM_IP, RC_SERVO_ENABLE_PORT); 74 | 75 | const struct sockaddr * GPS_COTS_ADDR = make_addr(GPS_IP, GPS_COTS); 76 | 77 | -------------------------------------------------------------------------------- /src/utilities/net_addrs.h: -------------------------------------------------------------------------------- 1 | /* Canonical definition of device addresses on the RocketNet 2 | * This file should be synched across all projects that want 3 | * to connect to the RocketNet 4 | */ 5 | #ifndef NET_ADDRS_H_ 6 | #define NET_ADDRS_H_ 7 | #include 8 | 9 | #ifdef FCF_FC_NETWORK 10 | #define RNH_IP IPv4(10,10,10,5) 11 | #define FC_IP IPv4(10,10,10,10) 12 | #define SENSOR_IP IPv4(10,10,10,20) 13 | #define ROLL_IP IPv4(10,10,10,30) 14 | #define GPS_IP IPv4(10,10,10,40) 15 | #define WIFI_IP IPv4(172,17,0,2) 16 | 17 | #else 18 | #define RNH_IP IPv4(127,0,0,1) 19 | #define FC_IP IPv4(127,0,0,1) 20 | #define SENSOR_IP IPv4(127,0,0,1) 21 | #define ROLL_IP IPv4(127,0,0,1) 22 | #define GPS_IP IPv4(127,0,0,1) 23 | #define WIFI_IP IPv4(127,0,0,1) 24 | #endif 25 | 26 | 27 | /* RCI */ 28 | #define RCI_PORT 23 29 | extern const struct sockaddr * RCI_ADDR; 30 | extern const struct sockaddr * RNH_RCI_ADDR; 31 | extern const struct sockaddr * SENSOR_RCI_ADDR; 32 | extern const struct sockaddr * GPS_RCI_ADDR; 33 | extern const struct sockaddr * ROLL_RCI_ADDR; 34 | 35 | /* Rocket Net Hub */ 36 | #define RNH_BATTERY 36101 // Battery data 37 | #define RNH_PORT 36102 // Port data 38 | #define RNH_ALARM 36103 // Battery alarm 39 | #define RNH_UMBDET 36104 // Umbilical detect 40 | extern const struct sockaddr * RNH_BATTERY_ADDR; 41 | extern const struct sockaddr * RNH_PORT_ADDR; 42 | extern const struct sockaddr * RNH_ALARM_ADDR; 43 | extern const struct sockaddr * RNH_UMBDET_ADDR; 44 | 45 | /* Flight Computer */ 46 | #define FC_LISTEN_PORT 36000 // FC device listener 47 | #define FCF_HEALTH_PORT 36201 // FC health monitor 48 | extern const struct sockaddr * FC_ADDR; 49 | 50 | /* Sensor Node */ 51 | #define ADIS_PORT 35020 // ADIS16405 52 | #define MPU_PORT 35002 // MPU1950 53 | #define MPL_PORT 35010 // MPL3115A2 54 | #define BMP_PORT 35011 // BMP180 55 | extern const struct sockaddr * ADIS_ADDR; 56 | extern const struct sockaddr * MPU_ADDR; 57 | extern const struct sockaddr * MPL_ADDR; 58 | extern const struct sockaddr * BMP_ADDR; 59 | 60 | /* Roll Control */ 61 | #define ROLL_PORT 35003 // Servo control 62 | extern const struct sockaddr * ROLL_ADDR; 63 | 64 | /* WiFi */ 65 | #define WIFI_PORT 35001 66 | extern const struct sockaddr * WIFI_ADDR; 67 | 68 | /* ARM Signal */ 69 | #define ARM_IP IPv4(127,0,0,1) 70 | #define ARM_PORT 35666 71 | #define RC_SERVO_ENABLE_PORT 35667 72 | extern const struct sockaddr * ARM_ADDR; 73 | extern const struct sockaddr * RC_SERVO_ENABLE_ADDR; 74 | 75 | #define JGPS_PORT 35050 76 | 77 | #define GPS_COTS 35051 78 | extern const struct sockaddr * GPS_COTS_ADDR; 79 | 80 | #endif /* NET_ADDRS_H_ */ 81 | -------------------------------------------------------------------------------- /src/utilities/net_addrs.miml: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | include: net_addrs.h 4 | object: src/utilities/net_addrs.o 5 | -------------------------------------------------------------------------------- /src/utilities/psas_packet.h: -------------------------------------------------------------------------------- 1 | /** 2 | * DO NOT EDIT THIS FILE 3 | * 4 | * It is automatically generated by 5 | * See documentation: 6 | */ 7 | 8 | #ifndef _PSAS_PACKET_H 9 | #define _PSAS_PACKET_H 10 | #include 11 | 12 | /*! \typedef 13 | * ADIS16405 Data 14 | */ 15 | typedef struct { 16 | uint16_t vcc; 17 | int16_t gyro_x; 18 | int16_t gyro_y; 19 | int16_t gyro_z; 20 | int16_t acc_x; 21 | int16_t acc_y; 22 | int16_t acc_z; 23 | int16_t magn_x; 24 | int16_t magn_y; 25 | int16_t magn_z; 26 | int16_t temp; 27 | uint16_t aux_adc; 28 | } __attribute__((packed)) ADIS16405Data; 29 | 30 | typedef struct { 31 | char ID[4]; 32 | uint8_t timestamp[6]; 33 | uint16_t data_length; 34 | ADIS16405Data data; 35 | } __attribute__((packed)) ADISMessage; 36 | 37 | 38 | /*! \typedef 39 | * FCFHealth Data 40 | */ 41 | typedef struct { 42 | float cpu_user; 43 | float cpu_system; 44 | float cpu_nice; 45 | float cpu_iowait; 46 | float cpu_irq; 47 | float cpu_softirq; 48 | uint64_t ram_used; 49 | uint64_t ram_buffer; 50 | uint64_t ram_cached; 51 | uint16_t pid; 52 | uint64_t disk_used; 53 | uint64_t disk_read; 54 | uint64_t disk_write; 55 | uint32_t io_lo_bytes_sent; 56 | uint32_t io_lo_bytes_recv; 57 | uint32_t io_lo_packets_sent; 58 | uint32_t io_lo_packets_recv; 59 | uint32_t io_eth0_bytes_sent; 60 | uint32_t io_eth0_bytes_recv; 61 | uint32_t io_eth0_packets_sent; 62 | uint32_t io_eth0_packets_recv; 63 | uint32_t io_wlan0_bytes_sent; 64 | uint32_t io_wlan0_bytes_recv; 65 | uint32_t io_wlan0_packets_sent; 66 | uint32_t io_wlan0_packets_recv; 67 | uint16_t core_temp; 68 | } __attribute__((packed)) FCFHealthData; 69 | 70 | typedef struct { 71 | char ID[4]; 72 | uint8_t timestamp[6]; 73 | uint16_t data_length; 74 | FCFHealthData data; 75 | } __attribute__((packed)) FCFHMessage; 76 | 77 | 78 | /*! \typedef 79 | * GPSFix Data 80 | */ 81 | typedef struct { 82 | uint8_t age_of_diff; 83 | uint8_t num_of_sats; 84 | uint16_t gps_week; 85 | double gps_time_of_week; 86 | double latitude; 87 | double longitude; 88 | float height; 89 | float vnorth; 90 | float veast; 91 | float vup; 92 | float std_dev_resid; 93 | uint16_t nav_mode; 94 | uint16_t extended_age_of_diff; 95 | } __attribute__((packed)) GPSFixData; 96 | 97 | typedef struct { 98 | char ID[4]; 99 | uint8_t timestamp[6]; 100 | uint16_t data_length; 101 | GPSFixData data; 102 | } __attribute__((packed)) GPS1Message; 103 | 104 | 105 | /*! \typedef 106 | * GPSFixQuality Data 107 | */ 108 | typedef struct { 109 | uint32_t mask_sats_tracked; 110 | uint32_t mask_sats_used; 111 | uint16_t gps_utc_diff; 112 | uint16_t hdop; 113 | uint16_t vdop; 114 | uint16_t mask_waas_prn; 115 | } __attribute__((packed)) GPSFixQualityData; 116 | 117 | typedef struct { 118 | char ID[4]; 119 | uint8_t timestamp[6]; 120 | uint16_t data_length; 121 | GPSFixQualityData data; 122 | } __attribute__((packed)) GPS2Message; 123 | 124 | 125 | /*! \typedef 126 | * GPSWAASMessage Data 127 | */ 128 | typedef struct { 129 | uint16_t prn; 130 | uint16_t spare; 131 | uint32_t msg_sec_of_week; 132 | char waas_msg[32]; 133 | } __attribute__((packed)) GPSWAASMessageData; 134 | 135 | typedef struct { 136 | char ID[4]; 137 | uint8_t timestamp[6]; 138 | uint16_t data_length; 139 | GPSWAASMessageData data; 140 | } __attribute__((packed)) GPS80Message; 141 | 142 | 143 | /*! \typedef 144 | * GPSWAASEphemeris Data 145 | */ 146 | typedef struct { 147 | uint16_t sv; 148 | uint16_t spare; 149 | uint32_t tow_sec_of_week; 150 | uint16_t iode; 151 | uint16_t ura; 152 | int32_t t_zero; 153 | int32_t xg; 154 | int32_t yg; 155 | int32_t zg; 156 | int32_t xg_dot; 157 | int32_t yg_dot; 158 | int32_t zg_dot; 159 | int32_t xg_dotdot; 160 | int32_t yg_dotdot; 161 | int32_t zg_dotdot; 162 | uint16_t gf_zero; 163 | uint16_t gf_zero_dot; 164 | } __attribute__((packed)) GPSWAASEphemerisData; 165 | 166 | typedef struct { 167 | char ID[4]; 168 | uint8_t timestamp[6]; 169 | uint16_t data_length; 170 | GPSWAASEphemerisData data; 171 | } __attribute__((packed)) GPS93Message; 172 | 173 | 174 | /*! \typedef 175 | * GPSIonosphereUTC Data 176 | */ 177 | typedef struct { 178 | double a0; 179 | double a1; 180 | double a2; 181 | double a3; 182 | double b0; 183 | double b1; 184 | double b2; 185 | double b3; 186 | double utc_a0; 187 | double utc_a1; 188 | uint32_t tot; 189 | uint16_t wnt; 190 | uint16_t wnlsf; 191 | uint16_t dn; 192 | uint16_t dtls; 193 | uint16_t dtlsf; 194 | uint16_t space; 195 | } __attribute__((packed)) GPSIonosphereUTCData; 196 | 197 | typedef struct { 198 | char ID[4]; 199 | uint8_t timestamp[6]; 200 | uint16_t data_length; 201 | GPSIonosphereUTCData data; 202 | } __attribute__((packed)) GPS94Message; 203 | 204 | 205 | /*! \typedef 206 | * GPSEphemeris Data 207 | */ 208 | typedef struct { 209 | uint16_t sv; 210 | uint16_t spare; 211 | uint32_t sec_of_week; 212 | char sf1_words[40]; 213 | char sf2_words[40]; 214 | char sf3_words[40]; 215 | } __attribute__((packed)) GPSEphemerisData; 216 | 217 | typedef struct { 218 | char ID[4]; 219 | uint8_t timestamp[6]; 220 | uint16_t data_length; 221 | GPSEphemerisData data; 222 | } __attribute__((packed)) GPS95Message; 223 | 224 | 225 | /*! \typedef 226 | * GPSPsudorange Data 227 | */ 228 | typedef struct { 229 | uint16_t spare; 230 | uint16_t week; 231 | double tow; 232 | uint32_t uics_tt_snr_prn_0; 233 | uint32_t uics_tt_snr_prn_1; 234 | uint32_t uics_tt_snr_prn_2; 235 | uint32_t uics_tt_snr_prn_3; 236 | uint32_t uics_tt_snr_prn_4; 237 | uint32_t uics_tt_snr_prn_5; 238 | uint32_t uics_tt_snr_prn_6; 239 | uint32_t uics_tt_snr_prn_7; 240 | uint32_t uics_tt_snr_prn_8; 241 | uint32_t uics_tt_snr_prn_9; 242 | uint32_t uics_tt_snr_prn_10; 243 | uint32_t uics_tt_snr_prn_11; 244 | uint32_t uidoppler_fl_0; 245 | uint32_t uidoppler_fl_1; 246 | uint32_t uidoppler_fl_2; 247 | uint32_t uidoppler_fl_3; 248 | uint32_t uidoppler_fl_4; 249 | uint32_t uidoppler_fl_5; 250 | uint32_t uidoppler_fl_6; 251 | uint32_t uidoppler_fl_7; 252 | uint32_t uidoppler_fl_8; 253 | uint32_t uidoppler_fl_9; 254 | uint32_t uidoppler_fl_10; 255 | uint32_t uidoppler_fl_11; 256 | double pseudorange_0; 257 | double pseudorange_1; 258 | double pseudorange_2; 259 | double pseudorange_3; 260 | double pseudorange_4; 261 | double pseudorange_5; 262 | double pseudorange_6; 263 | double pseudorange_7; 264 | double pseudorange_8; 265 | double pseudorange_9; 266 | double pseudorange_10; 267 | double pseudorange_11; 268 | double phase_0; 269 | double phase_1; 270 | double phase_2; 271 | double phase_3; 272 | double phase_4; 273 | double phase_5; 274 | double phase_6; 275 | double phase_7; 276 | double phase_8; 277 | double phase_9; 278 | double phase_10; 279 | double phase_11; 280 | } __attribute__((packed)) GPSPsudorangeData; 281 | 282 | typedef struct { 283 | char ID[4]; 284 | uint8_t timestamp[6]; 285 | uint16_t data_length; 286 | GPSPsudorangeData data; 287 | } __attribute__((packed)) GPS96Message; 288 | 289 | 290 | /*! \typedef 291 | * GPSProcessor Data 292 | */ 293 | typedef struct { 294 | uint32_t cpu_availible; 295 | uint16_t missed_sub_frames; 296 | uint16_t max_subframe_queued; 297 | uint16_t missed_accum; 298 | uint16_t missed_meas; 299 | uint32_t spare1; 300 | uint32_t spare2; 301 | uint32_t spare3; 302 | uint16_t spare4; 303 | uint16_t spare5; 304 | } __attribute__((packed)) GPSProcessorData; 305 | 306 | typedef struct { 307 | char ID[4]; 308 | uint8_t timestamp[6]; 309 | uint16_t data_length; 310 | GPSProcessorData data; 311 | } __attribute__((packed)) GPS97Message; 312 | 313 | 314 | /*! \typedef 315 | * GPSAlmanac Data 316 | */ 317 | typedef struct { 318 | char alman_data[64]; 319 | uint8_t last_alman; 320 | uint8_t ionoutcv_flag; 321 | uint16_t spare; 322 | } __attribute__((packed)) GPSAlmanacData; 323 | 324 | typedef struct { 325 | char ID[4]; 326 | uint8_t timestamp[6]; 327 | uint16_t data_length; 328 | GPSAlmanacData data; 329 | } __attribute__((packed)) GPS98Message; 330 | 331 | 332 | /*! \typedef 333 | * GPSSatellite Data 334 | */ 335 | typedef struct { 336 | uint8_t nav_mode_2; 337 | uint8_t utc_time_diff; 338 | uint16_t gps_week; 339 | double gps_time_of_week; 340 | uint8_t channel_0; 341 | uint8_t tracked_0; 342 | uint8_t status_0; 343 | uint8_t last_subframe_0; 344 | uint8_t ephm_v_flag_0; 345 | uint8_t ephm_health_0; 346 | uint8_t alm_v_flag_0; 347 | uint8_t alm_health_0; 348 | int8_t elev_angle_0; 349 | uint8_t azimuth_angle_0; 350 | uint8_t ura_0; 351 | uint8_t spare_0; 352 | uint16_t cli_for_snr_0; 353 | int16_t diffcorr_0; 354 | int16_t pos_resid_0; 355 | int16_t vel_resid_0; 356 | int16_t dopplr_0; 357 | int16_t n_carr_offset_0; 358 | uint8_t channel_1; 359 | uint8_t tracked_1; 360 | uint8_t status_1; 361 | uint8_t last_subframe_1; 362 | uint8_t ephm_v_flag_1; 363 | uint8_t ephm_health_1; 364 | uint8_t alm_v_flag_1; 365 | uint8_t alm_health_1; 366 | int8_t elev_angle_1; 367 | uint8_t azimuth_angle_1; 368 | uint8_t ura_1; 369 | uint8_t spare_1; 370 | uint16_t cli_for_snr_1; 371 | int16_t diffcorr_1; 372 | int16_t pos_resid_1; 373 | int16_t vel_resid_1; 374 | int16_t dopplr_1; 375 | int16_t n_carr_offset_1; 376 | uint8_t channel_2; 377 | uint8_t tracked_2; 378 | uint8_t status_2; 379 | uint8_t last_subframe_2; 380 | uint8_t ephm_v_flag_2; 381 | uint8_t ephm_health_2; 382 | uint8_t alm_v_flag_2; 383 | uint8_t alm_health_2; 384 | int8_t elev_angle_2; 385 | uint8_t azimuth_angle_2; 386 | uint8_t ura_2; 387 | uint8_t spare_2; 388 | uint16_t cli_for_snr_2; 389 | int16_t diffcorr_2; 390 | int16_t pos_resid_2; 391 | int16_t vel_resid_2; 392 | int16_t dopplr_2; 393 | int16_t n_carr_offset_2; 394 | uint8_t channel_3; 395 | uint8_t tracked_3; 396 | uint8_t status_3; 397 | uint8_t last_subframe_3; 398 | uint8_t ephm_v_flag_3; 399 | uint8_t ephm_health_3; 400 | uint8_t alm_v_flag_3; 401 | uint8_t alm_health_3; 402 | int8_t elev_angle_3; 403 | uint8_t azimuth_angle_3; 404 | uint8_t ura_3; 405 | uint8_t spare_3; 406 | uint16_t cli_for_snr_3; 407 | int16_t diffcorr_3; 408 | int16_t pos_resid_3; 409 | int16_t vel_resid_3; 410 | int16_t dopplr_3; 411 | int16_t n_carr_offset_3; 412 | uint8_t channel_4; 413 | uint8_t tracked_4; 414 | uint8_t status_4; 415 | uint8_t last_subframe_4; 416 | uint8_t ephm_v_flag_4; 417 | uint8_t ephm_health_4; 418 | uint8_t alm_v_flag_4; 419 | uint8_t alm_health_4; 420 | int8_t elev_angle_4; 421 | uint8_t azimuth_angle_4; 422 | uint8_t ura_4; 423 | uint8_t spare_4; 424 | uint16_t cli_for_snr_4; 425 | int16_t diffcorr_4; 426 | int16_t pos_resid_4; 427 | int16_t vel_resid_4; 428 | int16_t dopplr_4; 429 | int16_t n_carr_offset_4; 430 | uint8_t channel_5; 431 | uint8_t tracked_5; 432 | uint8_t status_5; 433 | uint8_t last_subframe_5; 434 | uint8_t ephm_v_flag_5; 435 | uint8_t ephm_health_5; 436 | uint8_t alm_v_flag_5; 437 | uint8_t alm_health_5; 438 | int8_t elev_angle_5; 439 | uint8_t azimuth_angle_5; 440 | uint8_t ura_5; 441 | uint8_t spare_5; 442 | uint16_t cli_for_snr_5; 443 | int16_t diffcorr_5; 444 | int16_t pos_resid_5; 445 | int16_t vel_resid_5; 446 | int16_t dopplr_5; 447 | int16_t n_carr_offset_5; 448 | uint8_t channel_6; 449 | uint8_t tracked_6; 450 | uint8_t status_6; 451 | uint8_t last_subframe_6; 452 | uint8_t ephm_v_flag_6; 453 | uint8_t ephm_health_6; 454 | uint8_t alm_v_flag_6; 455 | uint8_t alm_health_6; 456 | int8_t elev_angle_6; 457 | uint8_t azimuth_angle_6; 458 | uint8_t ura_6; 459 | uint8_t spare_6; 460 | uint16_t cli_for_snr_6; 461 | int16_t diffcorr_6; 462 | int16_t pos_resid_6; 463 | int16_t vel_resid_6; 464 | int16_t dopplr_6; 465 | int16_t n_carr_offset_6; 466 | uint8_t channel_7; 467 | uint8_t tracked_7; 468 | uint8_t status_7; 469 | uint8_t last_subframe_7; 470 | uint8_t ephm_v_flag_7; 471 | uint8_t ephm_health_7; 472 | uint8_t alm_v_flag_7; 473 | uint8_t alm_health_7; 474 | int8_t elev_angle_7; 475 | uint8_t azimuth_angle_7; 476 | uint8_t ura_7; 477 | uint8_t spare_7; 478 | uint16_t cli_for_snr_7; 479 | int16_t diffcorr_7; 480 | int16_t pos_resid_7; 481 | int16_t vel_resid_7; 482 | int16_t dopplr_7; 483 | int16_t n_carr_offset_7; 484 | uint8_t channel_8; 485 | uint8_t tracked_8; 486 | uint8_t status_8; 487 | uint8_t last_subframe_8; 488 | uint8_t ephm_v_flag_8; 489 | uint8_t ephm_health_8; 490 | uint8_t alm_v_flag_8; 491 | uint8_t alm_health_8; 492 | int8_t elev_angle_8; 493 | uint8_t azimuth_angle_8; 494 | uint8_t ura_8; 495 | uint8_t spare_8; 496 | uint16_t cli_for_snr_8; 497 | int16_t diffcorr_8; 498 | int16_t pos_resid_8; 499 | int16_t vel_resid_8; 500 | int16_t dopplr_8; 501 | int16_t n_carr_offset_8; 502 | uint8_t channel_9; 503 | uint8_t tracked_9; 504 | uint8_t status_9; 505 | uint8_t last_subframe_9; 506 | uint8_t ephm_v_flag_9; 507 | uint8_t ephm_health_9; 508 | uint8_t alm_v_flag_9; 509 | uint8_t alm_health_9; 510 | int8_t elev_angle_9; 511 | uint8_t azimuth_angle_9; 512 | uint8_t ura_9; 513 | uint8_t spare_9; 514 | uint16_t cli_for_snr_9; 515 | int16_t diffcorr_9; 516 | int16_t pos_resid_9; 517 | int16_t vel_resid_9; 518 | int16_t dopplr_9; 519 | int16_t n_carr_offset_9; 520 | uint8_t channel_10; 521 | uint8_t tracked_10; 522 | uint8_t status_10; 523 | uint8_t last_subframe_10; 524 | uint8_t ephm_v_flag_10; 525 | uint8_t ephm_health_10; 526 | uint8_t alm_v_flag_10; 527 | uint8_t alm_health_10; 528 | int8_t elev_angle_10; 529 | uint8_t azimuth_angle_10; 530 | uint8_t ura_10; 531 | uint8_t spare_10; 532 | uint16_t cli_for_snr_10; 533 | int16_t diffcorr_10; 534 | int16_t pos_resid_10; 535 | int16_t vel_resid_10; 536 | int16_t dopplr_10; 537 | int16_t n_carr_offset_10; 538 | uint8_t channel_11; 539 | uint8_t tracked_11; 540 | uint8_t status_11; 541 | uint8_t last_subframe_11; 542 | uint8_t ephm_v_flag_11; 543 | uint8_t ephm_health_11; 544 | uint8_t alm_v_flag_11; 545 | uint8_t alm_health_11; 546 | int8_t elev_angle_11; 547 | uint8_t azimuth_angle_11; 548 | uint8_t ura_11; 549 | uint8_t spare_11; 550 | uint16_t cli_for_snr_11; 551 | int16_t diffcorr_11; 552 | int16_t pos_resid_11; 553 | int16_t vel_resid_11; 554 | int16_t dopplr_11; 555 | int16_t n_carr_offset_11; 556 | int16_t clock_err_l1; 557 | uint16_t spare; 558 | } __attribute__((packed)) GPSSatelliteData; 559 | 560 | typedef struct { 561 | char ID[4]; 562 | uint8_t timestamp[6]; 563 | uint16_t data_length; 564 | GPSSatelliteData data; 565 | } __attribute__((packed)) GPS99Message; 566 | 567 | 568 | /*! \typedef 569 | * LaunchTowerComputer Data 570 | */ 571 | typedef struct { 572 | float rocket_ready; 573 | uint8_t iginition_relay; 574 | float ignition_battery; 575 | uint8_t shore_power_relay; 576 | float shore_power; 577 | float solar_voltage; 578 | float system_battery; 579 | float internal_temp; 580 | float external_temp; 581 | float humidity; 582 | float wind_speed; 583 | float wind_direction; 584 | float barometric_pressure; 585 | } __attribute__((packed)) LaunchTowerComputerData; 586 | 587 | typedef struct { 588 | char ID[4]; 589 | uint8_t timestamp[6]; 590 | uint16_t data_length; 591 | LaunchTowerComputerData data; 592 | } __attribute__((packed)) LTCHMessage; 593 | 594 | 595 | /*! \typedef 596 | * MPL3115A2 Data 597 | */ 598 | typedef struct { 599 | uint32_t pressure; 600 | int16_t temp; 601 | } __attribute__((packed)) MPL3115A2Data; 602 | 603 | typedef struct { 604 | char ID[4]; 605 | uint8_t timestamp[6]; 606 | uint16_t data_length; 607 | MPL3115A2Data data; 608 | } __attribute__((packed)) MPL3Message; 609 | 610 | /*! \typedef 611 | * BMP180 Data 612 | */ 613 | typedef struct { 614 | uint32_t pressure; 615 | uint16_t temp; 616 | } __attribute__((packed)) BMP180Data; 617 | 618 | typedef struct { 619 | char ID[4]; 620 | uint8_t timestamp[6]; 621 | uint16_t data_length; 622 | BMP180Data data; 623 | } __attribute__((packed)) BMP1Message; 624 | 625 | 626 | /*! \typedef 627 | * RNHHealth Data 628 | */ 629 | typedef struct { 630 | uint16_t temperature; 631 | int16_t ts1temperature; 632 | int16_t ts2temperature; 633 | uint16_t temprange; 634 | uint16_t voltage; 635 | int16_t current; 636 | int16_t averagecurrent; 637 | uint16_t cellvoltage1; 638 | uint16_t cellvoltage2; 639 | uint16_t cellvoltage3; 640 | uint16_t cellvoltage4; 641 | uint16_t packvoltage; 642 | uint16_t averagevoltage; 643 | } __attribute__((packed)) RNHHealthData; 644 | 645 | typedef struct { 646 | char ID[4]; 647 | uint8_t timestamp[6]; 648 | uint16_t data_length; 649 | RNHHealthData data; 650 | } __attribute__((packed)) RNHHMessage; 651 | 652 | 653 | /*! \typedef 654 | * RNHPower Data 655 | */ 656 | typedef struct { 657 | uint16_t port1; 658 | uint16_t port2; 659 | uint16_t port3; 660 | uint16_t port4; 661 | uint16_t umbilical; 662 | uint16_t port6; 663 | uint16_t port7; 664 | uint16_t port8; 665 | } __attribute__((packed)) RNHPowerData; 666 | 667 | typedef struct { 668 | char ID[4]; 669 | uint8_t timestamp[6]; 670 | uint16_t data_length; 671 | RNHPowerData data; 672 | } __attribute__((packed)) RNHPMessage; 673 | 674 | 675 | /*! \typedef 676 | * RNHUmbilical Data 677 | */ 678 | typedef struct { 679 | uint8_t detect; 680 | } __attribute__((packed)) RNHUmbilicalData; 681 | 682 | typedef struct { 683 | char ID[4]; 684 | uint8_t timestamp[6]; 685 | uint16_t data_length; 686 | RNHUmbilicalData data; 687 | } __attribute__((packed)) RNHUMessage; 688 | 689 | 690 | /*! \typedef 691 | * RollServo Data 692 | */ 693 | typedef struct { 694 | double angle; 695 | uint8_t disable; 696 | } __attribute__((packed)) RollServoData; 697 | 698 | typedef struct { 699 | char ID[4]; 700 | uint8_t timestamp[6]; 701 | uint16_t data_length; 702 | RollServoData data; 703 | } __attribute__((packed)) ROLLMessage; 704 | 705 | 706 | /*! \typedef 707 | * SequenceError Data 708 | */ 709 | typedef struct { 710 | uint16_t port; 711 | uint32_t expected; 712 | uint32_t received; 713 | } __attribute__((packed)) SequenceErrorData; 714 | 715 | typedef struct { 716 | char ID[4]; 717 | uint8_t timestamp[6]; 718 | uint16_t data_length; 719 | SequenceErrorData data; 720 | } __attribute__((packed)) SEQEMessage; 721 | 722 | 723 | /*! \typedef 724 | * SequenceNo Data 725 | */ 726 | typedef struct { 727 | uint32_t sequence; 728 | } __attribute__((packed)) SequenceNoData; 729 | 730 | typedef struct { 731 | char ID[4]; 732 | uint8_t timestamp[6]; 733 | uint16_t data_length; 734 | SequenceNoData data; 735 | } __attribute__((packed)) SEQNMessage; 736 | 737 | 738 | /*! \typedef 739 | * Venus8Navigation Data 740 | */ 741 | typedef struct { 742 | uint8_t fix_mode; 743 | uint8_t num_sv; 744 | uint16_t gps_week; 745 | uint32_t tow; 746 | int32_t latitude; 747 | int32_t longitude; 748 | uint32_t ellipsoid_altitude; 749 | uint32_t msl_altitude; 750 | uint16_t gdop; 751 | uint16_t pdop; 752 | uint16_t hdop; 753 | uint16_t vdop; 754 | uint16_t tdop; 755 | int32_t ecef_x; 756 | int32_t ecef_y; 757 | int32_t ecef_z; 758 | int32_t ecef_vx; 759 | int32_t ecef_vy; 760 | int32_t ecef_vz; 761 | } __attribute__((packed)) Venus8NavigationData; 762 | 763 | typedef struct { 764 | char ID[4]; 765 | uint8_t timestamp[6]; 766 | uint16_t data_length; 767 | Venus8NavigationData data; 768 | } __attribute__((packed)) V8A8Message; 769 | 770 | 771 | /*! \typedef 772 | * State Data 773 | */ 774 | typedef struct { 775 | double time; 776 | double acc_up; 777 | double vel_up; 778 | double altitude; 779 | double roll_rate; 780 | double roll_angle; 781 | } __attribute__((packed)) StateData; 782 | 783 | typedef struct { 784 | char ID[4]; 785 | uint8_t timestamp[6]; 786 | uint16_t data_length; 787 | StateData data; 788 | } __attribute__((packed)) VSTEMessage; 789 | 790 | 791 | 792 | #endif 793 | -------------------------------------------------------------------------------- /src/utilities/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H_ 2 | #define UTILS_H_ 3 | 4 | // It's gonna be clamp this clamp that, badda climp badda clamp, they won't know what clamped them! 5 | #define CLAMP(value, lower_bound, upper_bound) \ 6 | ((value) < (lower_bound)? (lower_bound) : ((value) > (upper_bound) ? (upper_bound) : (value))) 7 | 8 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 9 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 10 | 11 | 12 | #endif /* UTILS_H_ */ 13 | -------------------------------------------------------------------------------- /src/utilities/utils_sockets.c: -------------------------------------------------------------------------------- 1 | /** starting point for the socket code was: 2 | * http://publib.boulder.ibm.com/infocenter/iseries/v6r1m0/index.jsp?topic=/rzab6/poll.htm 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "utilities/utils_sockets.h" 21 | 22 | 23 | int readsocket(int fd, unsigned char *buffer, int bufsize) { 24 | /** 25 | * Receive data on this connection until the 26 | * recv fails with EWOULDBLOCK. If any other 27 | * failure occurs, we will close the 28 | * connection. 29 | */ 30 | int rc = recv(fd, buffer, bufsize, 0); 31 | if (rc < 0){ 32 | if (errno != EWOULDBLOCK){ 33 | perror("readsocket: recv() failed"); 34 | return -2; 35 | } 36 | return 0; 37 | } 38 | 39 | /** 40 | * Check to see if the connection has been 41 | * closed by the client 42 | */ 43 | if (rc == 0){ 44 | return -1; 45 | } 46 | 47 | // Data was received 48 | //printf(" %d bytes received\n", rc); 49 | 50 | return rc; 51 | } 52 | 53 | int readsocketfrom(int fd, unsigned char *buffer, int bufsize, struct sockaddr *sender, socklen_t *addrlen) { 54 | /** 55 | * Receive data packet on this socket, 56 | * and also return the sender's address. 57 | */ 58 | int rc = recvfrom(fd, buffer, bufsize, 0, sender, addrlen); 59 | if (rc < 0){ 60 | if (errno != EWOULDBLOCK){ 61 | perror("readsocket: recv() failed"); 62 | return -2; 63 | } 64 | return 0; 65 | } 66 | 67 | /** 68 | * Check to see if the connection has been 69 | * closed by the client 70 | */ 71 | if (rc == 0){ 72 | return -1; 73 | } 74 | 75 | return rc; 76 | } 77 | 78 | 79 | int readsocketfromts(int fd, unsigned char *buffer, int bufsize, struct sockaddr_in *sender, socklen_t addrlen, struct timespec *ts){ 80 | struct msghdr msg; 81 | struct cmsghdr *cmsg; 82 | struct iovec entry; 83 | struct { 84 | struct cmsghdr cm; 85 | char control[512]; 86 | } control; 87 | 88 | memset(&msg, 0, sizeof(msg)); 89 | msg.msg_iov = &entry; 90 | msg.msg_iovlen = 1; 91 | entry.iov_base = buffer; 92 | entry.iov_len = bufsize; 93 | msg.msg_name = (caddr_t)sender; 94 | msg.msg_namelen = addrlen; 95 | msg.msg_control = &control; 96 | msg.msg_controllen = sizeof(control); 97 | 98 | int rc = recvmsg(fd, &msg, 0); 99 | if (rc < 0){ 100 | if (errno != EWOULDBLOCK){ 101 | perror("readsocketfromts: recvmsg() failed"); 102 | return -2; 103 | } 104 | return 0; 105 | } 106 | 107 | /** 108 | * Check to see if the connection has been 109 | * closed by the client 110 | */ 111 | if (rc == 0){ 112 | fprintf(stderr, "readsocketfromts: Connection closed\n"); 113 | return -1; 114 | } 115 | 116 | /** Extract info from msg **/ 117 | //timestamp info 118 | for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)){ 119 | if(cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMPNS){ 120 | ts->tv_nsec = ((struct timespec *)CMSG_DATA(cmsg))->tv_nsec; 121 | ts->tv_sec = ((struct timespec *)CMSG_DATA(cmsg))->tv_sec; 122 | } 123 | } 124 | //data 125 | if(msg.msg_iovlen > 1){ 126 | printf("utils_sockets rsft: iovlen greater than 1\n"); 127 | } 128 | 129 | return rc; 130 | } 131 | 132 | int udp_socket(){ 133 | int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 134 | if (s < 0){ 135 | perror("udp_socket: socket() failed"); 136 | } 137 | 138 | return s; 139 | } 140 | 141 | int bound_udp_socket(int port) { 142 | int s = udp_socket(); 143 | if (s < 0){ 144 | return s; 145 | } 146 | 147 | struct sockaddr_in sin = {}; 148 | sin.sin_family = AF_INET; 149 | sin.sin_addr.s_addr = htonl(INADDR_ANY); 150 | sin.sin_port = htons(port); 151 | 152 | if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0){ 153 | perror("bound_udp_socket: bind() failed"); 154 | close(s); 155 | return -1; 156 | } 157 | 158 | return s; 159 | } 160 | 161 | int timestamped_bound_udp_socket(int port) { 162 | int s = bound_udp_socket(port); 163 | if (s < 0){ 164 | return s; 165 | } 166 | 167 | int opts = SOF_TIMESTAMPING_RX_HARDWARE 168 | | SOF_TIMESTAMPING_RX_SOFTWARE 169 | | SOF_TIMESTAMPING_SYS_HARDWARE; 170 | if(setsockopt(s, SOL_SOCKET, SO_TIMESTAMPNS, &opts, sizeof(opts)) < 0){ 171 | perror("timestamped_bound_udp_socket: setsockopt failed"); 172 | close(s); 173 | return -1; 174 | } 175 | 176 | return s; 177 | } 178 | -------------------------------------------------------------------------------- /src/utilities/utils_sockets.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Utilites for reducing boilerplate when dealing with sockets 3 | */ 4 | 5 | #ifndef NETUTILS_H_ 6 | #define NETUTILS_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #define ETH_MTU 1500 13 | 14 | int readsocket(int fd, unsigned char *buffer, int bufsize); 15 | int readsocketfrom(int fd, unsigned char *buffer, int bufsize, struct sockaddr *src, socklen_t *addrlen); 16 | int readsocketfromts(int fd, unsigned char *buffer, int bufsize, struct sockaddr_in *sender, socklen_t addrlen, struct timespec *); 17 | int sendto_socket(int sd, const char *buffer, int bufsize, const char *dest_ip, int dest_port); 18 | int udp_socket(); 19 | int bound_udp_socket(int port); 20 | int timestamped_bound_udp_socket(int port); 21 | 22 | #endif /* NETUTILS_H_ */ 23 | -------------------------------------------------------------------------------- /src/utilities/utils_sockets.miml: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | include: utils_sockets.h 4 | object: src/utilities/utils_sockets.o 5 | -------------------------------------------------------------------------------- /src/utilities/utils_time.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "utilities/utils_time.h" 4 | 5 | struct timespec starttime; // TODO: timer module 6 | 7 | void utils_time_init(){ 8 | clock_gettime(CLOCK_MONOTONIC, &starttime); 9 | } 10 | 11 | void to_psas_time(struct timespec* ts, unsigned char* out){ 12 | ts->tv_sec -= starttime.tv_sec; 13 | ts->tv_nsec -= starttime.tv_nsec; 14 | uint64_t now = ts->tv_nsec + (uint64_t) ts->tv_sec * 1000000000; 15 | 16 | out[0] = now >> 40; 17 | out[1] = now >> 32; 18 | out[2] = now >> 24; 19 | out[3] = now >> 16; 20 | out[4] = now >> 8; 21 | out[5] = now; 22 | } 23 | 24 | // nanoseconds, for comparison purposes 25 | uint64_t from_psas_time(const unsigned char* in) 26 | { 27 | uint64_t time = in[0]; 28 | time = (time << 8) | in[1]; 29 | time = (time << 8) | in[2]; 30 | time = (time << 8) | in[3]; 31 | time = (time << 8) | in[4]; 32 | time = (time << 8) | in[5]; 33 | return time; 34 | } 35 | 36 | void get_psas_time(unsigned char* out){ 37 | struct timespec ts; 38 | clock_gettime(CLOCK_MONOTONIC, &ts); 39 | to_psas_time(&ts, out); 40 | } 41 | 42 | -------------------------------------------------------------------------------- /src/utilities/utils_time.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Utilities for converting to and from the PSAS time format 3 | * 4 | * The PSAS time format is 6 bytes long number in nanoseconds 5 | */ 6 | 7 | #ifndef UTILS_TIME_H_ 8 | #define UTILS_TIME_H_ 9 | 10 | #include 11 | #include 12 | 13 | void utils_time_init(void); 14 | 15 | void to_psas_time(struct timespec* ts, unsigned char* out); 16 | uint64_t from_psas_time(const unsigned char* in); 17 | void get_psas_time(unsigned char* out); 18 | #endif /* UTILS_TIME_H_ */ 19 | -------------------------------------------------------------------------------- /src/utilities/utils_time.miml: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | include: utils_time.h 4 | object: src/utilities/utils_time.o 5 | init: utils_time_init(); 6 | --------------------------------------------------------------------------------