├── .github ├── CODEOWNERS └── workflows │ └── full_build.yaml ├── .gitignore ├── .mailmap ├── .pre-commit-config.yaml ├── .pylintrc ├── .python-version ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.txt ├── MANIFEST.in ├── README.rst ├── ait ├── core │ ├── __init__.py │ ├── api.py │ ├── bin │ │ ├── __init__.py │ │ ├── ait_bsc.py │ │ ├── ait_bsc_create_handler.py │ │ ├── ait_bsc_stop_handler.py │ │ ├── ait_ccsds_send_example.py │ │ ├── ait_cmd_hist.py │ │ ├── ait_cmd_send.py │ │ ├── ait_create_dirs.py │ │ ├── ait_dict_writer.py │ │ ├── ait_limits_find_dn.py │ │ ├── ait_mps_seq_convert.py │ │ ├── ait_pcap.py │ │ ├── ait_pcap_segment.py │ │ ├── ait_seq_decode.py │ │ ├── ait_seq_encode.py │ │ ├── ait_seq_print.py │ │ ├── ait_seq_send.py │ │ ├── ait_server.py │ │ ├── ait_table_decode.py │ │ ├── ait_table_encode.py │ │ ├── ait_tlm_csv.py │ │ ├── ait_tlm_db_insert.py │ │ ├── ait_tlm_send.py │ │ ├── ait_tlm_simulate.py │ │ └── ait_yaml_validate.py │ ├── bsc.py │ ├── ccsds.py │ ├── cfg.py │ ├── cmd.py │ ├── coord.py │ ├── data │ │ ├── cmd_schema.json │ │ ├── evr_schema.json │ │ ├── limits_schema.json │ │ ├── table_schema.json │ │ └── tlm_schema.json │ ├── db.py │ ├── dmc.py │ ├── dtype.py │ ├── evr.py │ ├── gds.py │ ├── geom.py │ ├── json.py │ ├── limits.py │ ├── log.py │ ├── notify.py │ ├── pcap.py │ ├── py.typed │ ├── seq.py │ ├── server │ │ ├── __init__.py │ │ ├── broker.py │ │ ├── client.py │ │ ├── config.py │ │ ├── handler.py │ │ ├── handlers │ │ │ ├── __init__.py │ │ │ ├── ccsds_packet_handler.py │ │ │ └── packet_handler.py │ │ ├── plugin.py │ │ ├── plugins │ │ │ ├── PacketAccumulator.py │ │ │ ├── PacketPadder.py │ │ │ ├── __init__.py │ │ │ ├── apid_routing.py │ │ │ ├── data_archive.py │ │ │ ├── limit_monitor.py │ │ │ └── openmct.py │ │ ├── process.py │ │ ├── serial.py │ │ ├── server.py │ │ ├── stream.py │ │ └── utils.py │ ├── table.py │ ├── tlm.py │ ├── util.py │ └── val.py └── data │ └── settings.yaml ├── config ├── bsc.yaml ├── ccsds_header.yaml ├── cmd.yaml ├── config.yaml ├── core_set_op_mode.yaml ├── evr.yaml ├── leapseconds.dat ├── limits.yaml ├── table.yaml └── tlm.yaml ├── doc ├── Makefile ├── make.bat ├── requirements.txt └── source │ ├── _static │ └── ccsds_prim_header.png │ ├── ait.core.api.rst │ ├── ait.core.bin.ait_bsc.rst │ ├── ait.core.bin.ait_bsc_create_handler.rst │ ├── ait.core.bin.ait_bsc_stop_handler.rst │ ├── ait.core.bin.ait_ccsds_send_example.rst │ ├── ait.core.bin.ait_cmd_hist.rst │ ├── ait.core.bin.ait_cmd_send.rst │ ├── ait.core.bin.ait_create_dirs.rst │ ├── ait.core.bin.ait_dict_writer.rst │ ├── ait.core.bin.ait_limits_find_dn.rst │ ├── ait.core.bin.ait_mps_seq_convert.rst │ ├── ait.core.bin.ait_pcap.rst │ ├── ait.core.bin.ait_pcap_segment.rst │ ├── ait.core.bin.ait_seq_decode.rst │ ├── ait.core.bin.ait_seq_encode.rst │ ├── ait.core.bin.ait_seq_print.rst │ ├── ait.core.bin.ait_seq_send.rst │ ├── ait.core.bin.ait_server.rst │ ├── ait.core.bin.ait_table_decode.rst │ ├── ait.core.bin.ait_table_encode.rst │ ├── ait.core.bin.ait_tlm_csv.rst │ ├── ait.core.bin.ait_tlm_db_insert.rst │ ├── ait.core.bin.ait_tlm_send.rst │ ├── ait.core.bin.ait_tlm_simulate.rst │ ├── ait.core.bin.ait_yaml_validate.rst │ ├── ait.core.bin.rst │ ├── ait.core.bsc.rst │ ├── ait.core.ccsds.rst │ ├── ait.core.cfg.rst │ ├── ait.core.cmd.rst │ ├── ait.core.coord.rst │ ├── ait.core.db.rst │ ├── ait.core.dmc.rst │ ├── ait.core.dtype.rst │ ├── ait.core.evr.rst │ ├── ait.core.gds.rst │ ├── ait.core.geom.rst │ ├── ait.core.json.rst │ ├── ait.core.limits.rst │ ├── ait.core.log.rst │ ├── ait.core.notify.rst │ ├── ait.core.pcap.rst │ ├── ait.core.rst │ ├── ait.core.seq.rst │ ├── ait.core.server.broker.rst │ ├── ait.core.server.client.rst │ ├── ait.core.server.config.rst │ ├── ait.core.server.handler.rst │ ├── ait.core.server.handlers.ccsds_packet_handler.rst │ ├── ait.core.server.handlers.packet_handler.rst │ ├── ait.core.server.handlers.rst │ ├── ait.core.server.plugin.rst │ ├── ait.core.server.plugins.PacketAccumulator.rst │ ├── ait.core.server.plugins.PacketPadder.rst │ ├── ait.core.server.plugins.apid_routing.rst │ ├── ait.core.server.plugins.data_archive.rst │ ├── ait.core.server.plugins.limit_monitor.rst │ ├── ait.core.server.plugins.openmct.rst │ ├── ait.core.server.plugins.rst │ ├── ait.core.server.process.rst │ ├── ait.core.server.rst │ ├── ait.core.server.server.rst │ ├── ait.core.server.stream.rst │ ├── ait.core.server.utils.rst │ ├── ait.core.table.rst │ ├── ait.core.tlm.rst │ ├── ait.core.util.rst │ ├── ait.core.val.rst │ ├── ait.rst │ ├── api_intro.rst │ ├── bsc_intro.rst │ ├── c_and_dh_intro.rst │ ├── command_intro.rst │ ├── command_line.rst │ ├── conf.py │ ├── configuration_intro.rst │ ├── data_types.rst │ ├── databases.rst │ ├── evr_intro.rst │ ├── extensions.rst │ ├── index.rst │ ├── installation.rst │ ├── limits_intro.rst │ ├── plugin_PacketAccumulator.rst │ ├── plugin_PacketPadder.rst │ ├── plugin_openmct.rst │ ├── project_setup.rst │ ├── serialization.rst │ ├── server_architecture.rst │ └── telemetry_intro.rst ├── openmct ├── README ├── ait_cfg_section.yaml ├── ait_integration.js └── example-server │ ├── ait_integration.js │ ├── index.html │ ├── lib │ └── http.js │ ├── package.json │ └── server.js ├── poetry.lock ├── poetry_cli ├── __init__.py └── build_sphinx.py ├── pyproject.toml ├── readthedocs.yml ├── scripts └── example_script.py ├── sequences └── example_sequence.txt ├── setup.cfg ├── tests ├── __init__.py └── ait │ ├── __init__.py │ └── core │ ├── __init__.py │ ├── server │ ├── __init__.py │ ├── test_apid_routing.py │ ├── test_client.py │ ├── test_handler.py │ ├── test_serial.py │ ├── test_server.py │ └── test_stream.py │ ├── test_bsc.py │ ├── test_ccsds.py │ ├── test_cfg.py │ ├── test_cmd.py │ ├── test_coord.py │ ├── test_db.py │ ├── test_dmc.py │ ├── test_dtype.py │ ├── test_evr.py │ ├── test_limits.py │ ├── test_log.py │ ├── test_notify.py │ ├── test_pcap.py │ ├── test_table.py │ ├── test_tlm.py │ ├── test_util.py │ ├── test_val.py │ └── testdata │ ├── dmc │ └── leapseconds.dat │ ├── testValidTable1.yaml │ ├── util │ └── test_util.txt │ └── val │ ├── testCmdValidator1.yaml │ ├── testCmdValidator2.yaml │ ├── testCmdValidator3.yaml │ ├── testCmdValidator4.yaml │ ├── testCmdValidator5.yaml │ ├── testCmdValidator6.yaml │ ├── testCmdValidator7.yaml │ ├── testCmdValidator8.yaml │ ├── testInvalidCmd1.yaml │ ├── testInvalidTlm1.yaml │ ├── testSchemaLoad1.json │ ├── testTlmValidator1.yaml │ ├── testTlmValidator2.yaml │ ├── testTlmValidator3.yaml │ ├── testTlmValidator4.yaml │ ├── testTlmValidator5.yaml │ ├── testTlmValidator6.yaml │ ├── testTlmValidator7.yaml │ ├── testValidCmd1.yaml │ └── testValidTlm1.yaml └── tox.ini /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @NASA-AMMOS/ait-committers @NASA-AMMOS/ait-project-management-committee 2 | -------------------------------------------------------------------------------- /.github/workflows/full_build.yaml: -------------------------------------------------------------------------------- 1 | name: Full Test and Lint 2 | on: 3 | push: 4 | branches: 5 | - 'master' 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | ait_build: 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | python-version: ["3.7", "3.8", "3.9", "3.10"] 15 | runs-on: "ubuntu-latest" 16 | steps: 17 | - uses: actions/checkout@v3 18 | - name: Poetry Install 19 | run: | 20 | pipx install poetry 21 | poetry --version 22 | - uses: actions/setup-python@v4 23 | with: 24 | python-version: ${{ matrix.python-version }} 25 | # Caching is currently causing the tox build to fail for 3.10. 26 | # Specifically, the `lint` run claims that it's unable to find 27 | # pre-commit. Oddly enough, the install for 3.10 shows that 28 | # pre-commit is installed and the tox run claims that it doesn't 29 | # need to install anything. 30 | #cache: 'poetry' 31 | - name: Install Package 32 | run: poetry install 33 | - name: Set AIT Config 34 | run: echo "AIT_CONFIG=./config/config.yaml" >> $GITHUB_ENV 35 | - name: "Install dependencies" 36 | run: | 37 | set -xe 38 | python -VV 39 | python -m pip install --upgrade pip 40 | python -m pip install --upgrade "tox-gh-actions<3" 41 | - name: Run Tox 42 | run: tox 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Distribution / Packaging / Build files 2 | .coverage* 3 | doc/build/* 4 | doc/source/ait.core.test* 5 | doc/source/ait.core.server.test* 6 | build/lib/* 7 | *.egg-info/ 8 | *.egg 9 | .tox 10 | dist/ 11 | 12 | # Object Files 13 | *.o 14 | *.pkl 15 | *.pyc 16 | 17 | # Libraries 18 | *.lib 19 | *.a 20 | 21 | # Log Files 22 | bliss-logmsgs-*.txt 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 | # LaTeX / PDF Documentation 36 | src/doc/dict/cmd/*.aux 37 | src/doc/dict/cmd/*.log 38 | src/doc/dict/cmd/*.out 39 | src/doc/dict/cmd/*.toc 40 | src/doc/dict/cmd/*.pdf 41 | src/doc/dict/cmd/cmddict-07-cmddefs.tex 42 | 43 | # Temporary files 44 | *~ 45 | tags* 46 | 47 | # Unignore (hack) 48 | !build/clean.py 49 | 50 | # Nosetests ouptut file 51 | nosetests.xml 52 | 53 | # Macintosh file 54 | *.DS_Store 55 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Michael Joyce 2 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.0.1 4 | hooks: 5 | - id: trailing-whitespace 6 | - id: end-of-file-fixer 7 | - id: check-merge-conflict 8 | - id: debug-statements 9 | 10 | - repo: https://github.com/asottile/reorder_python_imports 11 | rev: v2.6.0 12 | hooks: 13 | - id: reorder-python-imports 14 | files: ^ait/|tests/ 15 | 16 | - repo: local 17 | hooks: 18 | - id: mypy 19 | name: mypy 20 | entry: mypy --namespace-packages --package ait.core 21 | language: system 22 | pass_filenames: false 23 | exclude: "bin/" 24 | 25 | - repo: local 26 | hooks: 27 | - id: black 28 | name: black 29 | entry: black 30 | files: ^ait/|tests/ 31 | language: system 32 | types: [python] 33 | 34 | - repo: local 35 | hooks: 36 | - id: flake8 37 | name: flake8 38 | entry: flake8 ait 39 | language: system 40 | pass_filenames: false 41 | 42 | - repo: local 43 | hooks: 44 | - id: tlm_yaml_check 45 | name: AIT TLM YAML check 46 | entry: 'ait-yaml-validate --tlm' 47 | language: system 48 | pass_filenames: false 49 | 50 | - repo: local 51 | hooks: 52 | - id: cmd_yaml_check 53 | name: AIT CMD YAML check 54 | entry: 'ait-yaml-validate --cmd' 55 | language: system 56 | pass_filenames: false 57 | 58 | - repo: local 59 | hooks: 60 | - id: evr_yaml_check 61 | name: AIT EVR YAML check 62 | entry: 'ait-yaml-validate --evr' 63 | language: system 64 | pass_filenames: false 65 | 66 | - repo: local 67 | hooks: 68 | - id: limits_yaml_check 69 | name: AIT LIMITS YAML check 70 | entry: 'ait-yaml-validate --limits' 71 | language: system 72 | pass_filenames: false 73 | 74 | - repo: local 75 | hooks: 76 | - id: tests 77 | name: Tests 78 | entry: pytest 79 | language: system 80 | stages: [push] 81 | pass_filenames: false 82 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.7.15 2 | 3.8.15 3 | 3.9.15 4 | 3.10.8 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - '3.7' 4 | branches: 5 | only: 6 | - master 7 | env: 8 | - AIT_CONFIG: "${TRAVIS_BUILD_DIR}/config/config.yaml" 9 | install: 10 | - pip install .[tests] 11 | script: nosetests 12 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2013 California Institute of Technology 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include ait/core/data/*.json 2 | include ait/core/py.typed 3 | -------------------------------------------------------------------------------- /ait/core/__init__.py: -------------------------------------------------------------------------------- 1 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 2 | # Bespoke Link to Instruments and Small Satellites (BLISS) 3 | # 4 | # Copyright 2016, by the California Institute of Technology. ALL RIGHTS 5 | # RESERVED. United States Government Sponsorship acknowledged. Any 6 | # commercial use must be negotiated with the Office of Technology Transfer 7 | # at the California Institute of Technology. 8 | # 9 | # This software may be subject to U.S. export control laws. By accepting 10 | # this software, the user agrees to comply with all applicable U.S. export 11 | # laws and regulations. User has the responsibility to obtain export licenses, 12 | # or other export authority as may be required before exporting such 13 | # information to foreign countries or providing access to foreign persons. 14 | import sys 15 | 16 | from ait.core import cfg # noqa 17 | from ait.core import log 18 | 19 | # cfg isn't used but we want the AIT-level config attribute created 20 | 21 | 22 | def deprecated(message): 23 | def deprecated_decorator(func): 24 | def deprecated_func(*args, **kwargs): 25 | log.warn("{} is a deprecated function. {}".format(func.__name__, message)) 26 | return func(*args, **kwargs) 27 | 28 | return deprecated_func 29 | 30 | return deprecated_decorator 31 | 32 | 33 | sys.modules["ait"].deprecated = deprecated # type: ignore[attr-defined] 34 | sys.modules["ait"].DEFAULT_CMD_PORT = 3075 # type: ignore[attr-defined] 35 | sys.modules["ait"].DEFAULT_CMD_HOST = "127.0.0.1" # type: ignore[attr-defined] 36 | 37 | # Name of the ZMQ topic used to accept commands from external sources 38 | sys.modules["ait"].DEFAULT_CMD_TOPIC = "__commands__" # type: ignore[attr-defined] 39 | 40 | # Name of the ZMQ topic / stream used for making telemetry packets available to the script API 41 | sys.modules["ait"].DEFAULT_TLM_TOPIC = "__tlmpkts__" # type: ignore[attr-defined] 42 | 43 | # Number of seconds to sleep after ZmqSocket.connect() call, affects clients 44 | sys.modules["ait"].DEFAULT_CMD_ZMQ_SLEEP = 1 # type: ignore[attr-defined] 45 | 46 | 47 | sys.modules["ait"].SERVER_DEFAULT_XSUB_URL = "tcp://*:5559" # type: ignore[attr-defined] 48 | sys.modules["ait"].SERVER_DEFAULT_XPUB_URL = "tcp://*:5560" # type: ignore[attr-defined] 49 | -------------------------------------------------------------------------------- /ait/core/bin/__init__.py: -------------------------------------------------------------------------------- 1 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 2 | # Bespoke Link to Instruments and Small Satellites (BLISS) 3 | # 4 | # Copyright 2017, by the California Institute of Technology. ALL RIGHTS 5 | # RESERVED. United States Government Sponsorship acknowledged. Any 6 | # commercial use must be negotiated with the Office of Technology Transfer 7 | # at the California Institute of Technology. 8 | # 9 | # This software may be subject to U.S. export control laws. By accepting 10 | # this software, the user agrees to comply with all applicable U.S. export 11 | # laws and regulations. User has the responsibility to obtain export licenses, 12 | # or other export authority as may be required before exporting such 13 | # information to foreign countries or providing access to foreign persons. 14 | -------------------------------------------------------------------------------- /ait/core/bin/ait_bsc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 3 | # Bespoke Link to Instruments and Small Satellites (BLISS) 4 | # 5 | # Copyright 2016, by the California Institute of Technology. ALL RIGHTS 6 | # RESERVED. United States Government Sponsorship acknowledged. Any 7 | # commercial use must be negotiated with the Office of Technology Transfer 8 | # at the California Institute of Technology. 9 | # 10 | # This software may be subject to U.S. export control laws. By accepting 11 | # this software, the user agrees to comply with all applicable U.S. export 12 | # laws and regulations. User has the responsibility to obtain export licenses, 13 | # or other export authority as may be required before exporting such 14 | # information to foreign countries or providing access to foreign persons. 15 | """ 16 | Usage: ait-bsc 17 | 18 | Start the ait BSC for capturing network traffic into PCAP files 19 | and the manager server for RESTful manipulation of active loggers. 20 | """ 21 | import argparse 22 | import os 23 | import threading 24 | 25 | import yaml 26 | 27 | import ait 28 | from ait.core import bsc 29 | 30 | 31 | config_file = ait.config.bsc.filename # type: ignore 32 | 33 | 34 | def main(): 35 | ap = argparse.ArgumentParser( 36 | description=__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter 37 | ) 38 | args = ap.parse_args() # noqa 39 | 40 | if not os.path.isfile(config_file): 41 | print("Unable to locate config. Starting up handlers with default values ...") 42 | host = "localhost" 43 | port = "8080" 44 | handler_configs = [] 45 | root_log_dir = "/tmp" 46 | mngr_conf = {"root_log_directory": root_log_dir} 47 | 48 | else: 49 | with open(config_file) as log_conf: 50 | conf = yaml.safe_load(log_conf) 51 | 52 | mngr_conf = conf["capture_manager"] 53 | host = mngr_conf["manager_server"]["host"] 54 | port = mngr_conf["manager_server"]["port"] 55 | 56 | handler_configs = [] 57 | for handler_conf in conf["handlers"]: 58 | if "path" in handler_conf: 59 | handler_path = handler_conf.pop("path") 60 | if not os.path.isabs(handler_path): 61 | handler_path = os.path.join( 62 | mngr_conf["root_log_directory"], handler_path 63 | ) 64 | else: 65 | handler_path = mngr_conf["root_log_directory"] 66 | 67 | handler_configs.append( 68 | ( 69 | handler_conf.pop("name"), 70 | handler_conf.pop("address"), 71 | handler_conf.pop("conn_type"), 72 | handler_path, 73 | handler_conf, 74 | ) 75 | ) 76 | 77 | lgr_mngr = bsc.StreamCaptureManager(mngr_conf, handler_configs) 78 | manager_server = bsc.StreamCaptureManagerServer( 79 | logger_manager=lgr_mngr, host=host, port=port 80 | ) 81 | 82 | t = threading.Thread(target=manager_server.start) 83 | t.setDaemon(True) 84 | t.start() 85 | 86 | lgr_mngr.run_socket_event_loop() 87 | 88 | 89 | if __name__ == "__main__": 90 | main() 91 | -------------------------------------------------------------------------------- /ait/core/bin/ait_bsc_create_handler.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 3 | # Bespoke Link to Instruments and Small Satellites (BLISS) 4 | # 5 | # Copyright 2016, by the California Institute of Technology. ALL RIGHTS 6 | # RESERVED. United States Government Sponsorship acknowledged. Any 7 | # commercial use must be negotiated with the Office of Technology Transfer 8 | # at the California Institute of Technology. 9 | # 10 | # This software may be subject to U.S. export control laws. By accepting 11 | # this software, the user agrees to comply with all applicable U.S. export 12 | # laws and regulations. User has the responsibility to obtain export licenses, 13 | # or other export authority as may be required before exporting such 14 | # information to foreign countries or providing access to foreign persons. 15 | """ 16 | Usage: 17 | ait-bsc-create-handler [options] 18 | 19 | --service-host= The host for the BSC REST service connection 20 | [default: localhost] 21 | --service-port= The port for the BSC REST service connection 22 | [default: 8080] 23 | --rotate-log= Flag saying whether the log should be rotated 24 | automatically [default: True] 25 | --rotate-log-index= If log rotation is enabled, this determines the 26 | frequency of a rotation. One of 'year', 'month', 27 | 'day', 'hour', 'minutes', 'second' [default: day] 28 | --rotate-log-delta= If log rotation is enabled, this determines the 29 | delta between log creation and current time 30 | rotate-index value needed to trigger a log 31 | rotation [default: 1] 32 | --file-name-pattern= The file pattern for the log file name. This can 33 | include handler metadata values as well as strftime 34 | format characters [default: %Y-%m-%d-%H-%M-%S-{name}.pcap] 35 | """ 36 | import argparse 37 | 38 | import requests 39 | 40 | 41 | def main(): 42 | parser = argparse.ArgumentParser( 43 | description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter 44 | ) 45 | 46 | # Add required command line arguments 47 | parser.add_argument("name") 48 | parser.add_argument("loc") 49 | parser.add_argument("port", type=int) 50 | parser.add_argument("conn_type") 51 | 52 | # Add optional command line arguments 53 | parser.add_argument("--service-host", default="localhost") 54 | parser.add_argument("--service-port", type=int, default=8080) 55 | parser.add_argument( 56 | "--rotate-log", type=lambda x: x in ["True", "true"], default=True 57 | ) 58 | parser.add_argument( 59 | "--rotate-log-index", 60 | choices=["year", "month", "day", "hour", "minutes", "second"], 61 | default="day", 62 | ) 63 | parser.add_argument("--rotate-log-delta", type=int, default=1) 64 | parser.add_argument("--file-name-pattern", default="%Y-%m-%d-%H-%M-%S-{name}.pcap") 65 | 66 | # Get command line arguments 67 | args = vars(parser.parse_args()) 68 | 69 | host = args["service_host"] 70 | port = args["service_port"] 71 | handler_name = args["name"] 72 | 73 | requests.post("http://{}:{}/{}/start".format(host, port, handler_name), data=args) 74 | 75 | 76 | if __name__ == "__main__": 77 | main() 78 | -------------------------------------------------------------------------------- /ait/core/bin/ait_bsc_stop_handler.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 3 | # Bespoke Link to Instruments and Small Satellites (BLISS) 4 | # 5 | # Copyright 2016, by the California Institute of Technology. ALL RIGHTS 6 | # RESERVED. United States Government Sponsorship acknowledged. Any 7 | # commercial use must be negotiated with the Office of Technology Transfer 8 | # at the California Institute of Technology. 9 | # 10 | # This software may be subject to U.S. export control laws. By accepting 11 | # this software, the user agrees to comply with all applicable U.S. export 12 | # laws and regulations. User has the responsibility to obtain export licenses, 13 | # or other export authority as may be required before exporting such 14 | # information to foreign countries or providing access to foreign persons. 15 | """ 16 | Usage: 17 | ait-bsc-stop-handler [options] 18 | 19 | --service-host= The host for the BSC REST service connection 20 | [default: localhost] 21 | --service-port= The port for the BSC REST service connection 22 | [default: 8080] 23 | """ 24 | import argparse 25 | 26 | import requests 27 | 28 | 29 | def main(): 30 | parser = argparse.ArgumentParser( 31 | description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter 32 | ) 33 | 34 | # Add required command line arguments 35 | parser.add_argument("name") 36 | 37 | # Add optional command line arguments 38 | parser.add_argument("--service-host", default="localhost") 39 | parser.add_argument("--service-port", type=int, default=8080) 40 | 41 | # Get command line arguments 42 | args = vars(parser.parse_args()) 43 | 44 | host = args["service_host"] 45 | port = args["service_port"] 46 | 47 | handler_name = args["name"] 48 | 49 | requests.delete("http://{}:{}/{}/stop".format(host, port, handler_name)) 50 | 51 | 52 | if __name__ == "__main__": 53 | main() 54 | -------------------------------------------------------------------------------- /ait/core/bin/ait_ccsds_send_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import socket 3 | import struct 4 | import time 5 | 6 | from ait.core import log 7 | 8 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 9 | hs_packet = struct.Struct(">BBBBBBBBBBBBBBBB") 10 | data = bytearray(b"\x02\xE7\x40\x00\x00\x0B\x00\x00\x00\x01\x01\x71\x0C\x41\x00\x01") 11 | 12 | """ 13 | # CCSDS Packet # 14 | version: 000 15 | type: 0 16 | secondary header flag: 0 17 | apid: 01011100111 #743# 18 | sequence flag: 01 19 | sequence count: 00000000 000000 20 | packet length: 00000000 00001011 21 | time_coars 00000000 00000000 00000000 00000001 22 | time_fine 0000 0001 23 | time_id 01 24 | checkword_indicator 1 25 | zoe 1 26 | packet_type 0001 27 | 0 28 | element_id 0001 29 | data_packet 1 30 | version_id 0001 31 | format_id 000001 32 | 00000000 33 | frame_id 00000001 34 | """ 35 | 36 | buf = hs_packet.pack(*data) 37 | 38 | host = "localhost" 39 | port = 3076 40 | 41 | 42 | def main(): 43 | while True: 44 | s.sendto(buf, (host, port)) 45 | log.info("Sent telemetry (%d bytes) to %s:%d" % (hs_packet.size, host, port)) 46 | time.sleep(1) 47 | 48 | 49 | if __name__ == "__main__": 50 | main() 51 | -------------------------------------------------------------------------------- /ait/core/bin/ait_cmd_hist.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import argparse 3 | 4 | from ait.core import log 5 | from ait.core import pcap 6 | 7 | 8 | """Query all commands from a Command History PCAP""" 9 | 10 | 11 | def main(): 12 | log.begin() 13 | 14 | arguments = { 15 | "filename": { 16 | "metavar": "", 17 | "help": "command history pcap", 18 | } 19 | } 20 | 21 | ap = argparse.ArgumentParser(description=__doc__) 22 | for name, params in arguments.items(): 23 | ap.add_argument(name, **params) 24 | 25 | args = ap.parse_args() 26 | 27 | with pcap.open(args.filename) as stream: 28 | for header, data in stream: 29 | print(header.timestamp.strftime("%Y-%m-%d %H:%M:%S") + "\t" + data.decode()) 30 | 31 | log.end() 32 | 33 | 34 | if __name__ == "__main__": 35 | main() 36 | -------------------------------------------------------------------------------- /ait/core/bin/ait_cmd_send.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 3 | # Bespoke Link to Instruments and Small Satellites (BLISS) 4 | # 5 | # Copyright 2013, by the California Institute of Technology. ALL RIGHTS 6 | # RESERVED. United States Government Sponsorship acknowledged. Any 7 | # commercial use must be negotiated with the Office of Technology Transfer 8 | # at the California Institute of Technology. 9 | # 10 | # This software may be subject to U.S. export control laws. By accepting 11 | # this software, the user agrees to comply with all applicable U.S. export 12 | # laws and regulations. User has the responsibility to obtain export licenses, 13 | # or other export authority as may be required before exporting such 14 | # information to foreign countries or providing access to foreign persons. 15 | # 16 | # 17 | # Sends the given command and its arguments to the ISS simulator via 18 | # the AIT server, or if the 'udp' flag is set then directly via UDP. 19 | # 20 | # Examples: 21 | # $ ait-cmd-send OCO3_CMD_START_SEQUENCE_NOW 1 22 | # 23 | # 24 | """ 25 | usage: ait-cmd-send [options] command [arguments] 26 | 27 | Sends the given command and its arguments to the ISS simulator via 28 | the AIT server, or if the 'udp' flag is set then directly via UDP. 29 | 30 | --verbose Hexdump data (default: False) 31 | --topic=topicname Sets the name of ZMQ topic (default: '__commands__') 32 | --udp Send data via UDP (default: False) 33 | --host=url URL of the host to send data (default: '127.0.0.1') 34 | --port=number Port on which to send data (default: 3075) 35 | 36 | Examples: 37 | 38 | $ ait-cmd-send OCO3_CMD_START_SEQUENCE_NOW 1 39 | """ 40 | import argparse 41 | from collections import OrderedDict 42 | 43 | import ait 44 | from ait.core import api 45 | from ait.core import log 46 | from ait.core import util 47 | 48 | 49 | def main(): 50 | log.begin() 51 | 52 | descr = ( 53 | "Sends the given command and its arguments to the ISS simulator via " 54 | "the AIT server, or if the 'udp' flag is set then directly via UDP." 55 | ) 56 | 57 | parser = argparse.ArgumentParser( 58 | description=descr, formatter_class=argparse.ArgumentDefaultsHelpFormatter 59 | ) 60 | 61 | arg_defns = OrderedDict( 62 | { 63 | "--topic": { 64 | "type": str, 65 | "default": ait.config.get("command.topic", ait.DEFAULT_CMD_TOPIC), 66 | "help": "Name of topic from which to publish data", 67 | }, 68 | "--verbose": { 69 | "action": "store_true", 70 | "default": False, 71 | "help": "Hexdump of the raw command being sent.", 72 | }, 73 | "--udp": { 74 | "action": "store_true", 75 | "default": False, 76 | "help": "Send data to UDP socket.", 77 | }, 78 | "--host": { 79 | "type": str, 80 | "default": ait.DEFAULT_CMD_HOST, 81 | "help": "Host to which to send data", 82 | }, 83 | "--port": { 84 | "type": int, 85 | "default": ait.config.get("command.port", ait.DEFAULT_CMD_PORT), 86 | "help": "Port on which to send data", 87 | }, 88 | } 89 | ) 90 | 91 | arg_defns["command"] = {"type": str, "help": "Name of the command to send."} 92 | 93 | arg_defns["arguments"] = { 94 | "type": util.toNumberOrStr, 95 | "metavar": "arguments", 96 | "nargs": "*", 97 | "help": "Command arguments.", 98 | } 99 | 100 | # Push argument defs to the parser 101 | for name, params in arg_defns.items(): 102 | parser.add_argument(name, **params) 103 | 104 | # Get arg results of the parser 105 | args = parser.parse_args() 106 | 107 | # Extract args to local fields 108 | host = args.host 109 | port = args.port 110 | verbose = args.verbose 111 | udp = args.udp 112 | topic = args.topic 113 | 114 | # If UDP enabled, collect host/port info 115 | if udp: 116 | if host is not None: 117 | dest = (host, port) 118 | else: 119 | dest = port 120 | 121 | cmd_api = api.CmdAPI(udp_dest=dest, verbose=verbose) 122 | # Default CmdAPI connect hooks up to C&DH server 0MQ port 123 | else: 124 | cmd_api = api.CmdAPI(verbose=verbose, cmdtopic=topic) 125 | 126 | cmd_args = cmd_api.parse_args(args.command, *args.arguments) 127 | 128 | cmd_api.send(args.command, *cmd_args) 129 | 130 | log.end() 131 | 132 | 133 | if __name__ == "__main__": 134 | main() 135 | -------------------------------------------------------------------------------- /ait/core/bin/ait_dict_writer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 3 | # Bespoke Link to Instruments and Small Satellites (BLISS) 4 | # 5 | # Copyright 2016, by the California Institute of Technology. ALL RIGHTS 6 | # RESERVED. United States Government Sponsorship acknowledged. Any 7 | # commercial use must be negotiated with the Office of Technology Transfer 8 | # at the California Institute of Technology. 9 | # 10 | # This software may be subject to U.S. export control laws. By accepting 11 | # this software, the user agrees to comply with all applicable U.S. export 12 | # laws and regulations. User has the responsibility to obtain export licenses, 13 | # or other export authority as may be required before exporting such 14 | # information to foreign countries or providing access to foreign persons. 15 | """ 16 | Usage: 17 | ait-dict-writer [options] (--tlm | --cmd) 18 | 19 | --tlm Run dictionary processor for Telemetry dictionary. 20 | --cmd Run dictionary processor for Command dictionary. 21 | --format= Specify output format. Possible values: csv 22 | [Default: csv] 23 | --path= Output file path. 24 | 25 | 26 | Description: 27 | AIT TLM and CMD Dictionary Definitions to Specified Output Format 28 | 29 | Outputs AIT TLM and CMD Dictionary Definitions in Specific output format. Currently supports: 30 | * TLM -> CSV 31 | 32 | TODO 33 | * TLM -> TeX 34 | * CMD -> CSV 35 | * CMD -> TeX 36 | 37 | Copyright 2016 California Institute of Technology. ALL RIGHTS RESERVED. 38 | U.S. Government Sponsorship acknowledged. 39 | """ 40 | import argparse 41 | import sys 42 | 43 | from ait.core import log 44 | from ait.core import tlm 45 | 46 | 47 | def main(): 48 | parser = argparse.ArgumentParser( 49 | description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter 50 | ) 51 | 52 | # Add optional command line arguments 53 | parser.add_argument("--format", default="csv") 54 | parser.add_argument("--path", default="") 55 | 56 | group = parser.add_mutually_exclusive_group(required=True) 57 | group.add_argument("--tlm", action="store_true", default=False) 58 | group.add_argument("--cmd", action="store_true", default=False) 59 | 60 | # Get command line arguments 61 | args = vars(parser.parse_args()) 62 | 63 | # output format for the file 64 | format = args["format"] 65 | 66 | # output path 67 | path = args["path"] 68 | 69 | # initialize telemetry dictionary writer 70 | if args["tlm"]: 71 | writer = tlm.TlmDictWriter() 72 | 73 | # initialize command dictionary writer 74 | if args["cmd"]: 75 | log.error("Not yet supported") 76 | sys.exit() 77 | 78 | # write to csv 79 | if format == "csv": 80 | writer.writeToCSV(output_path=path) 81 | else: 82 | log.error("Invalid specified.") 83 | 84 | 85 | if __name__ == "__main__": 86 | main() 87 | -------------------------------------------------------------------------------- /ait/core/bin/ait_pcap_segment.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 3 | # Bespoke Link to Instruments and Small Satellites (BLISS) 4 | # 5 | # Copyright 2017, by the California Institute of Technology. ALL RIGHTS 6 | # RESERVED. United States Government Sponsorship acknowledged. Any 7 | # commercial use must be negotiated with the Office of Technology Transfer 8 | # at the California Institute of Technology. 9 | # 10 | # This software may be subject to U.S. export control laws. By accepting 11 | # this software, the user agrees to comply with all applicable U.S. export 12 | # laws and regulations. User has the responsibility to obtain export licenses, 13 | # or other export authority as may be required before exporting such 14 | # information to foreign countries or providing access to foreign persons. 15 | """ 16 | Segments one or more pcap files into multiple pcap files, according to 17 | a threshold number of bytes, packets, and/or seconds. New segment 18 | filenames are determined based on a strftime(3) format string and 19 | the timestamp of the first packet in the file. 20 | 21 | When segmenting based on time (-s, --seconds), for file naming and 22 | interval calculation purposes ONLY, the timestamp of the first packet 23 | in the file is rounded down to nearest even multiple of the number of 24 | seconds. This yields nice round number timestamps for filenames. For 25 | example: 26 | 27 | ait-pcap-segment -s 3600 %Y%m%dT%H%M%S.pcap foo.pcap bar.pcap 28 | 29 | If the first packet written to a file has a time of 2017-11-23 30 | 19:28:58, the file will be named: 31 | 32 | 20171123T190000.pcap 33 | 34 | And a new file will be started when a packet is written with a 35 | timestamp that exceeds 2017-11-23 19:59:59. 36 | """ 37 | import argparse 38 | 39 | from ait.core import log 40 | from ait.core import pcap 41 | 42 | 43 | def main(): 44 | ap = argparse.ArgumentParser( 45 | epilog=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter 46 | ) 47 | 48 | ap.add_argument( 49 | "-n", 50 | "--dry-run", 51 | action="store_true", 52 | help="Dry run; do not actually write files", 53 | ) 54 | 55 | ap.add_argument("-b", "--bytes", help="Segment evey B bytes", metavar="B", type=int) 56 | 57 | ap.add_argument( 58 | "-p", "--packets", help="Segment evey P packets", metavar="P", type=int 59 | ) 60 | 61 | ap.add_argument( 62 | "-s", 63 | "--seconds", 64 | help="Segment when first and last pcap timestamps span S seconds", 65 | metavar="S", 66 | type=int, 67 | ) 68 | 69 | ap.add_argument( 70 | "format", help="Segment filename (should include strftime(3) time format)" 71 | ) 72 | 73 | ap.add_argument("file", nargs="+", help="Packet Capture (.pcap) file(s)") 74 | 75 | args = ap.parse_args() 76 | 77 | if args.bytes is None and args.packets is None and args.seconds is None: 78 | msg = "At least one of -b, -p, or -s is required." 79 | ap.error(msg) 80 | 81 | try: 82 | pcap.segment( 83 | filenames=args.file, 84 | format=args.format, 85 | nbytes=args.bytes, 86 | npackets=args.packets, 87 | nseconds=args.seconds, 88 | dryrun=args.dry_run, 89 | ) 90 | 91 | except KeyboardInterrupt: 92 | log.info("Received Ctrl-C. Aborting pcap segmentation.") 93 | 94 | except IOError as e: 95 | log.error(str(e)) 96 | 97 | log.end() 98 | 99 | 100 | if __name__ == "__main__": 101 | main() 102 | -------------------------------------------------------------------------------- /ait/core/bin/ait_seq_decode.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 3 | # Bespoke Link to Instruments and Small Satellites (BLISS) 4 | # 5 | # Copyright 2013, by the California Institute of Technology. ALL RIGHTS 6 | # RESERVED. United States Government Sponsorship acknowledged. Any 7 | # commercial use must be negotiated with the Office of Technology Transfer 8 | # at the California Institute of Technology. 9 | # 10 | # This software may be subject to U.S. export control laws. By accepting 11 | # this software, the user agrees to comply with all applicable U.S. export 12 | # laws and regulations. User has the responsibility to obtain export licenses, 13 | # or other export authority as may be required before exporting such 14 | # information to foreign countries or providing access to foreign persons. 15 | """ 16 | 17 | usage: ait-seq-decode ait_seq_SSS_desc_NNN.bin 18 | 19 | Decodes the given relative time command sequence to text. 20 | 21 | Examples: 22 | 23 | $ ait-seq-decode seq/ait_seq_gps_reset_001.bin 24 | """ 25 | import argparse 26 | import os 27 | 28 | from ait.core import log 29 | from ait.core import seq 30 | 31 | 32 | def main(): 33 | log.begin() 34 | 35 | parser = argparse.ArgumentParser( 36 | description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter 37 | ) 38 | 39 | # Add required command line arguments 40 | parser.add_argument( 41 | "filename", nargs="+", metavar="", help="encoded sequence file(s)" 42 | ) 43 | 44 | # Get command line arguments 45 | args = parser.parse_args() 46 | for fname in args.filename: 47 | filename = os.path.abspath(fname) 48 | if not os.path.isfile(filename): 49 | raise Exception("File not found: %s " % filename) 50 | 51 | extension = os.path.splitext(filename)[1] 52 | 53 | if extension.lower() != ".bin": 54 | log.warn("Filename '%s' does not have a '.bin' extension", filename) 55 | 56 | # Parse the filename for the applicable information 57 | parts = os.path.basename(filename).split("_") 58 | seqid = os.path.splitext(parts[-1])[0] 59 | 60 | try: 61 | int(seqid) 62 | except ValueError: 63 | raise Exception( 64 | 'Invalid filename "%s": . %s' % (os.path.basename(filename), __doc__) 65 | ) 66 | 67 | sequence = seq.createSeq(filename, id=seqid) 68 | 69 | if not sequence.validate(): 70 | for msg in sequence.messages: 71 | log.error(msg) 72 | else: 73 | txtpath = sequence.txtpath 74 | seqid = sequence.seqid 75 | version = sequence.version 76 | 77 | msg = "Writing %s (seqid=0x%04x, version=%u)." 78 | log.info(msg, txtpath, seqid, version) 79 | 80 | sequence.write_text() 81 | 82 | log.end() 83 | 84 | 85 | if __name__ == "__main__": 86 | main() 87 | -------------------------------------------------------------------------------- /ait/core/bin/ait_seq_encode.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 3 | # Bespoke Link to Instruments and Small Satellites (BLISS) 4 | # 5 | # Copyright 2013, by the California Institute of Technology. ALL RIGHTS 6 | # RESERVED. United States Government Sponsorship acknowledged. Any 7 | # commercial use must be negotiated with the Office of Technology Transfer 8 | # at the California Institute of Technology. 9 | # 10 | # This software may be subject to U.S. export control laws. By accepting 11 | # this software, the user agrees to comply with all applicable U.S. export 12 | # laws and regulations. User has the responsibility to obtain export licenses, 13 | # or other export authority as may be required before exporting such 14 | # information to foreign countries or providing access to foreign persons. 15 | """ 16 | usage: ait-seq-encode mission_seq_SSS_desc_NNN.txt 17 | where: 18 | SSS = subsystem 19 | desc = sequence descriptor 20 | NNN = sequence ID (integer) 21 | 22 | Encodes the given relative time command sequence to binary. 23 | 24 | Examples: 25 | 26 | $ ait-seq-encode seq/ait_seq_gps_reset_001.txt 27 | """ 28 | import argparse 29 | import os 30 | import sys 31 | 32 | from ait.core import log 33 | from ait.core import seq 34 | 35 | 36 | def main(): 37 | log.begin() 38 | 39 | try: 40 | parser = argparse.ArgumentParser( 41 | description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter 42 | ) 43 | 44 | # Add required command line arguments 45 | parser.add_argument( 46 | "filename", 47 | nargs="+", 48 | metavar="", 49 | help="File or collection of sequence file(s)", 50 | ) 51 | 52 | # Add optional command line arguments 53 | args = parser.parse_args() 54 | 55 | for fname in args.filename: 56 | filename = os.path.abspath(fname) 57 | if not os.path.isfile(filename): 58 | raise Exception(f"File not found: {filename}") 59 | 60 | extension = os.path.splitext(filename)[1] 61 | 62 | if extension.lower() != ".txt": 63 | log.warn(f"Filename '{filename}' does not have a '.txt' extension") 64 | 65 | # Parse the filename for the applicable information 66 | parts = os.path.basename(filename).split("_") 67 | seqid = os.path.splitext(parts[-1])[0] 68 | 69 | try: 70 | seqid = int(seqid) 71 | except ValueError: 72 | raise Exception( 73 | 'Invalid filename "{os.path.basename(filename)}": . {__doc__}' 74 | ) 75 | 76 | sequence = seq.createSeq(filename, id=seqid) 77 | 78 | if not sequence.validate(): 79 | for msg in sequence.log.messages: 80 | log.error(msg) 81 | else: 82 | binpath = sequence.binpath 83 | seqid = sequence.seqid 84 | 85 | log.info(f"Writing {binpath} (seqid=0x{seqid:04X}).") 86 | sequence.write_binary() 87 | 88 | exit = 0 89 | except Exception as e: 90 | log.error(e) 91 | exit = 1 92 | 93 | log.end() 94 | 95 | sys.exit(exit) 96 | 97 | 98 | if __name__ == "__main__": 99 | main() 100 | -------------------------------------------------------------------------------- /ait/core/bin/ait_seq_print.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 3 | # Bespoke Link to Instruments and Small Satellites (BLISS) 4 | # 5 | # Copyright 2013, by the California Institute of Technology. ALL RIGHTS 6 | # RESERVED. United States Government Sponsorship acknowledged. Any 7 | # commercial use must be negotiated with the Office of Technology Transfer 8 | # at the California Institute of Technology. 9 | # 10 | # This software may be subject to U.S. export control laws. By accepting 11 | # this software, the user agrees to comply with all applicable U.S. export 12 | # laws and regulations. User has the responsibility to obtain export licenses, 13 | # or other export authority as may be required before exporting such 14 | # information to foreign countries or providing access to foreign persons. 15 | """ 16 | usage: ait-seq-print ait_seq_SSS_NNN_desc.bin 17 | 18 | Prints the given binary relative time command sequence to standard 19 | output as text. 20 | 21 | Examples: 22 | 23 | $ ait-seq-print seq/ait_seq_gps_001_reset.bin 24 | """ 25 | import argparse 26 | import os 27 | 28 | from ait.core import log 29 | from ait.core import seq 30 | 31 | 32 | def main(): 33 | log.begin() 34 | 35 | parser = argparse.ArgumentParser( 36 | description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter 37 | ) 38 | 39 | # Add required command line argument 40 | parser.add_argument( 41 | "filename", nargs="+", metavar="", help="encoded sequence file(s)" 42 | ) 43 | 44 | # Get command line arguments 45 | args = parser.parse_args() 46 | for fname in args.filename: 47 | filename = os.path.abspath(fname) 48 | if not os.path.isfile(filename): 49 | raise Exception("File not found: %s " % filename) 50 | 51 | extension = os.path.splitext(filename)[1] 52 | 53 | if extension.lower() != ".bin": 54 | log.warn("Filename '%s' does not have a '.bin' extension", filename) 55 | 56 | # Parse the filename for the applicable information 57 | parts = os.path.basename(filename).split("_") 58 | seqid = parts[-2] 59 | 60 | try: 61 | int(seqid) 62 | except ValueError: 63 | raise Exception( 64 | 'Invalid filename "%s": . %s' % (os.path.basename(filename), __doc__) 65 | ) 66 | 67 | sequence = seq.createSeq(filename, id=seqid) 68 | 69 | if not sequence.validate(): 70 | for msg in sequence.messages: 71 | log.error(msg) 72 | 73 | sequence.printText() 74 | 75 | log.end() 76 | 77 | 78 | if __name__ == "__main__": 79 | main() 80 | -------------------------------------------------------------------------------- /ait/core/bin/ait_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Usage: ait-server 4 | 5 | Start the AIT telemetry server for managing telemety streams, 6 | command outputs, processing handlers, and plugins. 7 | """ 8 | import argparse 9 | 10 | from ait.core.server import Server 11 | 12 | 13 | def main(): 14 | ap = argparse.ArgumentParser( 15 | description=__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter 16 | ) 17 | args = ap.parse_args() # noqa 18 | 19 | tlm_cmd_serv = Server() 20 | tlm_cmd_serv.wait() 21 | 22 | 23 | if __name__ == "__main__": 24 | main() 25 | -------------------------------------------------------------------------------- /ait/core/bin/ait_table_decode.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 4 | # Bespoke Link to Instruments and Small Satellites (BLISS) 5 | # 6 | # Copyright 2021, by the California Institute of Technology. ALL RIGHTS 7 | # RESERVED. United States Government Sponsorship acknowledged. Any 8 | # commercial use must be negotiated with the Office of Technology Transfer 9 | # at the California Institute of Technology. 10 | # 11 | # This software may be subject to U.S. export control laws. By accepting 12 | # this software, the user agrees to comply with all applicable U.S. export 13 | # laws and regulations. User has the responsibility to obtain export licenses, 14 | # or other export authority as may be required before exporting such 15 | # information to foreign countries or providing access to foreign persons. 16 | """Decode AIT FSW table binaries""" 17 | import argparse 18 | import os.path 19 | import sys 20 | 21 | from ait.core import log 22 | from ait.core import table 23 | 24 | 25 | def main(): 26 | log.begin() 27 | 28 | parser = argparse.ArgumentParser( 29 | description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter 30 | ) 31 | 32 | parser.add_argument("in_file", help="Input file path") 33 | parser.add_argument("--out_file", default=None, help="Output file path") 34 | parser.add_argument( 35 | "--raw", 36 | action="store_true", 37 | help="Decode columns into raw values without enumerations", 38 | ) 39 | 40 | args = parser.parse_args() 41 | 42 | file_in = open(args.in_file, "rb") 43 | out_path = ( 44 | args.out_file 45 | if args.out_file is not None 46 | else f"{os.path.splitext(args.in_file)[0]}_decoded.txt" 47 | ) 48 | 49 | # Extract the table upload type (byte 0) from the binary so we can 50 | # locate the table definition that we need. 51 | uptype = int.from_bytes(file_in.read(1), byteorder="big") 52 | file_in.seek(0) 53 | fswtabdict = table.getDefaultFSWTabDict() 54 | pos_defn = [map[0] for map in fswtabdict.items() if map[1].uptype == uptype] 55 | 56 | if len(pos_defn) != 1: 57 | log.error( 58 | f"Table upload type {uptype} not found in table dictionary. Stopping ..." 59 | ) 60 | sys.exit(1) 61 | 62 | tbldefn = fswtabdict[pos_defn[0]] 63 | decoded = tbldefn.decode(file_in=file_in, raw=args.raw) 64 | 65 | out_file = open(out_path, "w") 66 | 67 | # Output our header values in comments so the table can be re-encoded easily 68 | hdr_row = decoded[0] 69 | for defn, val in zip(tbldefn.fswheaderdefns, hdr_row): 70 | print(f"# {defn.name}={val}", file=out_file) 71 | 72 | for row in decoded[1:]: 73 | print(tbldefn.delimiter.join(map(str, row)), file=out_file) 74 | 75 | out_file.close() 76 | 77 | log.end() 78 | 79 | 80 | if __name__ == "__main__": 81 | main() 82 | -------------------------------------------------------------------------------- /ait/core/bin/ait_table_encode.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2021, by the California Institute of Technology. ALL RIGHTS 4 | # RESERVED. United States Government Sponsorship acknowledged. Any 5 | # commercial use must be negotiated with the Office of Technology Transfer 6 | # at the California Institute of Technology. 7 | # 8 | # This software may be subject to U.S. export control laws. By accepting 9 | # this software, the user agrees to comply with all applicable U.S. export 10 | # laws and regulations. User has the responsibility to obtain export licenses, 11 | # or other export authority as may be required before exporting such 12 | # information to foreign countries or providing access to foreign persons. 13 | """Encode AIT FSW tables for upload """ 14 | import argparse 15 | import os.path 16 | import sys 17 | 18 | from ait.core import log 19 | from ait.core import table 20 | 21 | 22 | def main(): 23 | log.begin() 24 | 25 | parser = argparse.ArgumentParser( 26 | description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter 27 | ) 28 | 29 | parser.add_argument( 30 | "table_type", 31 | choices=list(table.getDefaultDict().keys()), 32 | help=( 33 | f"The type of table being encoded. One of {list(table.getDefaultDict().keys())}" 34 | ), 35 | ) 36 | 37 | parser.add_argument("in_file", help="Input file path") 38 | parser.add_argument("--out_file", help="Output file path") 39 | 40 | args = parser.parse_args() 41 | 42 | fswtabdict = table.getDefaultFSWTabDict() 43 | tbldefn = fswtabdict[args.table_type] 44 | 45 | out_path = ( 46 | args.out_file 47 | if args.out_file is not None 48 | else f"{os.path.splitext(args.in_file)[0]}.bin" 49 | ) 50 | 51 | with open(args.in_file, "r") as in_file: 52 | encoded = tbldefn.encode(file_in=in_file) 53 | 54 | # Verify that the encoded table is the proper size. If it's too small we need 55 | # to pad it out. If it's too big then the user needs to remove some of the 56 | # entires. 57 | enc_len = len(encoded) 58 | if enc_len < tbldefn.size: 59 | encoded += bytearray(tbldefn.size - enc_len) 60 | elif enc_len > tbldefn.size: 61 | log.error( 62 | f"Encoded {tbldefn.name} table is too large. " 63 | f"Expected size: {tbldefn.size} bytes. Encoded size: {enc_len} bytes." 64 | "Please remove some entires from the table." 65 | ) 66 | sys.exit(1) 67 | 68 | with open(out_path, "wb") as out_file: 69 | out_file.write(encoded) 70 | 71 | log.end() 72 | 73 | 74 | if __name__ == "__main__": 75 | main() 76 | -------------------------------------------------------------------------------- /ait/core/bin/ait_tlm_db_insert.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 3 | # Bespoke Link to Instruments and Small Satellites (BLISS) 4 | # 5 | # Copyright 2016, by the California Institute of Technology. ALL RIGHTS 6 | # RESERVED. United States Government Sponsorship acknowledged. Any 7 | # commercial use must be negotiated with the Office of Technology Transfer 8 | # at the California Institute of Technology. 9 | # 10 | # This software may be subject to U.S. export control laws. By accepting 11 | # this software, the user agrees to comply with all applicable U.S. export 12 | # laws and regulations. User has the responsibility to obtain export licenses, 13 | # or other export authority as may be required before exporting such 14 | # information to foreign countries or providing access to foreign persons. 15 | """ 16 | Inserts telemetry into a database from one or more PCAP files. 17 | """ 18 | import argparse 19 | import os 20 | import struct 21 | 22 | import ait 23 | from ait.core import db 24 | from ait.core import log 25 | from ait.core import pcap 26 | from ait.core import tlm 27 | 28 | 29 | def main(): 30 | tlmdict = tlm.getDefaultDict() 31 | pnames = list(tlmdict.keys()) 32 | ap = argparse.ArgumentParser( 33 | description=__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter 34 | ) 35 | 36 | arguments = { 37 | "--packet": { 38 | "type": str, 39 | "choices": pnames, 40 | "default": pnames[0] if len(pnames) > 0 else None, 41 | "help": "Type of packets (!Packet name in tlm.yaml) in file", 42 | "required": len(pnames) > 1, 43 | }, 44 | "--database": { 45 | "default": ait.config.get("database.dbname"), 46 | "help": ( 47 | "Name of database in which to insert packets (may " 48 | "also be specified in config.yaml database.name)" 49 | ), 50 | "required": ait.config.get("database.dbname") is None, 51 | }, 52 | "--backend": { 53 | "default": "sqlite", 54 | "choices": ["sqlite", "influx"], 55 | "action": "store", 56 | "help": ( 57 | "Name of database in which to insert packets (may " 58 | "also be specified in config.yaml database.name)" 59 | ), 60 | }, 61 | "--use-current-time": { 62 | "action": "store_true", 63 | "help": ( 64 | "Use current time stamps when insert packets instead " 65 | "of ground receipt time (or the time written in the " 66 | "PCAP header)." 67 | ), 68 | }, 69 | "file": {"nargs": "+", "help": "File(s) containing telemetry packets"}, 70 | } 71 | 72 | for name, params in arguments.items(): 73 | ap.add_argument(name, **params) 74 | 75 | args = ap.parse_args() 76 | 77 | log.begin() 78 | 79 | try: 80 | npackets = 0 81 | dbconn = None 82 | defn = tlm.getDefaultDict()[args.packet] 83 | 84 | if args.backend == "sqlite": 85 | dbconn = db.SQLiteBackend() 86 | elif args.backend == "influx": 87 | dbconn = db.InfluxDBBackend() 88 | 89 | if args.backend == "sqlite" and ( 90 | args.database == ":memory:" or not os.path.exists(args.database) 91 | ): 92 | dbconn.create(database=args.database) 93 | else: 94 | dbconn.connect(database=args.database) 95 | 96 | for filename in args.file: 97 | log.info("Processing %s" % filename) 98 | with pcap.open(filename) as stream: 99 | for header, pkt_data in stream: 100 | try: 101 | packet = tlm.Packet(defn, pkt_data) 102 | 103 | time = header.timestamp 104 | if args.use_current_time: 105 | time = None 106 | 107 | dbconn.insert(packet, time=time) 108 | npackets += 1 109 | except struct.error: 110 | log.error("Unable to unpack data into packet. Skipping ...") 111 | 112 | except KeyboardInterrupt: 113 | log.info("Received Ctrl-C. Stopping database insert.") 114 | 115 | except IOError as e: 116 | log.error(str(e)) 117 | 118 | finally: 119 | dbconn.close() 120 | 121 | values = npackets, args.packet, args.database 122 | log.info("Inserted %d %s packets into database %s." % values) 123 | 124 | log.end() 125 | 126 | 127 | if __name__ == "__main__": 128 | main() 129 | -------------------------------------------------------------------------------- /ait/core/bin/ait_tlm_send.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 3 | # Bespoke Link to Instruments and Small Satellites (BLISS) 4 | # 5 | # Copyright 2013, by the California Institute of Technology. ALL RIGHTS 6 | # RESERVED. United States Government Sponsorship acknowledged. Any 7 | # commercial use must be negotiated with the Office of Technology Transfer 8 | # at the California Institute of Technology. 9 | # 10 | # This software may be subject to U.S. export control laws. By accepting 11 | # this software, the user agrees to comply with all applicable U.S. export 12 | # laws and regulations. User has the responsibility to obtain export licenses, 13 | # or other export authority as may be required before exporting such 14 | # information to foreign countries or providing access to foreign persons. 15 | """ 16 | Usage: ait-tlm-send [options] 17 | 18 | Sends the telemetry contained in the given pcap file via UDP. 19 | 20 | --port=number Port to which to send data (default: 3076) 21 | --verbose Report every packet sent (default:False) 22 | 23 | Examples: 24 | 25 | $ ait-tlm-send test/data/pcap/oco3fsw-iss1553-2015-04-22.pcap 26 | 27 | """ 28 | import argparse 29 | import socket 30 | import time 31 | 32 | from ait.core import log 33 | from ait.core import pcap 34 | 35 | 36 | def main(): 37 | try: 38 | log.begin() 39 | 40 | parser = argparse.ArgumentParser( 41 | description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter 42 | ) 43 | 44 | # Add required command line arguments 45 | parser.add_argument("filename") 46 | 47 | # Add optional command line arguments 48 | parser.add_argument("--port", default=3076, type=int) 49 | parser.add_argument("--verbose", action="store_true", default=False) 50 | 51 | # Get command line arguments 52 | args = vars(parser.parse_args()) 53 | 54 | filename = args["filename"] 55 | host = "localhost" 56 | port = args["port"] 57 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 58 | verbose = args["verbose"] 59 | 60 | if not verbose: 61 | log.info("Will only report every 10 telemetry packets") 62 | log.info("Will only report long telemetry send delays") 63 | 64 | with pcap.open(filename, "r") as stream: 65 | npackets = 0 66 | prev_ts = None 67 | 68 | for header, packet in stream: 69 | if prev_ts is None: 70 | prev_ts = header.ts 71 | 72 | delay = header.ts - prev_ts 73 | 74 | if delay >= 2: 75 | log.info("Next telemetry in %1.2f seconds" % delay) 76 | 77 | time.sleep(delay) 78 | 79 | nbytes = len(packet) 80 | 81 | if npackets == 0: 82 | log.info("Sent first telemetry packet (%d bytes)" % nbytes) 83 | elif verbose: 84 | log.info("Sent telemetry (%d bytes)" % nbytes) 85 | elif npackets % 10 == 0: 86 | log.info("Sent 10 telemetry packets") 87 | 88 | sock.sendto(packet, (host, port)) 89 | 90 | npackets += 1 91 | prev_ts = header.ts 92 | 93 | except KeyboardInterrupt: 94 | log.info("Received Ctrl-C. Stopping telemetry stream.") 95 | 96 | except Exception as e: 97 | log.error("TLM send error: %s" % str(e)) 98 | 99 | log.end() 100 | 101 | 102 | if __name__ == "__main__": 103 | main() 104 | -------------------------------------------------------------------------------- /ait/core/bin/ait_tlm_simulate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 3 | # Bespoke Link to Instruments and Small Satellites (BLISS) 4 | # 5 | # Copyright 2018, by the California Institute of Technology. ALL RIGHTS 6 | # RESERVED. United States Government Sponsorship acknowledged. Any 7 | # commercial use must be negotiated with the Office of Technology Transfer 8 | # at the California Institute of Technology. 9 | # 10 | # This software may be subject to U.S. export control laws. By accepting 11 | # this software, the user agrees to comply with all applicable U.S. export 12 | # laws and regulations. User has the responsibility to obtain export licenses, 13 | # or other export authority as may be required before exporting such 14 | # information to foreign countries or providing access to foreign persons. 15 | """ 16 | Usage: ait-tlm-simulate [options] 17 | 18 | Sends simulated telemetry. 19 | 20 | --port=number Port to which to send data (default:3076) 21 | --host=string Host to which to send data (default:'127.0.0.1') 22 | --packetName=str String name of packetDefn (default:None) 23 | --packetFill Byte to fill packet with (default:None) 24 | 25 | If no packetName specified, will choose the first available packetDefn. 26 | If no packetFill specified, will fill packet using range. 27 | 28 | Examples: 29 | 30 | $ ait-tlm-simulate 31 | 32 | """ 33 | import argparse 34 | import socket 35 | import time 36 | 37 | from ait.core import log 38 | from ait.core import tlm 39 | 40 | 41 | def main(): 42 | try: 43 | log.begin() 44 | 45 | parser = argparse.ArgumentParser( 46 | description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter 47 | ) 48 | 49 | # Add optional command line arguments 50 | parser.add_argument("--port", default=3076, type=int) 51 | parser.add_argument("--host", default="127.0.0.1", type=str) 52 | parser.add_argument("--packetName", default=None) 53 | parser.add_argument("--packetFill", default=None) 54 | 55 | # Get command line arguments 56 | args = vars(parser.parse_args()) 57 | 58 | port = args["port"] 59 | host = args["host"] 60 | fill = args["packetFill"] 61 | name = args["packetName"] 62 | 63 | if name: 64 | defn = tlm.getDefaultDict()[name] 65 | else: 66 | defn = list(tlm.getDefaultDict().values())[0] 67 | 68 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 69 | 70 | packet = defn.simulate(fill=fill) 71 | 72 | while True: 73 | sock.sendto(packet._data, (host, port)) 74 | 75 | log.info("Sent telemetry (%d bytes) to %s:%d" % (packet.nbytes, host, port)) 76 | 77 | time.sleep(1) 78 | 79 | except KeyboardInterrupt: 80 | log.info("Received Ctrl-C. Stopping telemetry stream.") 81 | 82 | except Exception as e: 83 | log.error("TLM send error: %s" % str(e)) 84 | 85 | log.end() 86 | 87 | 88 | if __name__ == "__main__": 89 | main() 90 | -------------------------------------------------------------------------------- /ait/core/ccsds.py: -------------------------------------------------------------------------------- 1 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 2 | # Bespoke Link to Instruments and Small Satellites (BLISS) 3 | # 4 | # Copyright 2017, by the California Institute of Technology. ALL 5 | # RIGHTS RESERVED. United States Government Sponsorship 6 | # acknowledged. Any commercial use must be negotiated with the Office 7 | # of Technology Transfer at the California Institute of Technology. 8 | # 9 | # This software may be subject to U.S. export control laws. By 10 | # accepting this software, the user agrees to comply with all 11 | # applicable U.S. export laws and regulations. User has the 12 | # responsibility to obtain export licenses, or other export authority 13 | # as may be required before exporting such information to foreign 14 | # countries or providing access to foreign persons. 15 | """ 16 | Consultative Committee for Space Data Systems (CCSDS) 17 | 18 | The ait.core.ccsds module provides CCSDS header definitions and 19 | datatypes. 20 | """ 21 | from ait.core import json 22 | from ait.core import tlm 23 | from ait.core import util 24 | 25 | 26 | class CcsdsDefinition(json.SlotSerializer, object): 27 | """A :class:`CcsdsDefinition` is analogous to a 28 | :class:`PacketDefinition`, except it defines the expected values 29 | in a CCSDS header. 30 | 31 | :class:`CcsdsDefinition`s are most often specified in a ``ccsds:`` 32 | block within a YAML ``!Command`` or ``!Packet`` definition. 33 | """ 34 | 35 | __slots__ = "version", "type", "secondary", "apid", "seqflags", "length" 36 | 37 | def __init__(self, *args, **kwargs): 38 | self.version = kwargs.get("version", 0) 39 | self.type = kwargs.get("type", 0) 40 | self.secondary = kwargs.get("secondary", None) 41 | self.apid = kwargs.get("apid", 0) 42 | self.seqflags = kwargs.get("seqflags", 3) # No segmentation 43 | self.length = kwargs.get("length", 0) 44 | 45 | def __repr__(self): 46 | return util.toRepr(self) 47 | 48 | @property 49 | def shflag(self): 50 | """Indicates whether a CCSDS Secondary Header is present.""" 51 | return 1 if self.secondary else 0 52 | 53 | 54 | class CcsdsHeader(tlm.Packet): 55 | """A :class:`CcsdsHeader` is just like any other :class:`Packet`, 56 | except that the CCSDS (primary) header :class:`FieldDefinition`s 57 | are already defined. That is, there is no need to pass in a 58 | :class`PacketDefinition` at initialization, only the underlying 59 | packet data to decode as a CCSDS header. 60 | """ 61 | 62 | # NOTE: CcsdsHeader.Definition is distinct from a CcsdsDefinition. 63 | # The former specifies how to decode the fields of a CCSDS header. 64 | # The latter defines the expected values for those fields within a 65 | # a particular type of packet. 66 | 67 | Definition = tlm.PacketDefinition( 68 | name="CCSDS_Header", 69 | fields=[ 70 | tlm.FieldDefinition(name="version", bytes=0, type="U8", mask=0xE0), 71 | tlm.FieldDefinition(name="type", bytes=0, type="U8", mask=0x10), 72 | tlm.FieldDefinition(name="shflag", bytes=0, type="U8", mask=0x08), 73 | tlm.FieldDefinition(name="apid", bytes=[0, 1], type="MSB_U16", mask=0x07FF), 74 | tlm.FieldDefinition( 75 | name="seqflags", 76 | bytes=2, 77 | type="U8", 78 | mask=0xC0, 79 | enum={ 80 | 0: "Continuation Segment", 81 | 1: "First Segment", 82 | 2: "Last Segment", 83 | 3: "Unsegmented", 84 | }, 85 | ), 86 | tlm.FieldDefinition( 87 | name="seqcount", bytes=[2, 3], type="MSB_U16", mask=0x3FFF 88 | ), 89 | tlm.FieldDefinition(name="length", bytes=[4, 5], type="MSB_U16"), 90 | ], 91 | ) 92 | 93 | def __init__(self, data=None): 94 | super(CcsdsHeader, self).__init__(CcsdsHeader.Definition, data) 95 | self.seqflags = 3 96 | -------------------------------------------------------------------------------- /ait/core/coord.py: -------------------------------------------------------------------------------- 1 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 2 | # Bespoke Link to Instruments and Small Satellites (BLISS) 3 | # 4 | # Copyright 2013, by the California Institute of Technology. ALL RIGHTS 5 | # RESERVED. United States Government Sponsorship acknowledged. Any 6 | # commercial use must be negotiated with the Office of Technology Transfer 7 | # at the California Institute of Technology. 8 | # 9 | # This software may be subject to U.S. export control laws. By accepting 10 | # this software, the user agrees to comply with all applicable U.S. export 11 | # laws and regulations. User has the responsibility to obtain export licenses, 12 | # or other export authority as may be required before exporting such 13 | # information to foreign countries or providing access to foreign persons. 14 | """ 15 | AIT Coordinate Functions 16 | 17 | The ait.core.coord module provides various coordinate manpulation 18 | and transformation functions. 19 | """ 20 | import math 21 | 22 | from ait.core import dmc 23 | 24 | 25 | class Ellipsoid(object): 26 | """An ellipsoid is the three dimensional analogue of an ellipse, used 27 | here to approximate the geoid. See WGS84. 28 | 29 | """ 30 | 31 | def __init__(self, a, b): 32 | """Creates a new Ellipsoid with the given semimajor and semiminor 33 | axes. 34 | """ 35 | self.a = a 36 | self.b = b 37 | self.a2 = a**2 38 | self.b2 = b**2 39 | self.f = (a - b) / a 40 | self.e2 = 1 - (self.b2 / self.a2) 41 | self.ep2 = (self.a2 - self.b2) / self.b2 42 | 43 | 44 | WGS84 = Ellipsoid(a=6378137, b=6356752.3142) 45 | 46 | 47 | def cbrt(x): 48 | """Returns the cube root of x.""" 49 | if x >= 0: 50 | return math.pow(x, 1.0 / 3.0) 51 | else: 52 | return -math.pow(abs(x), 1.0 / 3.0) 53 | 54 | 55 | def eci2ecef(x, y, z, gmst=None): 56 | """Converts the given ECI coordinates to ECEF at the given Greenwich 57 | Mean Sidereal Time (GMST) (defaults to now). 58 | 59 | This code was adapted from 60 | `shashwatak/satellite-js `_ 61 | and http://ccar.colorado.edu/ASEN5070/handouts/coordsys.doc 62 | 63 | """ 64 | if gmst is None: 65 | gmst = dmc.toGMST() 66 | 67 | X = (x * math.cos(gmst)) + (y * math.sin(gmst)) # noqa 68 | Y = (x * (-math.sin(gmst))) + (y * math.cos(gmst)) # noqa 69 | Z = z # noqa 70 | 71 | return X, Y, Z 72 | 73 | 74 | def eci2geodetic(x, y, z, gmst=None, ellipsoid=None): 75 | """Converts the given ECI coordinates to Geodetic coordinates at the 76 | given Greenwich Mean Sidereal Time (GMST) (defaults to now) and with 77 | the given ellipsoid (defaults to WGS84). 78 | 79 | This code was adapted from 80 | `shashwatak/satellite-js `_ 81 | and http://www.celestrak.com/columns/v02n03/ 82 | 83 | """ 84 | if gmst is None: 85 | gmst = dmc.toGMST() 86 | 87 | if ellipsoid is None: 88 | ellipsoid = WGS84 89 | 90 | a = WGS84.a 91 | f = WGS84.f 92 | r = math.sqrt((x * x) + (y * y)) 93 | e2 = (2 * f) - (f * f) 94 | lon = math.atan2(y, x) - gmst 95 | k = 0 96 | kmax = 20 97 | lat = math.atan2(z, r) 98 | 99 | while k < kmax: 100 | slat = math.sin(lat) 101 | C = 1 / math.sqrt(1 - e2 * (slat * slat)) # noqa 102 | lat = math.atan2(z + (a * C * e2 * slat), r) 103 | k += 1 104 | 105 | z = (r / math.cos(lat)) - (a * C) 106 | 107 | return lat, lon, z 108 | -------------------------------------------------------------------------------- /ait/core/data/evr_schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "EVR Dictionary Schema", 4 | "description": "EVR Dictionary Schema. See http://json-schema.org/ for more details on how to create this schema.", 5 | "type": "array", 6 | "items": { 7 | "required": ["evr", "name", "code"], 8 | "additionalProperties": false, 9 | "properties": { 10 | "evr": { 11 | "type": "string" 12 | }, 13 | "name": { 14 | "type": "string" 15 | }, 16 | "desc": { 17 | "type": "string" 18 | }, 19 | "code": { 20 | "type": "integer" 21 | }, 22 | "message": { 23 | "type": "string" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ait/core/data/limits_schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "Limit Dictionary Schema", 4 | "description": "Limit Dictionary Schema. See http://json-schema.org/ for more details on how to create this schema.", 5 | "type": "array", 6 | "items": { 7 | "required": ["limit", "source"], 8 | "anyOf": [{ 9 | "required": ["lower"] 10 | }, { 11 | "required": ["upper"] 12 | }, { 13 | "required": ["value"] 14 | }], 15 | "additionalProperties": false, 16 | "properties": { 17 | "limit": { 18 | "type": "string" 19 | }, 20 | "source": { 21 | "type": "string" 22 | }, 23 | "desc": { 24 | "type": "string" 25 | }, 26 | "units": { 27 | "type": "string" 28 | }, 29 | "lower": { 30 | "type": "object", 31 | "additionalProperties": false, 32 | "properties": { 33 | "error": { 34 | "type": "number" 35 | }, 36 | "warn": { 37 | "type": "number" 38 | } 39 | } 40 | }, 41 | "upper": { 42 | "type": "object", 43 | "additionalProperties": false, 44 | "properties": { 45 | "error": { 46 | "type": "number" 47 | }, 48 | "warn": { 49 | "type": "number" 50 | } 51 | } 52 | }, 53 | "value": { 54 | "type": "object", 55 | "additionalProperties": false, 56 | "properties": { 57 | "error": { 58 | "type": [ "string", "number", "array" ], 59 | "minLength": 1 60 | }, 61 | "warn": { 62 | "type": [ "string", "number", "array" ], 63 | "minLength": 1 64 | } 65 | } 66 | }, 67 | "when": { 68 | "type": "string" 69 | }, 70 | "persist": { 71 | "type": "number" 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /ait/core/data/table_schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "Flight Software Command Table Schema", 4 | "description": "Flight Software Command Table Schema", 5 | "type": "array", 6 | "items": { 7 | "required": ["fswtable", "name", "delimiter", "uptype", "size", "header", "columns"], 8 | "additionalProperties": false, 9 | "properties": { 10 | "fswtable": { 11 | "type": "string" 12 | }, 13 | "name": { 14 | "type": "string" 15 | }, 16 | "delimiter": { 17 | "type": "string" 18 | }, 19 | "uptype": { 20 | "type": "integer" 21 | }, 22 | "size": { 23 | "type": "integer" 24 | }, 25 | "header": { 26 | "type": "array", 27 | "items": { 28 | "type": "object", 29 | "additionalProperties": false, 30 | "required": ["fswcolumn", "name", "type"], 31 | "properties": { 32 | "fswcolumn": { 33 | "type": "string" 34 | }, 35 | "name": { 36 | "type": "string" 37 | }, 38 | "desc": { 39 | "type": "string" 40 | }, 41 | "type": { 42 | "type": "string" 43 | }, 44 | "enum": { 45 | "type": "object", 46 | "description": "TODO: Does not check valid enumeration" 47 | } 48 | } 49 | } 50 | }, 51 | "columns": { 52 | "type": "array", 53 | "items": { 54 | "type": "object", 55 | "additionalProperties": false, 56 | "required": ["fswcolumn", "name", "type"], 57 | "properties": { 58 | "fswcolumn": { 59 | "type": "string" 60 | }, 61 | "name": { 62 | "type": "string" 63 | }, 64 | "desc": { 65 | "type": "string" 66 | }, 67 | "type": { 68 | "type": "string" 69 | }, 70 | "enum": { 71 | "type": "object", 72 | "description": "TODO: Does not check valid enumeration" 73 | } 74 | } 75 | } 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /ait/core/json.py: -------------------------------------------------------------------------------- 1 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 2 | # Bespoke Link to Instruments and Small Satellites (BLISS) 3 | # 4 | # Copyright 2017, by the California Institute of Technology. ALL RIGHTS 5 | # RESERVED. United States Government Sponsorship acknowledged. Any 6 | # commercial use must be negotiated with the Office of Technology Transfer 7 | # at the California Institute of Technology. 8 | # 9 | # This software may be subject to U.S. export control laws. By accepting 10 | # this software, the user agrees to comply with all applicable U.S. export 11 | # laws and regulations. User has the responsibility to obtain export licenses, 12 | # or other export authority as may be required before exporting such 13 | # information to foreign countries or providing access to foreign persons. 14 | """ 15 | AIT Javascript Object Notation (JSON) 16 | 17 | The ait.core.json module provides JSON utilities and mixin classes 18 | for encoding and decoding between AIT data structures and JSON. 19 | """ 20 | import collections.abc 21 | from typing import List 22 | 23 | 24 | def slotsToJSON(obj, slots=None): # noqa 25 | """Converts the given Python object to one suitable for Javascript 26 | Object Notation (JSON) serialization via :func:`json.dump` or 27 | :func:`json.dumps`. This function delegates to :func:`toJSON`. 28 | 29 | Specifically only attributes in the list of *slots* are converted. 30 | If *slots* is not provided, it defaults to the object's 31 | ``__slots__` and any inherited ``__slots__``. 32 | 33 | To omit certain slots from serialization, the object may define a 34 | :meth:`__jsonOmit__(key, val)` method. When the method returns 35 | True for any particular slot name (i.e. key) and value 36 | combination, the slot will not serialized. 37 | """ 38 | if slots is None: 39 | slots = list(obj.__slots__) if hasattr(obj, "__slots__") else [] 40 | for base in obj.__class__.__bases__: 41 | if hasattr(base, "__slots__"): 42 | slots.extend(base.__slots__) 43 | 44 | test_omit = hasattr(obj, "__jsonOmit__") and callable(obj.__jsonOmit__) 45 | result = {} 46 | 47 | for slot in slots: 48 | key = slot[1:] if slot.startswith("_") else slot 49 | val = getattr(obj, slot, None) 50 | 51 | if test_omit is False or obj.__jsonOmit__(key, val) is False: 52 | result[key] = toJSON(val) 53 | 54 | return result 55 | 56 | 57 | def toJSON(obj): # noqa 58 | """Converts the given Python object to one suitable for Javascript 59 | Object Notation (JSON) serialization via :func:`json.dump` or 60 | :func:`json.dumps`. If the Python object has a :meth:`toJSON` 61 | method, it is always given preference and will be called to peform 62 | the conversion. 63 | 64 | Otherwise, plain mapping and sequence types are converted to 65 | Python dictionaries and lists, respectively, by recursively 66 | calling this :func:`toJSON` function on mapping keys and values or 67 | iterable items. Python primitive types handled natively by the 68 | JSON encoder (``int``, ``long``, ``float``, ``str``, ``unicode``, 69 | and ``None``) are returned as-is. 70 | 71 | If no other conversion is appropriate, the Python builtin function 72 | :func:`str` is used to convert the object. 73 | """ 74 | if hasattr(obj, "toJSON") and callable(obj.toJSON): 75 | result = obj.toJSON() 76 | elif isinstance(obj, (int, float, str)) or obj is None: 77 | result = obj 78 | elif isinstance(obj, collections.abc.Mapping): 79 | result = {toJSON(key): toJSON(obj[key]) for key in obj} 80 | elif isinstance(obj, collections.abc.Sequence): 81 | result = [toJSON(item) for item in obj] 82 | else: 83 | result = str(obj) 84 | 85 | return result 86 | 87 | 88 | class SlotSerializer(object): 89 | __slots__: List[str] = [] 90 | 91 | def __jsonOmit__(self, key, val): # noqa 92 | return val is None or val == "" 93 | 94 | def toJSON(self): # noqa 95 | return slotsToJSON(self) 96 | -------------------------------------------------------------------------------- /ait/core/notify.py: -------------------------------------------------------------------------------- 1 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 2 | # Bespoke Link to Instruments and Small Satellites (BLISS) 3 | # 4 | # Copyright 2018, by the California Institute of Technology. ALL RIGHTS 5 | # RESERVED. United States Government Sponsorship acknowledged. Any 6 | # commercial use must be negotiated with the Office of Technology Transfer 7 | # at the California Institute of Technology. 8 | # 9 | # This software may be subject to U.S. export control laws. By accepting 10 | # this software, the user agrees to comply with all applicable U.S. export 11 | # laws and regulations. User has the responsibility to obtain export licenses, 12 | # or other export authority as may be required before exporting such 13 | # information to foreign countries or providing access to foreign persons. 14 | import smtplib 15 | from email.mime.text import MIMEText 16 | 17 | import ait 18 | from ait.core import log 19 | 20 | 21 | def trigger_notification(trigger, msg): 22 | """""" 23 | email_triggers = ait.config.get("notifications.email.triggers", []) 24 | text_triggers = ait.config.get("notifications.text.triggers", []) 25 | 26 | if trigger in email_triggers: 27 | send_email_alert(msg) 28 | 29 | if trigger in text_triggers: 30 | send_text_alert(msg) 31 | 32 | 33 | def send_email_alert(msg, recipients=None): 34 | """""" 35 | if not recipients: 36 | recipients = ait.config.get("notifications.email.recipients", []) 37 | 38 | _send_email(msg, recipients) 39 | 40 | 41 | def send_text_alert(msg, recipients=None): 42 | """""" 43 | if not recipients: 44 | recipients = ait.config.get("notifications.text.recipients", []) 45 | 46 | _send_email(msg, recipients) 47 | 48 | 49 | def _send_email(message, recipients): 50 | """""" 51 | if type(recipients) != list: 52 | recipients = [recipients] 53 | 54 | if len(recipients) == 0 or any([i is None for i in recipients]): 55 | m = ( 56 | "Email recipient list error. Unable to send email. " 57 | "Recipient list length: {} Recipients: {}" 58 | ).format(len(recipients), ", ".join(recipients)) 59 | log.error(m) 60 | return 61 | 62 | server = ait.config.get("notifications.smtp.server", None) 63 | port = ait.config.get("notifications.smtp.port", None) 64 | un = ait.config.get("notifications.smtp.username", None) 65 | pw = ait.config.get("notifications.smtp.password", None) 66 | 67 | # if server is None or port is None or un is None or pw is None: 68 | if server is None or port is None: 69 | log.error("Email SMTP connection parameter error. Please check config.") 70 | return 71 | 72 | subject = ait.config.get("notifications.smtp.subject", "AIT Notification") 73 | 74 | # From address must have a valid @server, otherwise texts will not works 75 | fromaddr = ait.config.get("notifications.smtp.from", "ait-notify@%s" % server) 76 | 77 | msg = MIMEText(message) 78 | msg["Subject"] = subject 79 | msg["To"] = ", ".join(recipients) 80 | msg["From"] = fromaddr 81 | 82 | try: 83 | if un is None or pw is None: 84 | s = smtplib.SMTP() 85 | s.connect(server, port) 86 | s.sendmail(fromaddr, recipients, msg.as_string()) 87 | s.quit() 88 | else: 89 | s = smtplib.SMTP_SSL(server, port) 90 | s.login(un, pw) 91 | s.sendmail(fromaddr, recipients, msg.as_string()) 92 | s.quit() 93 | log.info("Email notification sent") 94 | except smtplib.SMTPException as e: 95 | log.error("Failed to send email notification.") 96 | log.error(e) 97 | -------------------------------------------------------------------------------- /ait/core/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NASA-AMMOS/AIT-Core/35679f7fd7191be998f95aa797f591b89ec3976e/ait/core/py.typed -------------------------------------------------------------------------------- /ait/core/server/__init__.py: -------------------------------------------------------------------------------- 1 | from .broker import * # noqa 2 | from .config import ZmqConfig # noqa 3 | from .handler import Handler # noqa 4 | from .plugin import Plugin # noqa 5 | from .plugin import PluginConfig # noqa 6 | from .plugin import PluginType # noqa 7 | from .process import * # noqa 8 | from .server import * # noqa 9 | -------------------------------------------------------------------------------- /ait/core/server/config.py: -------------------------------------------------------------------------------- 1 | import ait.core.server 2 | 3 | 4 | class ZmqConfig: 5 | """ 6 | Configuration methods associated with ZeroMQ 7 | """ 8 | 9 | @staticmethod 10 | def get_xsub_url(): 11 | return ait.config.get("server.xsub", ait.SERVER_DEFAULT_XSUB_URL) 12 | 13 | @staticmethod 14 | def get_xpub_url(): 15 | return ait.config.get("server.xpub", ait.SERVER_DEFAULT_XPUB_URL) 16 | -------------------------------------------------------------------------------- /ait/core/server/handler.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta 2 | from abc import abstractmethod 3 | 4 | 5 | class Handler(object): 6 | """ 7 | This is the base Handler class that all custom handlers must inherit 8 | from. All custom handlers must implement the handle method, which will 9 | called by the stream the handler is attached to when the stream receives 10 | data. 11 | """ 12 | 13 | __metaclass__ = ABCMeta 14 | 15 | def __init__(self, input_type=None, output_type=None, **kwargs): 16 | """ 17 | Params: 18 | input_type: (optional) Specifies expected input type, used to 19 | validate handler workflow. Defaults to None. 20 | output_type: (optional) Specifies expected output type, used to 21 | validate handler workflow. Defaults to None 22 | **kwargs: (optional) Requirements dependent on child class. 23 | """ 24 | self.input_type = input_type 25 | self.output_type = output_type 26 | 27 | for key, value in kwargs.items(): 28 | setattr(self, key, value) 29 | 30 | def __repr__(self): 31 | return "" % (self.__class__.__name__) 32 | 33 | @abstractmethod 34 | def handle(self, input_data): 35 | """ 36 | Not implemented by base Handler class. 37 | This handle method must be implemented by any custom handler class 38 | that inherits from this base Handler. 39 | 40 | Params: 41 | input_data: If this is a stream's first handler, the input_data will 42 | be the message received by the stream. Otherwise it will 43 | be the output of the previous handler. 44 | """ 45 | pass 46 | -------------------------------------------------------------------------------- /ait/core/server/handlers/__init__.py: -------------------------------------------------------------------------------- 1 | from .ccsds_packet_handler import * # noqa 2 | from .packet_handler import * # noqa 3 | -------------------------------------------------------------------------------- /ait/core/server/handlers/packet_handler.py: -------------------------------------------------------------------------------- 1 | from ait.core import tlm 2 | from ait.core.server.handler import Handler 3 | 4 | 5 | class PacketHandler(Handler): 6 | def __init__(self, input_type=None, output_type=None, **kwargs): 7 | """ 8 | Params: 9 | input_type: (optional) Specifies expected input type, used to 10 | validate handler workflow. Defaults to None. 11 | output_type: (optional) Specifies expected output type, used to 12 | validate handler workflow. Defaults to None 13 | **kwargs: 14 | packet: (required) Name of packet, present in default tlm dict. 15 | Raises: 16 | ValueError: If packet is not present in kwargs. 17 | If packet is specified but not present in default tlm dict. 18 | """ 19 | super(PacketHandler, self).__init__(input_type, output_type) 20 | self.packet = kwargs.get("packet", None) 21 | 22 | if not self.packet: 23 | msg = 'PacketHandler: No packet name provided in handler config as key "packet"' 24 | raise ValueError(msg) 25 | 26 | tlm_dict = tlm.getDefaultDict() 27 | if self.packet not in tlm_dict: 28 | msg = "PacketHandler: Packet name {} not present in telemetry dictionary".format( 29 | self.packet 30 | ) 31 | msg += " Available packet types are {}".format(tlm_dict.keys()) 32 | raise ValueError(msg) 33 | 34 | self._pkt_defn = tlm_dict[self.packet] 35 | 36 | def handle(self, input_data): 37 | """ 38 | Params: 39 | input_data: message received by stream 40 | Returns: 41 | tuple of packet UID and message received by stream 42 | """ 43 | return (self._pkt_defn.uid, input_data) 44 | -------------------------------------------------------------------------------- /ait/core/server/plugins/PacketAccumulator.py: -------------------------------------------------------------------------------- 1 | from gevent import Greenlet 2 | from gevent import sleep 3 | 4 | from ait.core.server.plugins import Plugin 5 | 6 | 7 | class PacketAccumulator(Plugin): 8 | def __init__( 9 | self, 10 | inputs=None, 11 | outputs=None, 12 | zmq_args=None, 13 | timer_seconds=1, 14 | max_size_octets=1024, 15 | ): 16 | super().__init__(inputs, outputs, zmq_args) 17 | 18 | self.packet_queue = [] 19 | self.size_packet_queue_octets = 0 20 | 21 | self.glet = Greenlet.spawn(self.periodic_check) 22 | 23 | if timer_seconds > 0: 24 | self.timer_seconds = timer_seconds 25 | else: 26 | msg = f"PacketAccumulator -> timer value {timer_seconds} must be greater " 27 | msg += "than or equal to 0! Defaulting to 1 second." 28 | self.timer_seconds = 1 29 | self.log.error(msg) 30 | 31 | if max_size_octets > 0: 32 | self.max_size_octets = max_size_octets 33 | else: 34 | msg = f"PacketAccumulator -> Maximum accumulation size {max_size_octets} octets must " 35 | msg += "be greater than 0! Defaulting to 1024 octets." 36 | self.max_size_octets = 1024 37 | self.log.error(msg) 38 | 39 | def periodic_check(self): 40 | while True: 41 | sleep(self.timer_seconds) 42 | self.emit() 43 | 44 | def process(self, data, topic=None): 45 | data_len = len(data) 46 | # Does not fit, need to emit 47 | if self.size_packet_queue_octets + data_len > self.max_size_octets: 48 | self.emit() 49 | # It fits! Add and defer emission 50 | self.packet_queue.append(data) 51 | self.size_packet_queue_octets += data_len 52 | 53 | def emit(self): 54 | if self.packet_queue: 55 | payload = self.packet_queue.pop(0) 56 | for i in self.packet_queue: 57 | payload += i 58 | self.publish(payload) 59 | self.size_packet_queue_octets = 0 60 | self.packet_queue.clear() 61 | -------------------------------------------------------------------------------- /ait/core/server/plugins/PacketPadder.py: -------------------------------------------------------------------------------- 1 | from ait.core import log 2 | from ait.core.server.plugins import Plugin 3 | 4 | 5 | class PacketPadder(Plugin): 6 | def __init__( 7 | self, inputs=None, outputs=None, zmq_args=None, pad_octets=0, **kwargs 8 | ): 9 | if pad_octets >= 0: 10 | self.size_pad_octets = pad_octets 11 | else: 12 | self.size_pad_octets = 0 13 | log.error( 14 | f"PacketPadder -> Pad value{pad_octets} octets must be a \ 15 | positive integer! Bypassing padding!" 16 | ) 17 | super().__init__(inputs, outputs, zmq_args) 18 | 19 | def process(self, data, topic=None): 20 | if len(data) < self.size_pad_octets: 21 | fill = bytearray(self.size_pad_octets - len(data)) 22 | data = data + fill 23 | self.publish(data) 24 | -------------------------------------------------------------------------------- /ait/core/server/plugins/__init__.py: -------------------------------------------------------------------------------- 1 | from .data_archive import * # noqa 2 | from .limit_monitor import * # noqa 3 | from .openmct import * # noqa 4 | -------------------------------------------------------------------------------- /ait/core/server/plugins/data_archive.py: -------------------------------------------------------------------------------- 1 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 2 | # Bespoke Link to Instruments and Small Satellites (BLISS) 3 | # 4 | # Copyright 2019, by the California Institute of Technology. ALL RIGHTS 5 | # RESERVED. United States Government Sponsorship acknowledged. Any 6 | # commercial use must be negotiated with the Office of Technology Transfer 7 | # at the California Institute of Technology. 8 | # 9 | # This software may be subject to U.S. export control laws. By accepting 10 | # this software, the user agrees to comply with all applicable U.S. export 11 | # laws and regulations. User has the responsibility to obtain export licenses, 12 | # or other export authority as may be required before exporting such 13 | # information to foreign countries or providing access to foreign persons. 14 | import importlib 15 | from collections import defaultdict 16 | 17 | import gevent.monkey 18 | 19 | gevent.monkey.patch_all() 20 | 21 | import ait.core # noqa 22 | from ait.core import log, tlm 23 | from ait.core.server.plugin import Plugin 24 | 25 | 26 | class DataArchive(Plugin): 27 | def __init__( 28 | self, inputs, outputs, datastore="ait.core.db.InfluxDBBackend", **kwargs 29 | ): 30 | """ 31 | Attempts to connect to database backend. Plugin will not be created if 32 | connection fails. 33 | 34 | Creates base packet dictionary for decoding packets with packet UIDs as 35 | keys and packet definitions as values. 36 | 37 | Params: 38 | inputs: list of names of input streams to plugin 39 | outputs: list of names of plugin output streams 40 | datastore: path to database backend to use 41 | **kwargs: any args required for connecting to backend database 42 | Raises: 43 | ImportError: raised if provided database backend does not exist or 44 | cannot be imported 45 | Exception: raised if the backened database cannot be connected to 46 | for any reason 47 | """ 48 | super(DataArchive, self).__init__(inputs, outputs, **kwargs) 49 | 50 | self.datastore = datastore 51 | self.packet_dict = defaultdict(dict) 52 | for _k, v in tlm.getDefaultDict().items(): 53 | self.packet_dict[v.uid] = v 54 | 55 | try: 56 | mod, cls = self.datastore.rsplit(".", 1) 57 | self.dbconn = getattr(importlib.import_module(mod), cls)() 58 | self.dbconn.connect(**kwargs) 59 | log.info("Starting telemetry data archiving") 60 | except ImportError as e: 61 | log.error("Could not import specified datastore {}".format(self.datastore)) 62 | raise (e) 63 | except Exception as e: 64 | log.error( 65 | "Unable to connect to {} backend. Disabling data archive.".format( 66 | self.datastore 67 | ) 68 | ) 69 | raise (e) 70 | 71 | def process(self, input_data, topic=None, **kwargs): 72 | """ 73 | Splits tuple received from PacketHandler into packet UID and packet message. 74 | Decodes packet and inserts into database backend. 75 | Logs any exceptions raised. 76 | 77 | Params: 78 | input_data: message received from inbound stream through PacketHandler 79 | topic: name of inbound stream message received from 80 | **kwargs: any args required for connected to the backend 81 | """ 82 | try: 83 | uid, pkt = int(input_data[0]), input_data[1] 84 | defn = self.packet_dict[uid] 85 | decoded = tlm.Packet(defn, data=bytearray(pkt)) 86 | self.dbconn.insert(decoded, **kwargs) 87 | except Exception as e: 88 | log.error("Data archival failed with error: {}.".format(e)) 89 | -------------------------------------------------------------------------------- /ait/core/server/plugins/limit_monitor.py: -------------------------------------------------------------------------------- 1 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 2 | # Bespoke Link to Instruments and Small Satellites (BLISS) 3 | # 4 | # Copyright 2019, by the California Institute of Technology. ALL RIGHTS 5 | # RESERVED. United States Government Sponsorship acknowledged. Any 6 | # commercial use must be negotiated with the Office of Technology Transfer 7 | # at the California Institute of Technology. 8 | # 9 | # This software may be subject to U.S. export control laws. By accepting 10 | # this software, the user agrees to comply with all applicable U.S. export 11 | # laws and regulations. User has the responsibility to obtain export licenses, 12 | # or other export authority as may be required before exporting such 13 | # information to foreign countries or providing access to foreign persons. 14 | from collections import defaultdict 15 | 16 | import gevent.monkey 17 | 18 | gevent.monkey.patch_all() 19 | 20 | import ait.core 21 | from ait.core import limits, log, notify, tlm 22 | from ait.core.server.plugin import Plugin 23 | 24 | 25 | class TelemetryLimitMonitor(Plugin): 26 | def __init__(self, inputs, outputs, **kwargs): 27 | super(TelemetryLimitMonitor, self).__init__(inputs, outputs, **kwargs) 28 | 29 | self.limit_dict = defaultdict(dict) 30 | for k, v in limits.getDefaultDict().items(): 31 | packet, field = k.split(".") 32 | self.limit_dict[packet][field] = v 33 | 34 | self.packet_dict = defaultdict(dict) 35 | for _k, v in tlm.getDefaultDict().items(): 36 | self.packet_dict[v.uid] = v 37 | 38 | self.notif_thrshld = ait.config.get("notifications.options.threshold", 1) 39 | self.notif_freq = ait.config.get( 40 | "notifications.options.frequency", float("inf") 41 | ) 42 | 43 | self.limit_trip_repeats = {} 44 | log.info("Starting telemetry limit monitoring") 45 | 46 | def process(self, input_data, topic=None, **kwargs): 47 | try: 48 | pkt_id, pkt_data = int(input_data[0]), input_data[1] 49 | packet = self.packet_dict[pkt_id] 50 | decoded = tlm.Packet(packet, data=bytearray(pkt_data)) 51 | except Exception as e: 52 | log.error("TelemetryLimitMonitor: {}".format(e)) 53 | log.error( 54 | "TelemetryLimitMonitor received input_data that it is unable to process. Skipping input ..." 55 | ) 56 | return 57 | 58 | if packet.name in self.limit_dict: 59 | for field, defn in self.limit_dict[packet.name].items(): 60 | v = decoded._getattr(field) 61 | 62 | if packet.name not in self.limit_trip_repeats.keys(): 63 | self.limit_trip_repeats[packet.name] = {} 64 | 65 | if field not in self.limit_trip_repeats[packet.name].keys(): 66 | self.limit_trip_repeats[packet.name][field] = 0 67 | 68 | if defn.error(v): 69 | msg = "Field {} error out of limit with value {}".format(field, v) 70 | log.error(msg) 71 | 72 | self.limit_trip_repeats[packet.name][field] += 1 73 | repeats = self.limit_trip_repeats[packet.name][field] 74 | 75 | if repeats == self.notif_thrshld or ( 76 | repeats > self.notif_thrshld 77 | and (repeats - self.notif_thrshld) % self.notif_freq == 0 78 | ): 79 | notify.trigger_notification("limit-error", msg) 80 | 81 | elif defn.warn(v): 82 | msg = "Field {} warning out of limit with value {}".format(field, v) 83 | log.warn(msg) 84 | 85 | self.limit_trip_repeats[packet.name][field] += 1 86 | repeats = self.limit_trip_repeats[packet.name][field] 87 | 88 | if repeats == self.notif_thrshld or ( 89 | repeats > self.notif_thrshld 90 | and (repeats - self.notif_thrshld) % self.notif_freq == 0 91 | ): 92 | notify.trigger_notification("limit-warn", msg) 93 | 94 | else: 95 | self.limit_trip_repeats[packet.name][field] = 0 96 | -------------------------------------------------------------------------------- /ait/core/server/utils.py: -------------------------------------------------------------------------------- 1 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 2 | # Bespoke Link to Instruments and Small Satellites (BLISS) 3 | # 4 | # Copyright 2021, by the California Institute of Technology. ALL RIGHTS 5 | # RESERVED. United States Government Sponsorship acknowledged. Any 6 | # commercial use must be negotiated with the Office of Technology Transfer 7 | # at the California Institute of Technology. 8 | # 9 | # This software may be subject to U.S. export control laws. By accepting 10 | # this software, the user agrees to comply with all applicable U.S. export 11 | # laws and regulations. User has the responsibility to obtain export licenses, 12 | # or other export authority as may be required before exporting such 13 | # information to foreign countries or providing access to foreign persons. 14 | from ait.core.server import serial 15 | 16 | # Create serializer (populated from AIT config) 17 | serializer = serial.Serializer() 18 | 19 | 20 | def encode_message(topic, data): 21 | """Encode a message for sending via 0MQ 22 | 23 | Given a string topic name and a serializable data object, encode and prep 24 | the data for sending via `send_multipart` 25 | 26 | Returns a list of the form: 27 | [ 28 | Bytes object of String (UTF-8), 29 | Serialized data object 30 | ] 31 | 32 | If encoding fails None will be returned. 33 | 34 | """ 35 | try: 36 | enc = [bytes(topic, "utf-8"), serializer.serialize(data)] 37 | # TODO: This should be way less generic than Exception 38 | except Exception: 39 | enc = None 40 | 41 | return enc 42 | 43 | 44 | def decode_message(msg): 45 | """Decode a message received via 0MQ 46 | 47 | Given a message received from `recv_multipart`, decode the components. 48 | 49 | Returns a tuple of the form: 50 | ( 51 | UTF-8 string 52 | Deserialized data object 53 | ) 54 | 55 | If decoding fails a tuple of None objects will be returned. 56 | """ 57 | [topic, data] = msg 58 | 59 | try: 60 | tpc = topic.decode("utf-8") 61 | msg = serializer.deserialize(data) 62 | # TODO: This should be way less generic than Exception 63 | except Exception: 64 | tpc = None 65 | msg = None 66 | 67 | return (tpc, msg) 68 | -------------------------------------------------------------------------------- /ait/data/settings.yaml: -------------------------------------------------------------------------------- 1 | # A relative path 2 | config_path: "data/config" 3 | -------------------------------------------------------------------------------- /config/bsc.yaml: -------------------------------------------------------------------------------- 1 | # Example bsc configuration data. This should be customized for your 2 | # use case before use. 3 | 4 | capture_manager: 5 | root_log_directory: /tmp 6 | 7 | manager_server: 8 | host: localhost 9 | port: 8080 10 | 11 | handlers: 12 | - name: test1 13 | conn_type: udp 14 | address: ['', 8500] 15 | 16 | # A Handler's 'path' attribute allows for specification of 17 | # handler-specific sub-folders to meet a desired nesting structure in 18 | # the root log directory. 19 | path: additional_dir/test/%j 20 | 21 | # A Handlers 'file_name_pattern' attribute allows for custom file name 22 | # specification. The final handler log file path is passed through 23 | # strftime and format string substitution with the handler passed 24 | # as the kwargs for substitution. If nothing is present for the 25 | # 'file_name_pattern' a default of -.pcap is used. 26 | file_name_pattern: '%Y-%m-%d-randomUDPtestData-{name}.pcap' 27 | rotate_log: True 28 | 29 | - name: test2 30 | conn_type: udp 31 | address: ['', 8500] 32 | rotate_log: True 33 | 34 | - name: test3 35 | conn_type: udp 36 | address: ['', 8125] 37 | 38 | # Example logger for monitoring raw Ethernet frames 39 | # - name: ethernet_test 40 | # type: ethernet 41 | # address: [etho0, 0x55aa] 42 | -------------------------------------------------------------------------------- /config/cmd.yaml: -------------------------------------------------------------------------------- 1 | - !Command 2 | name: NO_OP 3 | opcode: 0x0001 4 | subsystem: CORE 5 | title: NO_OP 6 | desc: | 7 | Standard NO_OP command. 8 | 9 | - !Command 10 | name: SEQ_START 11 | opcode: 0x0002 12 | subsystem: CMD 13 | title: Start Sequence 14 | desc: | 15 | This command starts a specified command sequence. 16 | 17 | arguments: 18 | - !Argument 19 | name: sequence_id 20 | desc: Sequence ID 21 | units: none 22 | type: MSB_U16 23 | bytes: [0,1] 24 | 25 | - !Command 26 | name: SEQ_ENABLE_DISABLE 27 | opcode: 0x0003 28 | subsystem: CMD 29 | title: Enable/Disable Sequence 30 | desc: | 31 | This command enables or disabled the specified sequence. If a 32 | sequence to be disabled is currently executing, it will be 33 | interrupted. 34 | 35 | arguments: 36 | - !Argument 37 | name: sequence_id 38 | desc: Sequence ID 39 | units: none 40 | type: MSB_U16 41 | bytes: [0,1] 42 | 43 | - !Argument 44 | name: enable 45 | desc: Enable 46 | units: none 47 | type: U8 48 | bytes: 2 49 | enum: 50 | 0: DISABLED 51 | 1: ENABLED 52 | 53 | - !Command 54 | name: SEND_FIXED_ARG 55 | opcode: 0x0004 56 | subsystem: CMD 57 | title: Send Fixed Argument 58 | desc: | 59 | This command tests sending a fixed argument. 60 | 61 | arguments: 62 | - !Fixed 63 | name: fixed_arg 64 | desc: Fixed Argument 65 | units: none 66 | type: MSB_U16 67 | bytes: [0,1] 68 | value: 5 69 | 70 | - !Command 71 | name: SEND_STR_ARG 72 | opcode: 0x0005 73 | subsystem: CMD 74 | title: Send string argument value 75 | desc: | 76 | This command tests sending a single string argument. 77 | 78 | arguments: 79 | - !Argument 80 | name: str_arg 81 | desc: String Argument 82 | units: none 83 | type: S16 84 | bytes: [0,15] 85 | 86 | # below is example of including additional command dictionary file 87 | # - !include core_set_op_mode.yaml 88 | -------------------------------------------------------------------------------- /config/core_set_op_mode.yaml: -------------------------------------------------------------------------------- 1 | # An example command for setting the operation 2 | # mode of an instrument. 3 | - !Command 4 | name: CORE_SET_OP_MODE 5 | opcode: 0x0006 6 | subsystem: CORE 7 | desc: | 8 | This command sets the operational mode. 9 | 10 | arguments: 11 | - !Argument 12 | name: mode 13 | desc: Mode 14 | units: none 15 | type: U8 16 | bytes: 0 17 | enum: 18 | 0: SAFE 19 | 1: IDLE 20 | 2: SCANNING 21 | 3: SCIENCE 22 | -------------------------------------------------------------------------------- /config/evr.yaml: -------------------------------------------------------------------------------- 1 | - !EVR 2 | name: NO_ERROR 3 | code: 0x0001 4 | desc: No error 5 | message: "No error" 6 | 7 | - !EVR 8 | name: EVR_1 9 | code: 0x0002 10 | desc: EVR 1 11 | message: "The first evr" 12 | 13 | - !EVR 14 | name: EVR_2 15 | code: 0x0003 16 | desc: EVR 2 17 | message: "The second evr" 18 | 19 | - !EVR 20 | name: EVR_3 21 | code: 0x0004 22 | desc: EVR 3 23 | message: "The third evr %s" 24 | -------------------------------------------------------------------------------- /config/leapseconds.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NASA-AMMOS/AIT-Core/35679f7fd7191be998f95aa797f591b89ec3976e/config/leapseconds.dat -------------------------------------------------------------------------------- /config/limits.yaml: -------------------------------------------------------------------------------- 1 | # Min/Max Range Example - Simple case for telemetry value range 2 | - !Limit 3 | source: 1553_HS_Packet.Voltage_A 4 | desc: Voltage A 5 | units: Volts 6 | lower: 7 | error: 5.0 8 | warn: 10.0 9 | upper: 10 | error: 45.0 11 | warn: 40.0 12 | 13 | # Error enumerations example - throw error if value is in enum list 14 | - !Limit 15 | source: Ethernet_HS_Packet.product_type 16 | desc: Ethernet Product Type field 17 | value: 18 | error: MEM_DUMP 19 | warn: 20 | - TABLE_FOO 21 | - TABLE_BAR 22 | -------------------------------------------------------------------------------- /config/table.yaml: -------------------------------------------------------------------------------- 1 | # Dictionary describing the FSW table formats that are 2 | # uploaded into FSW table memory. Uses OrderedDict to maintain 3 | # the order, and contains the name of each column, description, 4 | # datatype, units, and the maximum length for that column. 5 | 6 | - !FSWTable 7 | name: TestTable 8 | delimiter: "," 9 | uptype: 1 10 | size: 18 11 | header: 12 | - !FSWColumn 13 | name: uptype 14 | desc: This table's `uptype` field for use when decoding 15 | type: U8 16 | 17 | - !FSWColumn 18 | name: HEADER_COLUMN_TWO 19 | desc: The second column in our header 20 | type: U8 21 | 22 | - !FSWColumn 23 | name: HEADER_COLUMN_THREE 24 | desc: The third column in our header 25 | type: U8 26 | 27 | columns: 28 | - !FSWColumn 29 | name: COLUMN_ONE 30 | desc: First FSW Table Column 31 | type: MSB_U16 32 | 33 | - !FSWColumn 34 | name: COLUMN_TWO 35 | desc: Second FSW Table Column 36 | type: MSB_U16 37 | 38 | - !FSWColumn 39 | name: COLUMN_THREE 40 | desc: Third FSW Table Column 41 | type: U8 42 | enum: 43 | 0: TEST_ENUM_0 44 | 1: TEST_ENUM_1 45 | 2: TEST_ENUM_2 46 | 3: TEST_ENUM_3 47 | -------------------------------------------------------------------------------- /config/tlm.yaml: -------------------------------------------------------------------------------- 1 | - !Packet 2 | name: 1553_HS_Packet 3 | desc: Ethernet 1553 packet used to monitor telemetry in real-time 4 | functions: 5 | CurrA_Fx(dn): (dn - 2) / 1234.0 6 | Difference(x,y): x - y 7 | 8 | fields: 9 | - !Field 10 | name: Voltage_A 11 | desc: Voltage A as a 14-bit DN. Conversion to engineering units is TBD. 12 | units: Volts 13 | type: MSB_U16 14 | - !Field 15 | name: Voltage_B 16 | desc: Voltage B as a 14-bit DN. Conversion to engineering units is TBD. 17 | units: Volts 18 | type: MSB_U16 19 | - !Field 20 | name: Voltage_C 21 | desc: Voltage C as a 14-bit DN. Conversion to engineering units is TBD. 22 | units: Volts 23 | type: MSB_U16 24 | - !Field 25 | name: Voltage_D 26 | desc: Voltage D as a 14-bit DN. Conversion to engineering units is TBD. 27 | units: Volts 28 | type: MSB_U16 29 | aliases: 30 | icd: Voltage_D_Alias 31 | - !Field 32 | name: Current_A 33 | type: MSB_U16 34 | dntoeu: 35 | equation: CurrA_Fx(raw.Current_A) 36 | units: amperes 37 | 38 | derivations: 39 | - !Derivation 40 | name: Volt_Diff 41 | desc: Difference between Voltage_A and Voltage_B 42 | equation: Difference(Voltage_A, Voltage_B) 43 | units: Volts 44 | type: MSB_U16 45 | 46 | - !Packet 47 | name: Ethernet_HS_Packet 48 | desc: Ethernet Health and Status Packet 49 | fields: 50 | - !Field 51 | name: sync_word 52 | type: MSB_U32 53 | value: 0x01234567 54 | - !Field 55 | name: time_created 56 | type: TIME64 57 | desc: Time when data product created (seconds since GPS/ISS epoch) 58 | - !Field 59 | name: product_type 60 | type: U8 61 | enum: 62 | 0: TABLE_FOO 63 | 1: TABLE_BAR 64 | 2: MEM_DUMP 65 | 3: HEALTH_AND_STATUS 66 | when: product_type == 3 67 | - !Field 68 | name: product_length 69 | type: MSB_U32 70 | desc: Product length (including this header) 71 | mask: 0x00FFFFFF 72 | - !Field 73 | name: VoltageSampleTime 74 | desc: Time measurements were taken 75 | type: TIME64 76 | - !Field 77 | name: Voltage_A 78 | desc: Voltage A as a 14-bit DN. Conversion to engineering units is TBD. 79 | type: MSB_U16 80 | - !Field 81 | name: Voltage_B 82 | desc: Voltage B as a 14-bit DN. Conversion to engineering units is TBD. 83 | type: MSB_U16 84 | - !Field 85 | name: Voltage_C 86 | desc: Voltage C as a 14-bit DN. Conversion to engineering units is TBD. 87 | type: MSB_U16 88 | - !Field 89 | name: Voltage_D 90 | desc: Voltage D as a 14-bit DN. Conversion to engineering units is TBD. 91 | type: MSB_U16 92 | - !Field 93 | name: footer 94 | type: MSB_U32 95 | value: 0x89ABCDEF 96 | 97 | - !include ccsds_header.yaml 98 | -------------------------------------------------------------------------------- /doc/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx-rtd-theme 2 | sphinxcontrib-httpdomain 3 | -------------------------------------------------------------------------------- /doc/source/_static/ccsds_prim_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NASA-AMMOS/AIT-Core/35679f7fd7191be998f95aa797f591b89ec3976e/doc/source/_static/ccsds_prim_header.png -------------------------------------------------------------------------------- /doc/source/ait.core.api.rst: -------------------------------------------------------------------------------- 1 | ait.core.api module 2 | =================== 3 | 4 | .. automodule:: ait.core.api 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_bsc.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_bsc module 2 | ============================ 3 | 4 | .. automodule:: ait.core.bin.ait_bsc 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_bsc_create_handler.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_bsc\_create\_handler module 2 | ============================================= 3 | 4 | .. automodule:: ait.core.bin.ait_bsc_create_handler 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_bsc_stop_handler.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_bsc\_stop\_handler module 2 | =========================================== 3 | 4 | .. automodule:: ait.core.bin.ait_bsc_stop_handler 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_ccsds_send_example.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_ccsds\_send\_example module 2 | ============================================= 3 | 4 | .. automodule:: ait.core.bin.ait_ccsds_send_example 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_cmd_hist.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_cmd\_hist module 2 | ================================== 3 | 4 | .. automodule:: ait.core.bin.ait_cmd_hist 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_cmd_send.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_cmd\_send module 2 | ================================== 3 | 4 | .. automodule:: ait.core.bin.ait_cmd_send 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_create_dirs.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_create\_dirs module 2 | ===================================== 3 | 4 | .. automodule:: ait.core.bin.ait_create_dirs 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_dict_writer.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_dict\_writer module 2 | ===================================== 3 | 4 | .. automodule:: ait.core.bin.ait_dict_writer 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_limits_find_dn.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_limits\_find\_dn module 2 | ========================================= 3 | 4 | .. automodule:: ait.core.bin.ait_limits_find_dn 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_mps_seq_convert.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_mps\_seq\_convert module 2 | ========================================== 3 | 4 | .. automodule:: ait.core.bin.ait_mps_seq_convert 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_pcap.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_pcap module 2 | ============================= 3 | 4 | .. automodule:: ait.core.bin.ait_pcap 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_pcap_segment.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_pcap\_segment module 2 | ====================================== 3 | 4 | .. automodule:: ait.core.bin.ait_pcap_segment 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_seq_decode.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_seq\_decode module 2 | ==================================== 3 | 4 | .. automodule:: ait.core.bin.ait_seq_decode 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_seq_encode.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_seq\_encode module 2 | ==================================== 3 | 4 | .. automodule:: ait.core.bin.ait_seq_encode 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_seq_print.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_seq\_print module 2 | =================================== 3 | 4 | .. automodule:: ait.core.bin.ait_seq_print 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_seq_send.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_seq\_send module 2 | ================================== 3 | 4 | .. automodule:: ait.core.bin.ait_seq_send 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_server.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_server module 2 | =============================== 3 | 4 | .. automodule:: ait.core.bin.ait_server 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_table_decode.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_table\_decode module 2 | ====================================== 3 | 4 | .. automodule:: ait.core.bin.ait_table_decode 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_table_encode.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_table\_encode module 2 | ====================================== 3 | 4 | .. automodule:: ait.core.bin.ait_table_encode 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_tlm_csv.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_tlm\_csv module 2 | ================================= 3 | 4 | .. automodule:: ait.core.bin.ait_tlm_csv 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_tlm_db_insert.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_tlm\_db\_insert module 2 | ======================================== 3 | 4 | .. automodule:: ait.core.bin.ait_tlm_db_insert 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_tlm_send.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_tlm\_send module 2 | ================================== 3 | 4 | .. automodule:: ait.core.bin.ait_tlm_send 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_tlm_simulate.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_tlm\_simulate module 2 | ====================================== 3 | 4 | .. automodule:: ait.core.bin.ait_tlm_simulate 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.ait_yaml_validate.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin.ait\_yaml\_validate module 2 | ======================================= 3 | 4 | .. automodule:: ait.core.bin.ait_yaml_validate 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.bin.rst: -------------------------------------------------------------------------------- 1 | ait.core.bin package 2 | ==================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | ait.core.bin.ait_bsc 11 | ait.core.bin.ait_bsc_create_handler 12 | ait.core.bin.ait_bsc_stop_handler 13 | ait.core.bin.ait_ccsds_send_example 14 | ait.core.bin.ait_cmd_hist 15 | ait.core.bin.ait_cmd_send 16 | ait.core.bin.ait_create_dirs 17 | ait.core.bin.ait_dict_writer 18 | ait.core.bin.ait_limits_find_dn 19 | ait.core.bin.ait_mps_seq_convert 20 | ait.core.bin.ait_pcap 21 | ait.core.bin.ait_pcap_segment 22 | ait.core.bin.ait_seq_decode 23 | ait.core.bin.ait_seq_encode 24 | ait.core.bin.ait_seq_print 25 | ait.core.bin.ait_seq_send 26 | ait.core.bin.ait_server 27 | ait.core.bin.ait_table_decode 28 | ait.core.bin.ait_table_encode 29 | ait.core.bin.ait_tlm_csv 30 | ait.core.bin.ait_tlm_db_insert 31 | ait.core.bin.ait_tlm_send 32 | ait.core.bin.ait_tlm_simulate 33 | ait.core.bin.ait_yaml_validate 34 | 35 | Module contents 36 | --------------- 37 | 38 | .. automodule:: ait.core.bin 39 | :members: 40 | :undoc-members: 41 | :show-inheritance: 42 | -------------------------------------------------------------------------------- /doc/source/ait.core.bsc.rst: -------------------------------------------------------------------------------- 1 | ait.core.bsc module 2 | =================== 3 | 4 | .. automodule:: ait.core.bsc 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.ccsds.rst: -------------------------------------------------------------------------------- 1 | ait.core.ccsds module 2 | ===================== 3 | 4 | .. automodule:: ait.core.ccsds 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.cfg.rst: -------------------------------------------------------------------------------- 1 | ait.core.cfg module 2 | =================== 3 | 4 | .. automodule:: ait.core.cfg 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.cmd.rst: -------------------------------------------------------------------------------- 1 | ait.core.cmd module 2 | =================== 3 | 4 | .. automodule:: ait.core.cmd 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.coord.rst: -------------------------------------------------------------------------------- 1 | ait.core.coord module 2 | ===================== 3 | 4 | .. automodule:: ait.core.coord 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.db.rst: -------------------------------------------------------------------------------- 1 | ait.core.db module 2 | ================== 3 | 4 | .. automodule:: ait.core.db 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.dmc.rst: -------------------------------------------------------------------------------- 1 | ait.core.dmc module 2 | =================== 3 | 4 | .. automodule:: ait.core.dmc 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.dtype.rst: -------------------------------------------------------------------------------- 1 | ait.core.dtype module 2 | ===================== 3 | 4 | .. automodule:: ait.core.dtype 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.evr.rst: -------------------------------------------------------------------------------- 1 | ait.core.evr module 2 | =================== 3 | 4 | .. automodule:: ait.core.evr 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.gds.rst: -------------------------------------------------------------------------------- 1 | ait.core.gds module 2 | =================== 3 | 4 | .. automodule:: ait.core.gds 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.geom.rst: -------------------------------------------------------------------------------- 1 | ait.core.geom module 2 | ==================== 3 | 4 | .. automodule:: ait.core.geom 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.json.rst: -------------------------------------------------------------------------------- 1 | ait.core.json module 2 | ==================== 3 | 4 | .. automodule:: ait.core.json 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.limits.rst: -------------------------------------------------------------------------------- 1 | ait.core.limits module 2 | ====================== 3 | 4 | .. automodule:: ait.core.limits 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.log.rst: -------------------------------------------------------------------------------- 1 | ait.core.log module 2 | =================== 3 | 4 | .. automodule:: ait.core.log 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.notify.rst: -------------------------------------------------------------------------------- 1 | ait.core.notify module 2 | ====================== 3 | 4 | .. automodule:: ait.core.notify 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.pcap.rst: -------------------------------------------------------------------------------- 1 | ait.core.pcap module 2 | ==================== 3 | 4 | .. automodule:: ait.core.pcap 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.rst: -------------------------------------------------------------------------------- 1 | ait.core package 2 | ================ 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | ait.core.bin 11 | ait.core.server 12 | 13 | Submodules 14 | ---------- 15 | 16 | .. toctree:: 17 | :maxdepth: 4 18 | 19 | ait.core.api 20 | ait.core.bsc 21 | ait.core.ccsds 22 | ait.core.cfg 23 | ait.core.cmd 24 | ait.core.coord 25 | ait.core.db 26 | ait.core.dmc 27 | ait.core.dtype 28 | ait.core.evr 29 | ait.core.gds 30 | ait.core.geom 31 | ait.core.json 32 | ait.core.limits 33 | ait.core.log 34 | ait.core.notify 35 | ait.core.pcap 36 | ait.core.seq 37 | ait.core.table 38 | ait.core.tlm 39 | ait.core.util 40 | ait.core.val 41 | 42 | Module contents 43 | --------------- 44 | 45 | .. automodule:: ait.core 46 | :members: 47 | :undoc-members: 48 | :show-inheritance: 49 | -------------------------------------------------------------------------------- /doc/source/ait.core.seq.rst: -------------------------------------------------------------------------------- 1 | ait.core.seq module 2 | =================== 3 | 4 | .. automodule:: ait.core.seq 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.server.broker.rst: -------------------------------------------------------------------------------- 1 | ait.core.server.broker module 2 | ============================= 3 | 4 | .. automodule:: ait.core.server.broker 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.server.client.rst: -------------------------------------------------------------------------------- 1 | ait.core.server.client module 2 | ============================= 3 | 4 | .. automodule:: ait.core.server.client 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.server.config.rst: -------------------------------------------------------------------------------- 1 | ait.core.server.config module 2 | ============================= 3 | 4 | .. automodule:: ait.core.server.config 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.server.handler.rst: -------------------------------------------------------------------------------- 1 | ait.core.server.handler module 2 | ============================== 3 | 4 | .. automodule:: ait.core.server.handler 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.server.handlers.ccsds_packet_handler.rst: -------------------------------------------------------------------------------- 1 | ait.core.server.handlers.ccsds\_packet\_handler module 2 | ====================================================== 3 | 4 | .. automodule:: ait.core.server.handlers.ccsds_packet_handler 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.server.handlers.packet_handler.rst: -------------------------------------------------------------------------------- 1 | ait.core.server.handlers.packet\_handler module 2 | =============================================== 3 | 4 | .. automodule:: ait.core.server.handlers.packet_handler 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.server.handlers.rst: -------------------------------------------------------------------------------- 1 | ait.core.server.handlers package 2 | ================================ 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | ait.core.server.handlers.ccsds_packet_handler 11 | ait.core.server.handlers.packet_handler 12 | 13 | Module contents 14 | --------------- 15 | 16 | .. automodule:: ait.core.server.handlers 17 | :members: 18 | :undoc-members: 19 | :show-inheritance: 20 | -------------------------------------------------------------------------------- /doc/source/ait.core.server.plugin.rst: -------------------------------------------------------------------------------- 1 | ait.core.server.plugin module 2 | ============================= 3 | 4 | .. automodule:: ait.core.server.plugin 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.server.plugins.PacketAccumulator.rst: -------------------------------------------------------------------------------- 1 | ait.core.server.plugins.PacketAccumulator module 2 | ================================================ 3 | 4 | .. automodule:: ait.core.server.plugins.PacketAccumulator 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.server.plugins.PacketPadder.rst: -------------------------------------------------------------------------------- 1 | ait.core.server.plugins.PacketPadder module 2 | =========================================== 3 | 4 | .. automodule:: ait.core.server.plugins.PacketPadder 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.server.plugins.apid_routing.rst: -------------------------------------------------------------------------------- 1 | ait.core.server.plugins.apid\_routing module 2 | ============================================ 3 | 4 | .. automodule:: ait.core.server.plugins.apid_routing 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.server.plugins.data_archive.rst: -------------------------------------------------------------------------------- 1 | ait.core.server.plugins.data\_archive module 2 | ============================================ 3 | 4 | .. automodule:: ait.core.server.plugins.data_archive 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.server.plugins.limit_monitor.rst: -------------------------------------------------------------------------------- 1 | ait.core.server.plugins.limit\_monitor module 2 | ============================================= 3 | 4 | .. automodule:: ait.core.server.plugins.limit_monitor 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.server.plugins.openmct.rst: -------------------------------------------------------------------------------- 1 | ait.core.server.plugins.openmct module 2 | ====================================== 3 | 4 | .. automodule:: ait.core.server.plugins.openmct 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.server.plugins.rst: -------------------------------------------------------------------------------- 1 | ait.core.server.plugins package 2 | =============================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | ait.core.server.plugins.PacketAccumulator 11 | ait.core.server.plugins.PacketPadder 12 | ait.core.server.plugins.apid_routing 13 | ait.core.server.plugins.data_archive 14 | ait.core.server.plugins.limit_monitor 15 | ait.core.server.plugins.openmct 16 | 17 | Module contents 18 | --------------- 19 | 20 | .. automodule:: ait.core.server.plugins 21 | :members: 22 | :undoc-members: 23 | :show-inheritance: 24 | -------------------------------------------------------------------------------- /doc/source/ait.core.server.process.rst: -------------------------------------------------------------------------------- 1 | ait.core.server.process module 2 | ============================== 3 | 4 | .. automodule:: ait.core.server.process 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.server.rst: -------------------------------------------------------------------------------- 1 | ait.core.server package 2 | ======================= 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | ait.core.server.handlers 11 | ait.core.server.plugins 12 | 13 | Submodules 14 | ---------- 15 | 16 | .. toctree:: 17 | :maxdepth: 4 18 | 19 | ait.core.server.broker 20 | ait.core.server.client 21 | ait.core.server.config 22 | ait.core.server.handler 23 | ait.core.server.plugin 24 | ait.core.server.process 25 | ait.core.server.server 26 | ait.core.server.stream 27 | ait.core.server.utils 28 | 29 | Module contents 30 | --------------- 31 | 32 | .. automodule:: ait.core.server 33 | :members: 34 | :undoc-members: 35 | :show-inheritance: 36 | -------------------------------------------------------------------------------- /doc/source/ait.core.server.server.rst: -------------------------------------------------------------------------------- 1 | ait.core.server.server module 2 | ============================= 3 | 4 | .. automodule:: ait.core.server.server 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.server.stream.rst: -------------------------------------------------------------------------------- 1 | ait.core.server.stream module 2 | ============================= 3 | 4 | .. automodule:: ait.core.server.stream 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.server.utils.rst: -------------------------------------------------------------------------------- 1 | ait.core.server.utils module 2 | ============================ 3 | 4 | .. automodule:: ait.core.server.utils 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.table.rst: -------------------------------------------------------------------------------- 1 | ait.core.table module 2 | ===================== 3 | 4 | .. automodule:: ait.core.table 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.tlm.rst: -------------------------------------------------------------------------------- 1 | ait.core.tlm module 2 | =================== 3 | 4 | .. automodule:: ait.core.tlm 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.util.rst: -------------------------------------------------------------------------------- 1 | ait.core.util module 2 | ==================== 3 | 4 | .. automodule:: ait.core.util 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.core.val.rst: -------------------------------------------------------------------------------- 1 | ait.core.val module 2 | =================== 3 | 4 | .. automodule:: ait.core.val 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /doc/source/ait.rst: -------------------------------------------------------------------------------- 1 | ait namespace 2 | ============= 3 | 4 | .. py:module:: ait 5 | 6 | Subpackages 7 | ----------- 8 | 9 | .. toctree:: 10 | :maxdepth: 4 11 | 12 | ait.core 13 | -------------------------------------------------------------------------------- /doc/source/command_line.rst: -------------------------------------------------------------------------------- 1 | Command Line Utilities 2 | ====================== 3 | 4 | AIT provides a number of command line utilities for performing common operations and component initialization. Below is a breakdown of these utilities with a brief explanation of how they work and why you might use them. 5 | 6 | ---- 7 | 8 | Component Initialization Utilities 9 | ---------------------------------- 10 | 11 | The following commands are used to start up AIT services or components. 12 | 13 | ait-bsc 14 | ^^^^^^^^^ 15 | .. literalinclude:: ../../ait/core/bin/ait_bsc.py 16 | :start-after: ''' 17 | :end-before: ''' 18 | 19 | ---- 20 | 21 | Sequence Utilities 22 | ------------------ 23 | 24 | Utilities for the manipulation of command sequences. 25 | 26 | ait-seq-encode 27 | ^^^^^^^^^^^^^^^^ 28 | .. literalinclude:: ../../ait/core/bin/ait_seq_encode.py 29 | :start-after: ''' 30 | :end-before: ''' 31 | 32 | ait-seq-decode 33 | ^^^^^^^^^^^^^^^^ 34 | .. literalinclude:: ../../ait/core/bin/ait_seq_decode.py 35 | :start-after: ''' 36 | :end-before: ''' 37 | 38 | ait-seq-print 39 | ^^^^^^^^^^^^^^^ 40 | .. literalinclude:: ../../ait/core/bin/ait_seq_print.py 41 | :start-after: ''' 42 | :end-before: ''' 43 | 44 | ait-mps-seq-convert 45 | ^^^^^^^^^^^^^^^^^^^ 46 | .. literalinclude:: ../../ait/core/bin/ait_mps_seq_convert.py 47 | :start-after: ''' 48 | :end-before: ''' 49 | 50 | ____ 51 | 52 | Telemetry Utilities 53 | ___________________ 54 | 55 | ait-tlm-send 56 | ^^^^^^^^^^^^^^ 57 | .. literalinclude:: ../../ait/core/bin/ait_tlm_send.py 58 | :start-after: ''' 59 | :end-before: ''' 60 | 61 | ait-tlm-simulate 62 | ^^^^^^^^^^^^^^^^^^ 63 | .. literalinclude:: ../../ait/core/bin/ait_tlm_simulate.py 64 | :start-after: ''' 65 | :end-before: ''' 66 | 67 | ait-tlm-csv 68 | ^^^^^^^^^^^ 69 | .. literalinclude:: ../../ait/core/bin/ait_tlm_csv.py 70 | :start-after: ''' 71 | :end-before: ''' 72 | 73 | ____ 74 | 75 | Command Utilities 76 | _________________ 77 | 78 | ait-cmd-send 79 | ^^^^^^^^^^^^^^ 80 | .. literalinclude:: ../../ait/core/bin/ait_cmd_send.py 81 | :start-after: ''' 82 | :end-before: ''' 83 | 84 | ____ 85 | 86 | BSC Utilities 87 | _____________ 88 | 89 | ait-bsc-create-handler 90 | ^^^^^^^^^^^^^^^^^^^^^^^^ 91 | .. literalinclude:: ../../ait/core/bin/ait_bsc_create_handler.py 92 | :start-after: ''' 93 | :end-before: ''' 94 | 95 | ait-bsc-stop-handler 96 | ^^^^^^^^^^^^^^^^^^^^^^ 97 | .. literalinclude:: ../../ait/core/bin/ait_bsc_stop_handler.py 98 | :start-after: ''' 99 | :end-before: ''' 100 | 101 | ____ 102 | 103 | Command Table Utilities 104 | _______________________ 105 | 106 | ait-table-decode 107 | ^^^^^^^^^^^^^^^^^^ 108 | .. literalinclude:: ../../ait/core/bin/ait_table_decode.py 109 | :start-after: ''' 110 | :end-before: ''' 111 | 112 | ait-table-encode 113 | ^^^^^^^^^^^^^^^^^^ 114 | .. literalinclude:: ../../ait/core/bin/ait_table_encode.py 115 | :start-after: ''' 116 | :end-before: ''' 117 | 118 | ____ 119 | 120 | Miscellaneous Utilities 121 | _______________________ 122 | 123 | ait-create-dirs 124 | ^^^^^^^^^^^^^^^^^ 125 | .. literalinclude:: ../../ait/core/bin/ait_create_dirs.py 126 | :start-after: ''' 127 | :end-before: ''' 128 | 129 | ait-limits-find-dn 130 | ^^^^^^^^^^^^^^^^^^ 131 | .. literalinclude:: ../../ait/core/bin/ait_limits_find_dn.py 132 | :start-after: ''' 133 | :end-before: ''' 134 | 135 | ait-yaml-validate 136 | ^^^^^^^^^^^^^^^^^^^ 137 | .. literalinclude:: ../../ait/core/bin/ait_yaml_validate.py 138 | :start-after: ''' 139 | :end-before: ''' 140 | -------------------------------------------------------------------------------- /doc/source/data_types.rst: -------------------------------------------------------------------------------- 1 | AIT Data Types 2 | ============== 3 | 4 | AIT provides a number of built in data types and functionality for extending the toolkit with your own custom types. The default data types provided aim to cover the majority of the basic types you would expect to find in a packet of data. These so-called **Primitive Types** are all built on top of `ait.core.dtype.PrimitiveType`. More complicated type fields can be built on top of `PrimitiveType` by inheriting from it and implementing custom encode and decode functionality. Types such as `ait.core.dtype.CMD16` do exactly this. 5 | 6 | Built-in Types 7 | -------------- 8 | 9 | AIT provides a variety of primitive types that cover the many of fields encountered in data packets. These are defined in **ait.core.dtype.PrimitiveTypes**. 10 | 11 | .. code-block:: python 12 | 13 | [ 14 | 'I8', 15 | 'LSB_D64', 16 | 'LSB_F32', 17 | 'LSB_I16', 18 | 'LSB_I32', 19 | 'LSB_I64', 20 | 'LSB_U16', 21 | 'LSB_U32', 22 | 'LSB_U64', 23 | 'MSB_D64', 24 | 'MSB_F32', 25 | 'MSB_I16', 26 | 'MSB_I32', 27 | 'MSB_I64', 28 | 'MSB_U16', 29 | 'MSB_U32', 30 | 'MSB_U64', 31 | 'U8' 32 | ] 33 | 34 | AIT also provides some **Complex Types** that are built on top of various primitive types. The default types provided tie in with AIT's Command and EVR types as well as some CCSDS time formats. 35 | 36 | .. code-block:: python 37 | 38 | [ 39 | 'CMD16', 40 | 'EVR16', 41 | 'TIME8', 42 | 'TIME32', 43 | 'TIME40', 44 | 'TIME64' 45 | ] 46 | 47 | Custom Types 48 | ------------ 49 | 50 | If the default AIT types aren't meeting your needs you can define custom types and integrate them with the toolkit via the :doc:`Extensions ` mechanism. 51 | 52 | Consider the following example which creates a custom 24-bit MSB type. 53 | 54 | .. code-block:: python 55 | 56 | from typing import Optional 57 | 58 | from ait.core import dtype 59 | 60 | class MyCustomTypes(dtype.CustomTypes): 61 | def get(self, typename: str) -> Optional[dtype.PrimitiveType]: 62 | if typename == 'My24BitMSB': 63 | return My24BitMSB() 64 | 65 | return None 66 | 67 | 68 | class My24BitMSB(dtype.PrimitiveType): 69 | def __init__(self): 70 | # Current implementation requires that you pass a valid type here 71 | # even if it's not accurate. Manually overwrite stuff after the call 72 | # so it's correct. I.e., this isn't actually an MSB_U32! 73 | super(My24BitMSB, self).__init__('MSB_U32') 74 | self._nbits = 24 75 | self._nbytes = 3 76 | 77 | def encode(self, value: int): 78 | return (value & 0xFFFFFF).to_bytes(3, byteorder='big') 79 | 80 | def decode(self, bytes, raw=False): 81 | return int.from_bytes(bytes[:3], byteorder='big') 82 | 83 | To add a custom type you must extend **dtype.CustomTypes** with your own implementation and return an instance of your custom types. The above would be added as extension by placing the following in your **config.yaml** file. As always with extensions, the module containing the relevant class must be findable by Python. 84 | 85 | .. code-block:: yaml 86 | 87 | default: 88 | extensions: 89 | ait.core.dtype.CustomTypes: yourmodule.MyCustomTypes 90 | -------------------------------------------------------------------------------- /doc/source/databases.rst: -------------------------------------------------------------------------------- 1 | AIT Database API 2 | ================ 3 | 4 | Database Backends 5 | ----------------- 6 | 7 | AIT provides a general database abstraction class on top of which custom implementations can be written. AIT comes packaged with abstractions for InfluxDB (:class:`ait.core.db.InfluxDBBackend`)and SQLite (:class:`ait.core.db.SQLiteBackend`). You can inherit from the abstract base class :class:`ait.core.db.GenericBackend` and implement your own database abstraction. 8 | 9 | .. warning:: 10 | 11 | Note, the database-specific implementations of :class:`ait.core.db.GenericBackend` will often require 12 | a custom field to be inserted to track time associated with a given packet or field value. In general, 13 | AIT-provided implementations use **time** for the name of this field. User defined Packet Fields should 14 | avoid using this name. If an implementation uses a different value it will be noted for that specific 15 | backend. 16 | 17 | .. autoclass:: ait.core.db.GenericBackend 18 | :members: 19 | :undoc-members: 20 | :show-inheritance: 21 | 22 | 23 | Data Archive Plugin 24 | ------------------- 25 | 26 | The Data Archive Plugin, which is provided as part of AIT-Core at :class:`ait.core.server.plugins.DataArchive`, uses the database backend APIs for archiving incoming data. It uses the :class:`ait.core.db.InfluxDBBackend` by default, but a custom database can be implemented and used instead by specifying the ``datastore`` parameter in the plugin's configuration. 27 | 28 | .. code:: 29 | 30 | plugins: 31 | - plugin: 32 | name: ait.core.server.plugins.DataArchive 33 | inputs: 34 | - log_stream 35 | datastore: 36 | ait.core.db.InfluxDBBackend 37 | 38 | .. autoclass:: ait.core.server.plugins.DataArchive 39 | :members: 40 | :undoc-members: 41 | :show-inheritance: 42 | -------------------------------------------------------------------------------- /doc/source/evr_intro.rst: -------------------------------------------------------------------------------- 1 | EVRs Introduction 2 | ================= 3 | 4 | AIT provides support for YAML-based configuration of Event Verification Records (EVRs) within the system. Below is an example of a simple set of EVRs defined for use in the toolkit. 5 | 6 | .. code-block:: yaml 7 | 8 | - !EVR 9 | name: NO_ERROR 10 | code: 0x0001 11 | desc: No error 12 | message: "No error" 13 | 14 | - !EVR 15 | name: EVR_1 16 | code: 0x0002 17 | desc: EVR 1 18 | message: "The first evr" 19 | 20 | - !EVR 21 | name: EVR_2 22 | code: 0x0003 23 | desc: EVR 2 24 | message: "The second evr" 25 | 26 | - !EVR 27 | name: EVR_3 28 | code: 0x0004 29 | desc: EVR 3 30 | message: "The third evr %s" 31 | 32 | Message Formatting 33 | ------------------ 34 | 35 | AIT EVRs allow you to include common format strings in the **message** attribute so that EVR data can be decoded and included in displays. You can use the :meth:`ait.core.evr.EVRDefn.format_message` method for this. 36 | 37 | >>> import ait.core.evr 38 | >>> evr = ait.core.evr.getDefaultDict()[3] 39 | >>> evr.message 40 | 'The third evr %s' 41 | 42 | We'll need a :func:`bytearray` of data to decode: 43 | 44 | >>> data = bytearray([0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x72, 0x65, 0x61, 0x74, 0x65, 0x73, 0x74, 0x21, 0x00]) 45 | 46 | We can now decode that data and include it in our message: 47 | 48 | >>> evr.format_message(data) 49 | 'The third evr is the greatest!' 50 | 51 | !EVR 52 | ---- 53 | 54 | name: 55 | The EVR's name 56 | 57 | code: 58 | The code that specifies this EVR 59 | 60 | desc (optional): 61 | A human readable description of what the EVR represents 62 | 63 | message (optional): 64 | A human readable description of what the EVR represents. The message attribute can contain **printf** strings. The :class:`ait.core.evr.EVRDefn` class provides an interface for unpacking data into it's message attribute. 65 | -------------------------------------------------------------------------------- /doc/source/extensions.rst: -------------------------------------------------------------------------------- 1 | Core Library Extensions 2 | ======================= 3 | 4 | AIT allows users to easily overwrite core library classes used in telemetry handling, commanding, EVR processing, and limit checking to meet project specific use cases. Below we'll look at how we could create an extension to modify the default command encoding behavior in the toolkit. 5 | 6 | Preface 7 | ------- 8 | 9 | AIT needs to be able to find your extension and import it in order for everything to work as expected. For this example we will be including the new module along with the core AIT code for convenience. Generally users will use the **PYTHONPATH** environment variable to locate their new classes since AIT is normally installed as a standalone package without checking out the code base. We'll leave it up to the reader to determine what works better for their use cases but we tend to recommend not working out of a code checkout only for extensions. 10 | 11 | Creating our Extension 12 | ---------------------- 13 | 14 | Let's create a new class that overrides the default command encoding behavior. For this example we're creating a new module called **newCmd** alongside the core AIT modules. 15 | 16 | .. code:: 17 | 18 | from ait.core import cmd, log 19 | 20 | class NewCmd(cmd.Cmd): 21 | def encode(self): 22 | log.info('We are encoding this command from our custom extension') 23 | return super(NewCmd, self).encode() 24 | 25 | Notice that our **NewCmd** class inherits from the `core Cmd class `_. All we're changing in this example is the addition of the log message. Next we need to tell AIT to use our custom class instead of the built-in. In our **config.yaml** file we'll add a new section for extensions to do just that. 26 | 27 | .. code:: 28 | 29 | extensions: 30 | ait.core.cmd.Cmd: ait.core.newCmd.NewCmd 31 | 32 | .. note:: 33 | 34 | As mentioned previously, we're keeping the **newCmd** module alongside the core AIT library files for convenience. As such, our custom module path (**ait.core.newCmd.NewCmd**) references AIT core pathing. If we had our **PYTHONPATH** environment variable pointing to a folder containing our custom extensions instead our module path would look different (**newCmd.NewCmd**). 35 | 36 | Testing our Extension 37 | --------------------- 38 | 39 | Let's see if our new extension is working as we expect. 40 | 41 | >>> import ait.core.cmd as cmd 42 | | INFO | Replacing ait.core.cmd.Cmd with custom extension: ait.core.newCmd.NewCmd 43 | >>> cmdDict = cmd.getDefaultDict() 44 | >>> no_op = cmdDict.create('NO_OP') 45 | >>> no_op.encode() 46 | | INFO | We are encoding this command from our custom extension 47 | ... 48 | 49 | Looks like it's working as we expect! 50 | 51 | What else can we extend? 52 | ------------------------ 53 | 54 | AIT lets you add extensions for any class found in the `core.cmd `_, `core.tlm `_, `core.evr `_, `core.limits `_, and `core.seq `_ modules. 55 | -------------------------------------------------------------------------------- /doc/source/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to the AMMOS Instrument Toolkit (AIT) documentation! 2 | ============================================================ 3 | 4 | The AMMOS Instrument Toolkit (Formerly the Bespoke Links to Instruments for Surface and Space (BLISS)) is a Python-based software suite developed to handle Ground Data System (GDS), Electronic Ground Support Equipment (EGSE), commanding, telemetry uplink/downlink, and sequencing for JPL International Space Station and CubeSat Missions. It is a generalization and expansion of tools developed for a number of JPL ISS missions. 5 | 6 | Visit the :doc:`Installation and Environment Configuration ` guide for installation information. Visit the :doc:`API Documentation ` page to view in-depth interface documentation. 7 | 8 | .. note:: The AMMOS Instrument Toolkit was formerly known as "BLISS". You will see references to this, especially in the :doc:`API Documentation ` and code snippets, since part of the internal software structure still maintains that provenance. 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | 13 | installation 14 | project_setup 15 | Core Library APIs 16 | command_line 17 | configuration_intro 18 | server_architecture 19 | Command Dictionary Introduction 20 | Telemetry Dictionary Introduction 21 | Data Types 22 | Ground Script API Introduction 23 | EVR Introduction 24 | limits_intro 25 | Database API 26 | Extensions 27 | Serialization 28 | Onboard Tables 29 | bsc_intro 30 | plugin_openmct 31 | 32 | Indices and tables 33 | ================== 34 | 35 | * :ref:`genindex` 36 | * :ref:`modindex` 37 | * :ref:`search` 38 | -------------------------------------------------------------------------------- /doc/source/limits_intro.rst: -------------------------------------------------------------------------------- 1 | Limits Introduction 2 | =================== 3 | 4 | The :class:`ait.core.limits` module provides support for specifying acceptable value ranges for telemetry fields. 5 | 6 | Consider the below example telemetry packet and fields for which we'll specify limit values. 7 | 8 | .. code-block:: YAML 9 | 10 | - !Packet 11 | name: 1553_HS_Packet 12 | desc: Ethernet 1553 packet used to monitor telemetry in real-time 13 | functions: CurrA_Fx(dn): (dn - 2) / 1234.0 14 | 15 | fields: 16 | - !Field 17 | name: Voltage_A 18 | desc: Voltage A as a 14-bit DN. Conversion to engineering units is TBD. 19 | units: Volts 20 | type: MSB_U16 21 | 22 | - !Field 23 | name: product_type 24 | type: U8 25 | enum: 26 | 0: TABLE_FOO 27 | 1: TABLE_BAR 28 | 2: MEM_DUMP 29 | 3: HEALTH_AND_STATUS 30 | 31 | .. note:: 32 | 33 | Limits are expected to be specified in Engineering Units when a given telemetry field has a corresponding DN-to-EU conversion. A field's value is converted via it's DN-to-EU function if present prior to limit checks. 34 | 35 | Specifying Limits 36 | ----------------- 37 | 38 | Limit values can be specified for fields with a value range and for fields with enumerated values. By default, limits are specified in **limits.yaml**. You can see the path specified in **config.yaml** under the **limits.filename** parameter. 39 | 40 | .. code-block:: YAML 41 | 42 | limits: 43 | filename: limits.yaml 44 | 45 | Value-Range Limits 46 | ^^^^^^^^^^^^^^^^^^ 47 | 48 | For the **1553_HS_PACKET.Voltage_A** field we'll specify min/max value ranges for our limits. You'll see in the example below that we're specifying upper and lower bounds with error and warning values for each. You can customize the limits as necessary by specify a subset of these values. For instance, you could specify just a lower warning bound if that is all you were concerned about. 49 | 50 | .. code-block:: YAML 51 | 52 | - !Limit 53 | source: 1553_HS_Packet.Voltage_A 54 | desc: Voltage A 55 | units: Volts 56 | lower: 57 | error: 5.0 58 | warn: 10.0 59 | upper: 60 | error: 45.0 61 | warn: 40.0 62 | 63 | Enum Limits 64 | ^^^^^^^^^^^ 65 | 66 | For fields with enumerated values, such as the **1553_HS_PACKET.product_type** field, we specify warning and error limits for one or more of the field's enumerated values. Here we're specifying an error limit when the field has the **MEM_DUMP** value and a warning limit when the field value is either **TABLE_FOO** or **TABLE_BAR**. 67 | 68 | .. code-block:: YAML 69 | 70 | - !Limit 71 | source: 1553_HS_PACKET.product_type 72 | desc: Ethernet Product Type field 73 | value: 74 | error: MEM_DUMP 75 | warn: 76 | - TABLE_FOO 77 | - TABLE_BAR 78 | -------------------------------------------------------------------------------- /doc/source/plugin_PacketAccumulator.rst: -------------------------------------------------------------------------------- 1 | Packet Accumulator Plugin 2 | ======================== 3 | The Packet Accumulator plugin accumulates pipeline data. 4 | The accumulated packets are not published to the next pipeline stage until one of two conditions are met: 5 | 6 | #. The periodic timer defined by *timer_seconds* has triggered. 7 | #. The accumulated packets are large enough that the next packet will not fit without exceeding *max_size_octets* 8 | 9 | Configuration 10 | ^^^^^^^^^^^^^ 11 | Add the following template to the plugins block of your config.yaml and customize as necessary. 12 | 13 | .. code-block:: none 14 | 15 | - plugin: 16 | name: ait.core.server.plugins.PacketAccumulator.PacketAccumulator 17 | inputs: 18 | - command_stream 19 | timer_seconds: 1 20 | max_size_octets: 1024 21 | 22 | Limitations 23 | ^^^^^^^^^^^ 24 | This plugin can not split a packet set across two accumulations. 25 | Any packet whose addition would cause the currently accumulation size to exceed *max_size_octets* will be put in a new accumulation. 26 | -------------------------------------------------------------------------------- /doc/source/plugin_PacketPadder.rst: -------------------------------------------------------------------------------- 1 | Packet Padder Plugin 2 | ======================== 3 | The packet padder plugin pads command processing pipeline data to a user configured size in octets. 4 | 5 | If the size of pipeline data in octets is less than the user specified value, the pipline will pad 0s to achieve the specified size. 6 | 7 | Configuration 8 | ------------- 9 | 10 | Add and customize the following template in your config.yaml 11 | 12 | .. code-block:: none 13 | 14 | - plugin: 15 | name: ait.core.server.plugins.PacketPadder.PacketPadder 16 | inputs: 17 | - PacketAccumulator 18 | pad_octets: 987 19 | 20 | The *pad_octets* option specifies the minimum size to pad the data to. The plugin will not modify the data if its size already meets or exceeds this value. A negative, 0, or missing value will bypass the plugin. 21 | 22 | Use *inputs* to specify which PUB/SUB topic the plugin should receive pipeline data from. 23 | -------------------------------------------------------------------------------- /doc/source/project_setup.rst: -------------------------------------------------------------------------------- 1 | Setting up a New Project with AIT 2 | ================================= 3 | 4 | The following documentation will teach you how to setup a new project to build off of the AMMOS Instrument Toolkit. This guide assumes that the project you'll be developing is a Python-based project. 5 | 6 | .. note:: AIT is tested against *Python 3.7*. Running AIT with other versions of *Python 3* may have issues. 7 | 8 | Add AIT Core as a Dependency 9 | ------------------------------ 10 | 11 | You'll need to add AIT Core to either your **requirements.txt** file or your **setup.py** file. 12 | 13 | If you use a requirements file for specifying dependencies: 14 | 15 | .. code-block:: bash 16 | 17 | ait-core==1.0.0 18 | 19 | If you use **setup.py** for specifying dependencies: 20 | 21 | .. code-block:: bash 22 | 23 | install_requires = [ 24 | ait-core==1.0.0 25 | ], 26 | 27 | Set AIT Config Values 28 | --------------------- 29 | 30 | AIT provides a large number of configuration parameters for customizing and configuring the toolkit. AIT ships with an example **config.yaml** skeleton located at **/PROJECT_ROOT/config/config.yaml** that you can use as a baseline configuration file. You should read the :doc:`Configuration Introduction ` and the component specific configuration documents such as the :doc:`Telemetry `, :doc:`Commanding `, and :doc:`EVR ` pages for additional information and update the files to meet your project's specifications. 31 | -------------------------------------------------------------------------------- /openmct/README: -------------------------------------------------------------------------------- 1 | This directory contains files needed to expose AIT realtime and historical 2 | telemetry endpoints to the OpenMCT framework. 3 | 4 | You can learn more about OpenMCT by visiting: https://nasa.github.io/openmct/ 5 | 6 | ------------------------ 7 | 8 | Notes: 9 | 10 | - It is assumed that the AitOpenMctPlugin has been activated in the AIT 11 | server configuration file, as this provides the sources for telemetry. 12 | See the local file 'ait_cfg_section.yaml' for the expected section to 13 | be added to your $AIT_CONFIG file. 14 | 15 | - For this guide, we also assume that the AIT server is running on 16 | host 'localhost' and port 8082, which are two configuration options 17 | that can be passed to the OpenMct AIT extension during 18 | session setup. 19 | 20 | - Earlier versions of the AIT-OpenMCT integration required explicit 21 | installation of OpenMCT, and adding AIT extensions to that deployment. 22 | This has since been simplified where OpenMCT is now treated as a dependency. 23 | 24 | - The AIT extension requires 'http.js', a library that was 25 | included in the OpenMCT Tutorial (Apache License, Version 2.0). 26 | The source location of this file is: 27 | https://github.com/nasa/openmct-tutorial/tree/completed/lib/http.js 28 | It is currently included with our example OpenMCT server. 29 | 30 | ------------------------ 31 | 32 | Setup: 33 | 34 | While treating OpenMCT as a dependency, a Node web-server capable of running 35 | the OpenMCT service is still needed. AIT provides a basic example 36 | server which fulfills this need (based on OpenMCT's tutorial). 37 | See AIT-Core/openmct/example-server/. 38 | 39 | The example server includes: 40 | - package.json; with all dependencies (including OpenMCT) and service launcher. 41 | - server.js; entry point for the web-server that will host OpenMCT service. 42 | - index.html; sets up OpenMCT and AIT extensions. 43 | - lib/https.js; a modified library required by the integration. 44 | - ait_integration.js; symlink to AIT-OpenMct service integration. 45 | 46 | Setup via the example-server: 47 | 48 | 1) Copy 'example-server' to a directory referenced by the environment variable ${OPENMCT_DIR} 49 | 50 | > cp -RL ./example-server ${OPENMCT_DIR} #Recursive copy, resolve symlinks to real files 51 | 52 | 53 | 2) Install service dependencies (including OpenMCT) via NPM and package.json: 54 | 55 | > cd ${OPENMCT_DIR} 56 | > npm install 57 | 58 | 59 | The index.html includes the import of the required Javascript files: 60 | 61 | 62 | 63 | 64 | ...as well as the OpenMCT installation of the integration and data endpoints: 65 | 66 | openmct.install(AITIntegration({ 67 | host: 'localhost', 68 | port : 8082 })); 69 | openmct.install(AITHistoricalTelemetryPlugin()); 70 | openmct.install(AITRealtimeTelemetryPlugin()); 71 | 72 | 73 | The web-server can be launched via Node-NPM: 74 | 75 | > npm start 76 | 77 | -------------------------- 78 | 79 | Running AIT and OpenMCT: 80 | 81 | 1) Start the AIT server (configured to run AIT's OpenMct plugin) 82 | 2) Start OpenMCT server 83 | 3) Open browser to location of the OpenMCT UI endpoint. 84 | -------------------------------------------------------------------------------- /openmct/ait_cfg_section.yaml: -------------------------------------------------------------------------------- 1 | # Copy-paste the non-commented out section of YAML to install 2 | # the AIT OpenMCT plugin for your server 3 | # Port default is 8082, ensure this matches the AIT integration config 4 | # in OpenMCT's index.html. 5 | 6 | //default: 7 | // server: 8 | plugins: 9 | - plugin: 10 | name: ait.core.server.plugins.openmct.AITOpenMctPlugin 11 | inputs: 12 | - telem_stream 13 | service_port: 8082 14 | debug_enabled: False 15 | database_enabled: True 16 | datastore: ait.core.db.InfluxDBBackend 17 | -------------------------------------------------------------------------------- /openmct/example-server/ait_integration.js: -------------------------------------------------------------------------------- 1 | ../ait_integration.js -------------------------------------------------------------------------------- /openmct/example-server/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Open MCT Tutorials 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /openmct/example-server/lib/http.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(factory); 4 | } else if (typeof exports === 'object') { 5 | module.exports = factory; 6 | } else { 7 | root.http = factory(root); 8 | } 9 | })(this, function (root) { 10 | 11 | 'use strict'; 12 | 13 | var exports = {}; 14 | 15 | var generateResponse = function (req) { 16 | var response = { 17 | data: req.responseText, 18 | status: req.status, 19 | request: req 20 | }; 21 | if (req.getResponseHeader('Content-Type').indexOf('application/json') !== -1) { 22 | response.data = JSON.parse(response.data); 23 | } 24 | return response; 25 | }; 26 | 27 | var xhr = function (type, url, data) { 28 | var promise = new Promise(function (resolve, reject) { 29 | var XHR = XMLHttpRequest || ActiveXObject; 30 | var request = new XHR('MSXML2.XMLHTTP.3.0'); 31 | 32 | request.open(type, url, true); 33 | request.onreadystatechange = function () { 34 | var req; 35 | if (request.readyState === 4) { 36 | req = generateResponse(request); 37 | if (request.status >= 200 && request.status < 300) { 38 | resolve(req); 39 | } else { 40 | reject(req); 41 | } 42 | } 43 | }; 44 | request.send(data); 45 | }); 46 | return promise; 47 | }; 48 | 49 | exports.get = function (src) { 50 | return xhr('GET', src); 51 | }; 52 | 53 | exports.put = function (url, data) { 54 | return xhr('PUT', url, data); 55 | }; 56 | 57 | exports.post= function (url, data) { 58 | return xhr('POST', url, data); 59 | }; 60 | 61 | exports.delete = function (url) { 62 | return xhr('DELETE', url); 63 | }; 64 | 65 | return exports; 66 | }); 67 | -------------------------------------------------------------------------------- /openmct/example-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ait-openmct-basic-server", 3 | "version": "0.0.1", 4 | "description": "AIT Web-Server for Open MCT", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/nasa/openmct-tutorial.git" 12 | }, 13 | "author": "", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/NASA-AMMOS/AIT-Core/issues" 17 | }, 18 | "homepage": "https://github.com/NASA-AMMOS/AIT-Core/issues", 19 | "dependencies": { 20 | "express": "^4.16.4", 21 | "express-ws": "^4.0.0", 22 | "openmct": "latest", 23 | "ws": "^6.1.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /openmct/example-server/server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | 3 | function StaticServer() { 4 | var router = express.Router(); 5 | 6 | router.use('/', express.static(__dirname + '/.')); 7 | 8 | return router 9 | } 10 | 11 | var expressWs = require('express-ws'); 12 | var app = express(); 13 | 14 | expressWs(app); 15 | 16 | var staticServer = new StaticServer(); 17 | app.use('/', staticServer); 18 | 19 | var port = process.env.PORT || 8080 20 | 21 | app.listen(port, function () { 22 | console.log('Open MCT hosted at http://localhost:' + port); 23 | }); 24 | -------------------------------------------------------------------------------- /poetry_cli/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NASA-AMMOS/AIT-Core/35679f7fd7191be998f95aa797f591b89ec3976e/poetry_cli/__init__.py -------------------------------------------------------------------------------- /poetry_cli/build_sphinx.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from sphinx.cmd.build import main as build_main 3 | 4 | 5 | def main(): 6 | sys.exit(build_main([ 'doc/source', 'doc/build', '-a' ])) 7 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["poetry-core>=1.0.0"] 3 | build-backend = "poetry.core.masonry.api" 4 | 5 | [tool.poetry] 6 | name = 'ait-core' 7 | version = '3.0.0' 8 | description = "NASA JPL's Ground Data System toolkit for Instrument and CubeSat Missions" 9 | license = 'MIT' 10 | readme = 'README.rst' 11 | homepage = 'https://github.com/NASA-AMMOS/AIT-Core' 12 | repository = 'https://github.com/NASA-AMMOS/AIT-Core' 13 | documentation = 'https://ait-core.readthedocs.io/en/latest' 14 | authors = ['AMMOS Instrument Toolkit Development Team '] 15 | 16 | packages = [ 17 | {include = "ait"} 18 | ] 19 | 20 | include = [ 21 | "ait/core/data/*.json" 22 | ] 23 | 24 | [tool.poetry.dependencies] 25 | python = '>= 3.7 < 3.11' 26 | bottle = '0.12.23' 27 | jsonschema = '3.0.2' 28 | pyyaml = '6.0.1' 29 | requests = '>= 2.22.0' 30 | greenlet = '1.1.3' 31 | gevent = '*' 32 | gevent-websocket = '0.10.1' 33 | pyzmq = '24.0.0' 34 | gipc = "^1.1.0" 35 | setproctitle = "^1.2.3" 36 | msgpack = '*' 37 | 38 | [tool.poetry.dev-dependencies] 39 | black = '*' 40 | flake8 = '*' 41 | pyproject-flake8 = '^0.0.1-alpha.2' 42 | flake8-bugbear = '*' 43 | pep8-naming = '*' 44 | mypy = '*' 45 | types-PyYAML = '*' 46 | types-requests = '*' 47 | types-setuptools = '*' 48 | pydocstyle = '*' 49 | coverage = '*' 50 | pytest = '*' 51 | pytest-cov = '*' 52 | pytest-watch = '*' 53 | pytest-xdist = '*' 54 | pre-commit = '*' 55 | sphinx = '>= 4.2' 56 | sphinx-rtd-theme = '*' 57 | sphinxcontrib-httpdomain = '*' 58 | tox = '>= 3.8 < 4.0' 59 | twine = '^3.4.2' 60 | 61 | [tool.poetry.scripts] 62 | ait-bsc = 'ait.core.bin.ait_bsc:main' 63 | ait-bsc-create-handler = 'ait.core.bin.ait_bsc_create_handler:main' 64 | ait-bsc-stop-handler = 'ait.core.bin.ait_bsc_stop_handler:main' 65 | ait-ccsds-send-example = 'ait.core.bin.ait_ccsds_send_example:main' 66 | ait-cmd-hist = 'ait.core.bin.ait_cmd_hist:main' 67 | ait-cmd-send = 'ait.core.bin.ait_cmd_send:main' 68 | ait-create-dirs = 'ait.core.bin.ait_create_dirs:main' 69 | ait-dict-writer = 'ait.core.bin.ait_dict_writer:main' 70 | ait-limits-find-dn = 'ait.core.bin.ait_limits_find_dn:main' 71 | ait-mps-seq-convert = 'ait.core.bin.ait_mps_seq_convert:main' 72 | ait-pcap = 'ait.core.bin.ait_pcap:main' 73 | ait-pcap-segment = 'ait.core.bin.ait_pcap_segment:main' 74 | ait-seq-decode = 'ait.core.bin.ait_seq_decode:main' 75 | ait-seq-encode = 'ait.core.bin.ait_seq_encode:main' 76 | ait-seq-print = 'ait.core.bin.ait_seq_print:main' 77 | ait-seq-send = 'ait.core.bin.ait_seq_send:main' 78 | ait-server = 'ait.core.bin.ait_server:main' 79 | ait-table-decode = 'ait.core.bin.ait_table_decode:main' 80 | ait-table-encode = 'ait.core.bin.ait_table_encode:main' 81 | ait-tlm-csv = 'ait.core.bin.ait_tlm_csv:main' 82 | ait-tlm-db-insert = 'ait.core.bin.ait_tlm_db_insert:main' 83 | ait-tlm-send = 'ait.core.bin.ait_tlm_send:main' 84 | ait-tlm-simulate = 'ait.core.bin.ait_tlm_simulate:main' 85 | ait-yaml-validate = 'ait.core.bin.ait_yaml_validate:main' 86 | build_sphinx = 'poetry_cli.build_sphinx:main' 87 | -------------------------------------------------------------------------------- /readthedocs.yml: -------------------------------------------------------------------------------- 1 | formats: 2 | - none 3 | 4 | python: 5 | version: "3.8" 6 | pip_install: true 7 | extra_requirements: 8 | - docs 9 | - tests 10 | install: 11 | - requirements: doc/requirements.txt 12 | -------------------------------------------------------------------------------- /scripts/example_script.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Check out the AIT API Documentation for a more detailed look at the scripting API. 5 | 6 | https://ait-core.readthedocs.io/en/latest/api_intro.html 7 | 8 | """ 9 | 10 | from ait.core.api import Instrument 11 | from ait.core.table import FSWTabDictCache 12 | 13 | 14 | inst = Instrument() 15 | 16 | # Send a command 17 | inst.cmd.send('NO_OP') 18 | 19 | cache = FSWTabDictCache() 20 | tab_dict = cache.load() 21 | -------------------------------------------------------------------------------- /sequences/example_sequence.txt: -------------------------------------------------------------------------------- 1 | # 2 | # AIT sequences are relative time sequences where each row 3 | # consists of a time offset in seconds and the command to 4 | # execute. 5 | # 6 | # For additional information check out the project documentation 7 | # 8 | 10.5 NO_OP 9 | 10 NO_OP 10 | 5 SEQ_START 2 11 | 5 SEQ_ENABLE_DISABLE 3 ENABLED 12 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [build_sphinx] 2 | source-dir = doc/source 3 | build-dir = doc/build 4 | all_files = 1 5 | 6 | 7 | [tool:pytest] 8 | junit_family=xunit1 9 | addopts = --cov=ait 10 | filterwarnings = 11 | ignore::DeprecationWarning:ait.core.*: 12 | 13 | [coverage:run] 14 | omit = */_version.py,*/__init__.py,*/bin/*,*/test/* 15 | 16 | [flake8] 17 | extend-exclude = versioneer.py,_version.py,docs,doc,tests,test,setup.py 18 | max-line-length = 120 19 | 20 | # Line breaks before/after a binary operator, allow them since it 21 | # is not clear how to resolve while making code readable 22 | ignore = W503,W504 23 | 24 | # Ignoring: 25 | # E203 prevents flake8 from complaining about whitespace around slice 26 | # components. Black formats per PEP8 and flake8 doesn't like some of 27 | # this. 28 | # 29 | # E501 prevents flake8 from complaining line lengths > 79. We will use 30 | # flake8-bugbear's B950 to handle line length lint errors. This trips 31 | # when a line is > max-line-length + 10%. 32 | # 33 | # E402 prevents flake8 complaining about module level imports not appearing 34 | # at the top of a file. We need to run gevent monkeypatching which triggers 35 | # this on every import where that's the case. 36 | extend-ignore = E203, E501, E402 37 | 38 | # Selects following test categories: 39 | # D: Docstring errors and warnings 40 | # E, W: PEP8 errors and warnings 41 | # F: PyFlakes codes 42 | # N: PEP8 Naming plugin codes 43 | # B: flake8-bugbear codes 44 | # B***: Specific flake8-bugbear opinionated warnings to trigger 45 | # B902: Invalid first argument used for method. Use self for instance 46 | # methods, and cls for class methods 47 | # B903: Use collections.namedtuple (or typing.NamedTuple) for data classes 48 | # that only set attributes in an __init__ method, and do nothing else. 49 | # B950: Line too long. This is a pragmatic equivalent of pycodestyle's 50 | # E501: it considers "max-line-length" but only triggers when the value 51 | # has been exceeded by more than 10%. 52 | select = E,F,N,W,B,B902,B903,B950 53 | 54 | 55 | [mypy] 56 | 57 | # 58 | # TODO: Evaluate whether updating to newer dependency versions fixes our ability 59 | # to locate type stubs for these ignored libraries. 60 | # 61 | [mypy-gevent.*] 62 | ignore_missing_imports = True 63 | 64 | [mypy-zmq.*] 65 | ignore_missing_imports = True 66 | 67 | [mypy-bottle.*] 68 | ignore_missing_imports = True 69 | 70 | [mypy-jsonschema.*] 71 | ignore_missing_imports = True 72 | 73 | [mypy-geventwebsocket.*] 74 | ignore_missing_imports = True 75 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NASA-AMMOS/AIT-Core/35679f7fd7191be998f95aa797f591b89ec3976e/tests/__init__.py -------------------------------------------------------------------------------- /tests/ait/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NASA-AMMOS/AIT-Core/35679f7fd7191be998f95aa797f591b89ec3976e/tests/ait/__init__.py -------------------------------------------------------------------------------- /tests/ait/core/__init__.py: -------------------------------------------------------------------------------- 1 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 2 | # Bespoke Link to Instruments and Small Satellites (BLISS) 3 | # 4 | # Copyright 2022, by the California Institute of Technology. ALL RIGHTS 5 | # RESERVED. United States Government Sponsorship acknowledged. Any 6 | # commercial use must be negotiated with the Office of Technology Transfer 7 | # at the California Institute of Technology. 8 | # 9 | # This software may be subject to U.S. export control laws. By accepting 10 | # this software, the user agrees to comply with all applicable U.S. export 11 | # laws and regulations. User has the responsibility to obtain export licenses, 12 | # or other export authority as may be required before exporting such 13 | # information to foreign countries or providing access to foreign persons. 14 | """AIT Unit and Functional Tests""" 15 | import logging 16 | 17 | 18 | def setUp(): 19 | """ 20 | Set up tests: 21 | Turn logging level to CRITICAL: due to failure test cases, there 22 | are many verbose log messages that are useful in context. 23 | """ 24 | logging.getLogger("ait").setLevel(logging.CRITICAL) 25 | 26 | 27 | def tearDown(): 28 | """ 29 | Tear down tests: 30 | Turn logging level back to INFO. 31 | """ 32 | logging.getLogger("ait").setLevel(logging.INFO) 33 | -------------------------------------------------------------------------------- /tests/ait/core/server/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NASA-AMMOS/AIT-Core/35679f7fd7191be998f95aa797f591b89ec3976e/tests/ait/core/server/__init__.py -------------------------------------------------------------------------------- /tests/ait/core/server/test_apid_routing.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from unittest.mock import patch 3 | 4 | from ait.core import log 5 | from ait.core.server.plugins.apid_routing import APIDRouter 6 | from ait.core.util import TestFile 7 | 8 | 9 | class TestPacket: 10 | def __init__(self, apid=0): 11 | self.apid = apid 12 | 13 | 14 | def create_test_dict(number_of_packets=150): 15 | test_dict = {} 16 | for i in range(1, number_of_packets + 1): 17 | packet_name = f"Packet_{i}" 18 | test_dict[packet_name] = TestPacket(apid=i) 19 | return test_dict 20 | 21 | 22 | class TestPacketRouting(unittest.TestCase): 23 | def routing_table_yaml(): 24 | """ 25 | # Call this function to return the yaml string below 26 | 27 | output_topics: 28 | - telem_topic_1: 29 | - 1 30 | - 7 31 | - telem_topic_2: 32 | - 2 33 | - range: 34 | - 4 35 | - 9 36 | - exclude: 37 | - 6 38 | """ 39 | 40 | pass 41 | 42 | with TestFile(routing_table_yaml.__doc__, "wt") as filename: 43 | 44 | def new_init(self, routing_table=None, default_topic=None): 45 | self.default_topic = default_topic 46 | 47 | if "path" in routing_table: 48 | self.routing_table_object = self.load_table_yaml( 49 | routing_table["path"], create_test_dict(10) 50 | ) 51 | else: 52 | self.routing_table_object = None 53 | log.error("no path specified for routing table") 54 | if self.routing_table_object is None: 55 | log.error("Unable to load routing table .yaml file") 56 | 57 | with patch.object(APIDRouter, "__init__", new_init): 58 | router_plugin_instance = APIDRouter( 59 | routing_table={"path": filename}, default_topic="test_default_topic" 60 | ) 61 | 62 | def test_routing_table(self): 63 | test_routing_table_dict = { 64 | 1: ["test_default_topic", "telem_topic_1"], 65 | 2: ["test_default_topic", "telem_topic_2"], 66 | 3: ["test_default_topic"], 67 | 4: ["test_default_topic", "telem_topic_2"], 68 | 5: ["test_default_topic", "telem_topic_2"], 69 | 6: ["test_default_topic"], 70 | 7: ["test_default_topic", "telem_topic_1", "telem_topic_2"], 71 | 8: ["test_default_topic", "telem_topic_2"], 72 | 9: ["test_default_topic", "telem_topic_2"], 73 | 10: ["test_default_topic"], 74 | } 75 | self.assertEqual( 76 | self.router_plugin_instance.routing_table_object, test_routing_table_dict 77 | ) 78 | 79 | def test_apid_extraction1(self): 80 | test_bytearray = bytearray(b"\x00\x1f\x75\x94\xfa\xdc\x43\x90\x9a\x8c\xff\xe0") 81 | self.assertEqual( 82 | self.router_plugin_instance.get_packet_apid(test_bytearray), 31 83 | ) 84 | 85 | def test_apid_extraction2(self): 86 | test_bytearray = bytearray(b"\x01\x03\x75\x94\xfa\xdc\x43\x90\x9a\x8c\xff\xe0") 87 | self.assertEqual( 88 | self.router_plugin_instance.get_packet_apid(test_bytearray), 259 89 | ) 90 | 91 | def test_get_topics(self): 92 | test_bytearray = bytearray(b"\x00\x07\x75\x94\xfa\xdc\x43\x90\x9a\x8c\xff\xe0") 93 | test_bytearray_apid = self.router_plugin_instance.get_packet_apid( 94 | test_bytearray 95 | ) 96 | expected_topics = ["test_default_topic", "telem_topic_1", "telem_topic_2"] 97 | self.assertEqual( 98 | self.router_plugin_instance.routing_table_object[test_bytearray_apid], 99 | expected_topics, 100 | ) 101 | -------------------------------------------------------------------------------- /tests/ait/core/server/test_client.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NASA-AMMOS/AIT-Core/35679f7fd7191be998f95aa797f591b89ec3976e/tests/ait/core/server/test_client.py -------------------------------------------------------------------------------- /tests/ait/core/server/test_handler.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from unittest import mock 3 | 4 | from ait.core import tlm 5 | from ait.core.server.handlers import CCSDSPacketHandler 6 | from ait.core.server.handlers import PacketHandler 7 | 8 | 9 | class TestCCSDSPacketCheck(unittest.TestCase): 10 | # Check if packet length is at least 7 bytes 11 | def test_ccsds_packet_length(self): 12 | handler = CCSDSPacketHandler(packet_types={"01011100111": "CCSDS_HEADER"}) 13 | data = bytearray(b"\x02\xE7\x40\x00\x00\x00") 14 | with self.assertLogs("ait", level="INFO") as cm: 15 | result = handler.handle(data) 16 | self.assertIn("less than minimum of 7 bytes", cm.output[0]) 17 | 18 | # Check if APID match between config and packet 19 | def test_ccsds_packet_apid(self): 20 | handler = CCSDSPacketHandler(packet_types={"00000000000": "CCSDS_HEADER"}) 21 | data = bytearray(b"\x02\xE7\x40\x00\x00\x00\x01") 22 | with self.assertLogs("ait", level="INFO") as cm: 23 | result = handler.handle(data) 24 | self.assertIn("not present in config", cm.output[0]) 25 | 26 | # Check packet length vs header reported length 27 | def test_ccsds_packet_header(self): 28 | handler = CCSDSPacketHandler(packet_types={"01011100111": "CCSDS_HEADER"}) 29 | data = bytearray(b"\x02\xE7\x40\x00\x00\x0F\x01") 30 | with self.assertLogs("ait", level="INFO") as cm: 31 | result = handler.handle(data) 32 | self.assertIn( 33 | "Packet data length is less than stated length in packet primary header", 34 | cm.output[0], 35 | ) 36 | 37 | # Check if dumped uid match expected tlm dictionary uid 38 | def test_ccsds_packet_uid(self): 39 | handler = CCSDSPacketHandler(packet_types={"01011100111": "CCSDS_HEADER"}) 40 | data = bytearray(b"\x02\xE7\x40\x00\x00\x00\x01") 41 | 42 | tlm_dict = tlm.getDefaultDict() 43 | packet_uid = tlm_dict["CCSDS_HEADER"].uid 44 | result = handler.handle(data) 45 | self.assertEqual(packet_uid, result[0]) 46 | 47 | 48 | class TestHandlerClassWithInputOutputTypes(object): 49 | handler = PacketHandler(packet="CCSDS_HEADER", input_type="int", output_type="str") 50 | 51 | def test_handler_creation(self): 52 | assert self.handler.input_type is "int" 53 | assert self.handler.output_type is "str" 54 | 55 | @mock.patch( 56 | "ait.core.server.handlers.PacketHandler.handle", return_value="SpecialReturn" 57 | ) 58 | def test_execute_handler_returns_handle_return_on_input(self, handle_mock): 59 | returned = self.handler.handle("2") 60 | assert returned == "SpecialReturn" 61 | 62 | 63 | class TestHandlerClassWithoutInputOutputTypes(object): 64 | handler = PacketHandler(packet="CCSDS_HEADER") 65 | 66 | def test_handler_default_params(self): 67 | assert self.handler.input_type is None 68 | assert self.handler.output_type is None 69 | 70 | @mock.patch( 71 | "ait.core.server.handlers.PacketHandler.handle", return_value="SpecialReturn" 72 | ) 73 | def test_execute_handler_returns_handle_return_on_input(self, handle_mock): 74 | returned = self.handler.handle("2") 75 | assert returned == "SpecialReturn" 76 | 77 | def test_handler_repr(self): 78 | assert self.handler.__repr__() == "" 79 | 80 | 81 | class TestCCSDSHandlerClassWithInputOutputTypes(object): 82 | handler = CCSDSPacketHandler( 83 | packet_types={"01011100111": "CCSDS_HEADER"}, 84 | input_type="int", 85 | output_type="str", 86 | ) 87 | 88 | def test_handler_creation(self): 89 | assert self.handler.input_type is "int" 90 | assert self.handler.output_type is "str" 91 | 92 | @mock.patch( 93 | "ait.core.server.handlers.CCSDSPacketHandler.handle", 94 | return_value="SpecialReturn", 95 | ) 96 | def test_execute_handler_returns_handle_return_on_input(self, handle_mock): 97 | data = bytearray(b"\x02\xE7\x40\x00\x00\x00\x01") 98 | returned = self.handler.handle(data) 99 | assert returned == "SpecialReturn" 100 | 101 | 102 | class TestCCSDSHandlerClassWithoutInputOutputTypes(object): 103 | handler = CCSDSPacketHandler(packet_types={"01011100111": "CCSDS_HEADER"}) 104 | 105 | def test_ccsds_handler_default_params(self): 106 | assert self.handler.input_type is None 107 | assert self.handler.output_type is None 108 | 109 | @mock.patch( 110 | "ait.core.server.handlers.CCSDSPacketHandler.handle", 111 | return_value="SpecialReturn", 112 | ) 113 | def test_execute_handler_returns_handle_return_on_input(self, handle_mock): 114 | data = bytearray(b"\x02\xE7\x40\x00\x00\x00\x01") 115 | returned = self.handler.handle(data) 116 | assert returned == "SpecialReturn" 117 | 118 | def test_handler_repr(self): 119 | assert self.handler.__repr__() == "" 120 | -------------------------------------------------------------------------------- /tests/ait/core/server/test_stream.py: -------------------------------------------------------------------------------- 1 | from unittest import mock 2 | 3 | import pytest 4 | import zmq.green 5 | 6 | import ait.core 7 | from ait.core.server.broker import Broker 8 | from ait.core.server.handlers import PacketHandler 9 | from ait.core.server.stream import ZMQStream 10 | 11 | 12 | class TestStream: 13 | def setup_method(self): 14 | self.broker = Broker() 15 | self.stream = ZMQStream( 16 | "some_stream", 17 | ["input_stream"], 18 | [PacketHandler(input_type=int, output_type=str, packet="CCSDS_HEADER")], 19 | zmq_args={"zmq_context": self.broker.context}, 20 | ) 21 | self.stream.handlers = [ 22 | PacketHandler(input_type=int, output_type=str, packet="CCSDS_HEADER") 23 | ] 24 | 25 | def test_stream_creation(self): 26 | assert self.stream.name is "some_stream" 27 | assert self.stream.inputs == ["input_stream"] 28 | assert len(self.stream.handlers) == 1 29 | assert type(self.stream.handlers[0]) == PacketHandler 30 | assert self.stream.context == self.broker.context 31 | assert type(self.stream.pub) == zmq.green.core._Socket 32 | assert type(self.stream.sub) == zmq.green.core._Socket 33 | 34 | def test_repr(self): 35 | assert self.stream.__repr__() == "" 36 | 37 | @mock.patch.object(PacketHandler, "handle") 38 | def test_process(self, execute_handler_mock): 39 | self.stream.process("input_data") 40 | execute_handler_mock.assert_called_with("input_data") 41 | 42 | def test_valid_workflow_one_handler(self): 43 | assert self.stream.valid_workflow() is True 44 | 45 | def test_valid_workflow_more_handlers(self): 46 | self.stream.handlers.append( 47 | PacketHandler(input_type=str, packet="CCSDS_HEADER") 48 | ) 49 | assert self.stream.valid_workflow() is True 50 | 51 | def test_invalid_workflow_more_handlers(self): 52 | self.stream.handlers.append( 53 | PacketHandler(input_type=int, packet="CCSDS_HEADER") 54 | ) 55 | assert self.stream.valid_workflow() is False 56 | 57 | def test_stream_creation_invalid_workflow(self): 58 | with pytest.raises(ValueError): 59 | ZMQStream( 60 | "some_stream", 61 | "input_stream", 62 | [ 63 | PacketHandler( 64 | input_type=int, output_type=str, packet="CCSDS_HEADER" 65 | ), 66 | PacketHandler(input_type=int, packet="CCSDS_HEADER"), 67 | ], 68 | zmq_args={"zmq_context": self.broker.context}, 69 | ) 70 | -------------------------------------------------------------------------------- /tests/ait/core/test_ccsds.py: -------------------------------------------------------------------------------- 1 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 2 | # Bespoke Link to Instruments and Small Satellites (BLISS) 3 | # 4 | # Copyright 2017, by the California Institute of Technology. ALL 5 | # RIGHTS RESERVED. United States Government Sponsorship 6 | # acknowledged. Any commercial use must be negotiated with the Office 7 | # of Technology Transfer at the California Institute of Technology. 8 | # 9 | # This software may be subject to U.S. export control laws. By 10 | # accepting this software, the user agrees to comply with all 11 | # applicable U.S. export laws and regulations. User has the 12 | # responsibility to obtain export licenses, or other export authority 13 | # as may be required before exporting such information to foreign 14 | # countries or providing access to foreign persons. 15 | import gevent.monkey 16 | 17 | gevent.monkey.patch_all() 18 | 19 | from ait.core import ccsds 20 | 21 | 22 | def testCcsdsDefinition(): 23 | defn = ccsds.CcsdsDefinition(apid=42, length=128) 24 | 25 | assert defn.version == 0 26 | assert defn.type == 0 27 | assert defn.secondary == None 28 | assert defn.shflag == 0 29 | assert defn.apid == 42 30 | assert defn.seqflags == 0b11 31 | assert defn.length == 128 32 | 33 | 34 | def testCcsdsHeaderDefaults(): 35 | header = ccsds.CcsdsHeader() 36 | 37 | assert header.version == 0 38 | assert header.type == 0 39 | assert header.shflag == 0 40 | assert header.apid == 0 41 | assert header.raw.seqflags == 0b11 42 | assert header.seqcount == 0 43 | assert header.length == 0 44 | 45 | 46 | def testCcsdsHeaderDecode(): 47 | header = ccsds.CcsdsHeader([0x18, 0x2A, 0xC4, 0xD2, 0x16, 0x2E]) 48 | 49 | assert header.version == 0 50 | assert header.type == 1 51 | assert header.shflag == 1 52 | assert header.apid == 42 53 | assert header.raw.seqflags == 0b11 54 | assert header.seqcount == 1234 55 | assert header.length == 5678 56 | 57 | 58 | def testCcsdsHeaderEncode(): 59 | header = ccsds.CcsdsHeader() 60 | 61 | header.version = 0 62 | header.type = 1 63 | header.shflag = 1 64 | header.apid = 42 65 | header.seqflags = 0b11 66 | header.seqcount = 1234 67 | header.length = 5678 68 | 69 | assert header._data == bytearray([0x18, 0x2A, 0xC4, 0xD2, 0x16, 0x2E]) 70 | -------------------------------------------------------------------------------- /tests/ait/core/test_coord.py: -------------------------------------------------------------------------------- 1 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 2 | # Bespoke Link to Instruments and Small Satellites (BLISS) 3 | # 4 | # Copyright 2016, by the California Institute of Technology. ALL RIGHTS 5 | # RESERVED. United States Government Sponsorship acknowledged. Any 6 | # commercial use must be negotiated with the Office of Technology Transfer 7 | # at the California Institute of Technology. 8 | # 9 | # This software may be subject to U.S. export control laws. By accepting 10 | # this software, the user agrees to comply with all applicable U.S. export 11 | # laws and regulations. User has the responsibility to obtain export licenses, 12 | # or other export authority as may be required before exporting such 13 | # information to foreign countries or providing access to foreign persons. 14 | import gevent.monkey 15 | 16 | gevent.monkey.patch_all() 17 | 18 | import datetime 19 | import math 20 | 21 | from ait.core import dmc, coord 22 | 23 | 24 | def float_equality(a, b, rel_tol=1e-06, abs_tol=0.0): 25 | return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol) 26 | 27 | 28 | def test_cbrt(): 29 | assert float_equality(coord.cbrt(64), 4) 30 | assert float_equality(coord.cbrt(-64), -4) 31 | assert float_equality(coord.cbrt(10), 2.1544346) 32 | 33 | 34 | def test_eci2ecef(): 35 | eci = -6.0744 * 1e6, -1.8289 * 1e6, 0.6685 * 1e6 36 | t = datetime.datetime(2010, 1, 17, 10, 20, 36) 37 | gmst = dmc.to_gmst(t) 38 | ecef = coord.eci2ecef(eci[0], eci[1], eci[2], gmst=gmst) 39 | assert float_equality(ecef[0], 1628340.306018) 40 | assert float_equality(ecef[1], -6131208.5609442) 41 | assert float_equality(ecef[2], 668500.0) 42 | 43 | 44 | def test_eci2geodetic(): 45 | eci = -6.0744 * 1e6, -1.8289 * 1e6, 0.6685 * 1e6 46 | t = datetime.datetime(2010, 1, 17, 10, 20, 36) 47 | gmst = dmc.to_gmst(t) 48 | lla = list(coord.eci2geodetic(eci[0], eci[1], eci[2], gmst=gmst)) 49 | lla[0] = math.fmod(lla[0], math.pi * 2) 50 | lla[1] = math.fmod(lla[1], math.pi * 2) 51 | assert float_equality(math.degrees(lla[0]), 6.0558200) 52 | assert float_equality(math.degrees(lla[1]), -75.1266047) 53 | assert float_equality(lla[2], 978.4703290) 54 | -------------------------------------------------------------------------------- /tests/ait/core/test_log.py: -------------------------------------------------------------------------------- 1 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 2 | # Bespoke Link to Instruments and Small Satellites (BLISS) 3 | # 4 | # Copyright 2014, by the California Institute of Technology. ALL RIGHTS 5 | # RESERVED. United States Government Sponsorship acknowledged. Any 6 | # commercial use must be negotiated with the Office of Technology Transfer 7 | # at the California Institute of Technology. 8 | # 9 | # This software may be subject to U.S. export control laws. By accepting 10 | # this software, the user agrees to comply with all applicable U.S. export 11 | # laws and regulations. User has the responsibility to obtain export licenses, 12 | # or other export authority as may be required before exporting such 13 | # information to foreign countries or providing access to foreign persons. 14 | import unittest 15 | 16 | import ait 17 | from ait.core import log 18 | 19 | 20 | def test_syslog_parser_success(): 21 | """Unit test of the log.SysLogParser.parse method""" 22 | 23 | test_message = ( 24 | "<14>1 2015-03-06T21:29:43.756496Z LMC-037512 ait 12074 " 25 | "INFO - Waiting for AIT telemetry on port 2514" 26 | ) 27 | test_expected = { 28 | "pri": "14", 29 | "version": "1", 30 | "timestamp": "2015-03-06T21:29:43.756496Z", 31 | "hostname": "LMC-037512", 32 | "appname": "ait", 33 | "procid": "12074", 34 | "msgid": "INFO", 35 | "msg": "Waiting for AIT telemetry on port 2514", 36 | } 37 | parts = log.parse_syslog(test_message) 38 | for key, expected in test_expected.items(): 39 | actual = parts.get(key, "") 40 | msg = 'Syslog Parsing failed for "%s" ' % key 41 | msg += '(expected: "%s", actual: "%s")' % (expected, actual) 42 | assert actual == expected 43 | 44 | 45 | def test_syslog_parser_msg_with_hypen(): 46 | """Unit test of the log.SysLogParser.parse method""" 47 | 48 | test_message = ( 49 | "<14>1 2015-03-06T21:29:43.756496Z LMC-037512 ait 12074 " 50 | "INFO - Waiting for AIT - GUI telemetry" 51 | ) 52 | test_expected = { 53 | "pri": "14", 54 | "version": "1", 55 | "timestamp": "2015-03-06T21:29:43.756496Z", 56 | "hostname": "LMC-037512", 57 | "appname": "ait", 58 | "procid": "12074", 59 | "msgid": "INFO", 60 | "msg": "Waiting for AIT - GUI telemetry", 61 | } 62 | parts = log.parse_syslog(test_message) 63 | for key, expected in test_expected.items(): 64 | actual = parts.get(key, "") 65 | msg = 'Syslog Parsing failed for "%s" ' % key 66 | msg += '(expected: "%s", actual: "%s")' % (expected, actual) 67 | assert actual == expected 68 | -------------------------------------------------------------------------------- /tests/ait/core/test_notify.py: -------------------------------------------------------------------------------- 1 | # Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT) 2 | # Bespoke Link to Instruments and Small Satellites (BLISS) 3 | # 4 | # Copyright 2018, by the California Institute of Technology. ALL RIGHTS 5 | # RESERVED. United States Government Sponsorship acknowledged. Any 6 | # commercial use must be negotiated with the Office of Technology Transfer 7 | # at the California Institute of Technology. 8 | # 9 | # This software may be subject to U.S. export control laws. By accepting 10 | # this software, the user agrees to comply with all applicable U.S. export 11 | # laws and regulations. User has the responsibility to obtain export licenses, 12 | # or other export authority as may be required before exporting such 13 | # information to foreign countries or providing access to foreign persons. 14 | from unittest import mock 15 | 16 | import ait.core 17 | from ait.core import notify 18 | 19 | 20 | @mock.patch("ait.core.notify.send_text_alert") 21 | @mock.patch("ait.core.notify.send_email_alert") 22 | def test_trigger_notification(send_email_mock, send_text_mock): 23 | ait.config._config["notifications"] = { 24 | "email": {"triggers": ["email-only-trigger", "both-trigger"]}, 25 | "text": {"triggers": ["text-only-trigger", "both-trigger"]}, 26 | } 27 | 28 | notify.trigger_notification("email-only-trigger", "foo") 29 | send_email_mock.assert_called() 30 | 31 | notify.trigger_notification("text-only-trigger", "foo") 32 | send_text_mock.assert_called() 33 | 34 | send_email_mock.reset_mock() 35 | send_text_mock.reset_mock() 36 | notify.trigger_notification("both-trigger", "foo") 37 | send_email_mock.assert_called() 38 | send_text_mock.assert_called() 39 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/dmc/leapseconds.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NASA-AMMOS/AIT-Core/35679f7fd7191be998f95aa797f591b89ec3976e/tests/ait/core/testdata/dmc/leapseconds.dat -------------------------------------------------------------------------------- /tests/ait/core/testdata/testValidTable1.yaml: -------------------------------------------------------------------------------- 1 | # Sample table for use in tests for ait/core/table. 2 | 3 | - !FSWTable 4 | name: test 5 | delimiter: '-' 6 | uptype: 1 7 | size: 5120 8 | header: 9 | - !FSWColumn: 10 | name: HEADER_COLUMN_ONE 11 | desc: First header column 12 | format: "%u" 13 | units: none 14 | type: U8 15 | bytes: 1 16 | 17 | - !FSWColumn: 18 | name: HEADER_COLUMN_TWO 19 | desc: Second header column 20 | format: "%u" 21 | units: cm 22 | type: U16 23 | bytes: 4 24 | 25 | columns: 26 | - !FSWColumn 27 | name: COL_ONE 28 | desc: First table column 29 | format: "%u" 30 | units: none 31 | type: U8 32 | bytes: 1 33 | 34 | - !FSWColumn 35 | name: COL_TWO 36 | desc: Second table column 37 | format: "%u" 38 | units: cm 39 | type: U16 40 | bytes: 4 41 | enum: 42 | 0: SPRING 43 | 1: SUMMER 44 | 2: AUTUMN 45 | 3: WINTER 46 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/util/test_util.txt: -------------------------------------------------------------------------------- 1 | “I choose a lazy person to do a hard job. Because a lazy person will find an easy way to do it.” 2 | - Bill Gates 3 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/val/testCmdValidator1.yaml: -------------------------------------------------------------------------------- 1 | - !Command 2 | name: AIT_DUPLICATE_COMMAND 3 | opcode: 0x1001 4 | subsystem: DCC 5 | desc: | 6 | This is a generic command 7 | 8 | arguments: 9 | - !Argument 10 | name: genericargument 11 | desc: genericargument 12 | units: none 13 | type: MSB_U16 14 | bytes: [0,1] 15 | 16 | # Following command has same name as the first 17 | - !Command 18 | name: AIT_DUPLICATE_COMMAND 19 | opcode: 0x2001 20 | subsystem: CMD 21 | desc: | 22 | This command has a duplicate name as the first command 23 | 24 | arguments: 25 | - !Argument 26 | name: genericargument 27 | desc: genericargument 28 | units: none 29 | type: MSB_U16 30 | bytes: [0,1] 31 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/val/testCmdValidator2.yaml: -------------------------------------------------------------------------------- 1 | - !Command 2 | name: AIT_COMMAND1 3 | opcode: 0x1001 4 | subsystem: DCC 5 | desc: | 6 | This is a generic command 7 | 8 | arguments: 9 | - !Argument 10 | name: genericargument 11 | desc: genericargument 12 | units: none 13 | type: MSB_U16 14 | bytes: [0,1] 15 | 16 | 17 | # Following command has same name as the first 18 | - !Command 19 | name: AIT_COMMAND2 20 | opcode: 0x1001 21 | subsystem: CMD 22 | desc: | 23 | This command has the same opcode as the first command 24 | 25 | arguments: 26 | - !Argument 27 | name: genericargument 28 | desc: genericargument 29 | units: none 30 | type: MSB_U16 31 | bytes: [0,1] 32 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/val/testCmdValidator3.yaml: -------------------------------------------------------------------------------- 1 | - !Command 2 | name: AIT_COMMAND 3 | opcode: 0x1001 4 | subsystem: DCC 5 | desc: | 6 | This command has a bad type identifier 7 | 8 | arguments: 9 | - !Argument 10 | name: genericargument 11 | desc: genericargument 12 | units: none 13 | type: BADTYPE 14 | bytes: [0,1] 15 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/val/testCmdValidator4.yaml: -------------------------------------------------------------------------------- 1 | - !Command 2 | name: AIT_COMMAND 3 | opcode: 0x1000 4 | subsystem: CMD 5 | desc: | 6 | This is a generic command 7 | 8 | arguments: 9 | - !Argument 10 | name: genericargument1 11 | desc: genericargument1 12 | units: none 13 | type: MSB_U16 14 | bytes: [0,1] 15 | 16 | - !Argument 17 | name: genericargument2 18 | desc: genericargument2 19 | units: none 20 | type: U8 21 | bytes: [2,3] # invalid nbytes, type says 1 byte but 2 specified 22 | enum: 23 | 0: DISABLED 24 | 1: ENABLED 25 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/val/testCmdValidator5.yaml: -------------------------------------------------------------------------------- 1 | - !Command 2 | name: AIT_COMMAND 3 | opcode: 0x1000 4 | subsystem: CMD 5 | desc: | 6 | This is a generic command 7 | 8 | arguments: 9 | - !Argument 10 | name: genericargument1 11 | desc: genericargument1 12 | units: none 13 | type: MSB_U16 14 | bytes: [0,1] 15 | 16 | - !Argument 17 | name: genericargument2 18 | desc: genericargument2 19 | units: none 20 | type: U8 21 | bytes: 3 # invalid byte order, should be 2 22 | enum: 23 | 0: DISABLED 24 | 1: ENABLED 25 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/val/testCmdValidator6.yaml: -------------------------------------------------------------------------------- 1 | - !Command 2 | name: AIT_COMMAND 3 | opcode: 0x1000 4 | subsystem: CMD 5 | desc: | 6 | This is a generic command 7 | 8 | arguments: 9 | - !Argument 10 | name: genericargument1 11 | desc: genericargument1 12 | units: none 13 | type: MSB_U16 14 | bytes: [1,2] 15 | 16 | - !Argument 17 | name: genericargument2 18 | desc: genericargument2 19 | units: none 20 | type: U8 21 | bytes: 3 22 | enum: 23 | 0: DISABLED 24 | 1: ENABLED 25 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/val/testCmdValidator7.yaml: -------------------------------------------------------------------------------- 1 | - !Command 2 | name: AIT_COMMAND 3 | opcode: 0x1000 4 | subsystem: CMD 5 | desc: | 6 | This is a generic command 7 | 8 | arguments: 9 | - !Argument 10 | name: genericargument1 11 | desc: genericargument1 12 | units: none 13 | type: MSB_U16 14 | bytes: [0,1] 15 | 16 | - !Argument 17 | name: genericargument2 18 | desc: genericargument2 19 | units: none 20 | type: U8 21 | bytes: 2 22 | enum: 23 | 0: DISABLED 24 | 1: ENABLED 25 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/val/testCmdValidator8.yaml: -------------------------------------------------------------------------------- 1 | - !Command 2 | name: AIT_COMMAND 3 | opcode: 0x1000 4 | subsystem: CMD 5 | desc: | 6 | This is a generic command 7 | 8 | arguments: 9 | - !Argument 10 | name: genericargument1 11 | desc: genericargument1 12 | units: none 13 | type: MSB_U16 14 | bytes: [0,1] 15 | 16 | - !Argument 17 | name: genericargument2 18 | desc: genericargument2 19 | units: none 20 | type: U8 21 | bytes: 2 22 | enum: 23 | 0: True 24 | 1: 'False' 25 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/val/testInvalidCmd1.yaml: -------------------------------------------------------------------------------- 1 | - !Command 2 | name: AIT_DUPLICATE_COMMAND 3 | opcode: BAD_OPCODE 4 | subsystem: DCC 5 | desc: | 6 | This is a generic command 7 | 8 | arguments: 9 | - !Argument 10 | name: genericargument 11 | desc: genericargument 12 | units: none 13 | type: MSB_U16 14 | bytes: [0,1] 15 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/val/testInvalidTlm1.yaml: -------------------------------------------------------------------------------- 1 | - !Packet 2 | name: CCSDS_Packet 3 | type: ethernet 4 | desc: TBD 5 | fields: 6 | - !Field 7 | name: version 8 | desc: Indicates CCSDS Version-1 (does not change) 9 | bytes: test 10 | type: U8 11 | mask: 0xE0 12 | - !Field 13 | name: SampleTime 14 | type: TIME64 15 | bytes: test 16 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/val/testSchemaLoad1.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "Command Dictionary Schema", 4 | "description": "Command Dictionary Schema", 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "type": "string" 9 | }, 10 | "opcode": { 11 | "type": "integer" 12 | }, 13 | "subsystem": { 14 | "type": "string" 15 | }, 16 | "desc": { 17 | "type": "string" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/val/testTlmValidator1.yaml: -------------------------------------------------------------------------------- 1 | # Test YAML for TLM Dict Validation 2 | # 3 | # Expected Result: Validation Failure 4 | # Reason: Duplicate packet name 5 | # 6 | - !Packet 7 | name: CCSDS_Packet 8 | desc: TBD 9 | fields: 10 | - !Field 11 | name: version 12 | desc: Indicates CCSDS Version-1 (does not change) 13 | bytes: 0 14 | type: U8 15 | mask: 0xE0 16 | - !Field 17 | name: SampleTime 18 | type: TIME64 19 | bytes: 1 20 | 21 | - !Packet 22 | name: CCSDS_Packet 23 | desc: TBD 24 | fields: 25 | - !Field 26 | name: version2 27 | desc: Indicates CCSDS Version-1 (does not change) 28 | bytes: 0 29 | type: U8 30 | mask: 0xE0 31 | - !Field 32 | name: SampleTime2 33 | type: TIME64 34 | bytes: 1 35 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/val/testTlmValidator2.yaml: -------------------------------------------------------------------------------- 1 | # Test YAML for TLM Dict Validation 2 | # 3 | # Expected Result: Validation Failure 4 | # Reason: Duplicate field name 5 | # 6 | - !Packet 7 | name: CCSDS_Packet 8 | desc: TBD 9 | fields: 10 | - !Field 11 | name: version 12 | desc: Indicates CCSDS Version-1 (does not change) 13 | bytes: 0 14 | type: U8 15 | mask: 0xE0 16 | - !Field 17 | name: version 18 | desc: | 19 | Distinguishes between core and payload packet types to extend the 20 | APID space to 4032 21 | bytes: 0 22 | type: U8 23 | mask: 0x10 24 | enum: 25 | 0: 'Core' 26 | 1: 'Payload' 27 | - !Field 28 | name: secondary_header_flag 29 | desc: | 30 | Indicates whether, or not, a Secondary Header follows the primary 31 | header (always set to 1) 32 | bytes: 0 33 | type: U8 34 | mask: 0x08 35 | enum: 36 | 0: 'Not Present' 37 | 1: 'Present' 38 | - !Field 39 | name: apid 40 | desc: | 41 | Used in conjunction with Type to define the Logical Data Path 42 | bytes: [0, 1] 43 | type: MSB_U16 44 | mask: 0x07FF 45 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/val/testTlmValidator3.yaml: -------------------------------------------------------------------------------- 1 | # Test YAML for TLM Dict Validation 2 | # 3 | # Expected Result: Validation Failure 4 | # Reason: Invalid field type for field version 5 | # 6 | - !Packet 7 | name: CCSDS_Packet 8 | desc: TBD 9 | fields: 10 | - !Field 11 | name: version 12 | desc: Indicates CCSDS Version-1 (does not change) 13 | bytes: 0 14 | type: BAD_TYPE 15 | mask: 0xE0 16 | - !Field 17 | name: type 18 | desc: | 19 | Distinguishes between core and payload packet types to extend the 20 | APID space to 4032 21 | bytes: 0 22 | type: U8 23 | mask: 0x10 24 | enum: 25 | 0: 'Core' 26 | 1: 'Payload' 27 | - !Field 28 | name: secondary_header_flag 29 | desc: | 30 | Indicates whether, or not, a Secondary Header follows the primary 31 | header (always set to 1) 32 | bytes: 0 33 | type: U8 34 | mask: 0x08 35 | enum: 36 | 0: 'Not Present' 37 | 1: 'Present' 38 | - !Field 39 | name: apid 40 | desc: | 41 | Used in conjunction with Type to define the Logical Data Path 42 | bytes: [0, 1] 43 | type: MSB_U16 44 | mask: 0x07FF 45 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/val/testTlmValidator4.yaml: -------------------------------------------------------------------------------- 1 | # Test YAML for TLM Dict Validation 2 | # 3 | # Expected Result: Validation Failure 4 | # Reason: Field size for apid is invalid 5 | # 6 | - !Packet 7 | name: CCSDS_Packet 8 | desc: TBD 9 | fields: 10 | - !Field 11 | name: version 12 | desc: Indicates CCSDS Version-1 (does not change) 13 | bytes: 0 14 | type: U8 15 | mask: 0xE0 16 | - !Field 17 | name: type 18 | desc: | 19 | Distinguishes between core and payload packet types to extend the 20 | APID space to 4032 21 | bytes: 0 22 | type: U8 23 | mask: 0x10 24 | enum: 25 | 0: 'Core' 26 | 1: 'Payload' 27 | - !Field 28 | name: secondary_header_flag 29 | desc: | 30 | Indicates whether, or not, a Secondary Header follows the primary 31 | header (always set to 1) 32 | bytes: 0 33 | type: U8 34 | mask: 0x08 35 | enum: 36 | 0: 'Not Present' 37 | 1: 'Present' 38 | - !Field 39 | name: apid 40 | desc: | 41 | Used in conjunction with Type to define the Logical Data Path 42 | bytes: [0, 3] 43 | type: MSB_U16 44 | mask: 0x07FF 45 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/val/testTlmValidator5.yaml: -------------------------------------------------------------------------------- 1 | # Test YAML for TLM Dict Validation 2 | # 3 | # Expected Result: Validation Failure 4 | # Reason: TRUE not enclosed in quotes for secondary_header_flag enums 5 | # 6 | - !Packet 7 | name: CCSDS_Packet 8 | desc: TBD 9 | fields: 10 | - !Field 11 | name: version 12 | desc: Indicates CCSDS Version-1 (does not change) 13 | bytes: 0 14 | type: U8 15 | mask: 0xE0 16 | - !Field 17 | name: type 18 | desc: | 19 | Distinguishes between core and payload packet types to extend the 20 | APID space to 4032 21 | bytes: 0 22 | type: U8 23 | mask: 0x10 24 | enum: 25 | 0: 'Core' 26 | 1: 'Payload' 27 | - !Field 28 | name: secondary_header_flag 29 | desc: | 30 | Indicates whether, or not, a Secondary Header follows the primary 31 | header (always set to 1) 32 | bytes: 0 33 | type: U8 34 | mask: 0x08 35 | enum: 36 | 0: TRUE 37 | 1: 'Present' 38 | - !Field 39 | name: apid 40 | desc: | 41 | Used in conjunction with Type to define the Logical Data Path 42 | bytes: [0, 1] 43 | type: MSB_U16 44 | mask: 0x07FF 45 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/val/testTlmValidator6.yaml: -------------------------------------------------------------------------------- 1 | # Test YAML for TLM Dict Validation 2 | # 3 | # Expected Result: Validation Failure 4 | # Reason: version.desc is empty string 5 | # 6 | - !Packet 7 | name: CCSDS_Packet 8 | desc: TBD 9 | fields: 10 | - !Field 11 | name: version 12 | desc: 13 | bytes: 0 14 | type: U8 15 | mask: 0xE0 16 | - !Field 17 | name: type 18 | desc: | 19 | Distinguishes between core and payload packet types to extend the 20 | APID space to 4032 21 | bytes: 0 22 | type: U8 23 | mask: 0x10 24 | enum: 25 | 0: 'Core' 26 | 1: 'Payload' 27 | - !Field 28 | name: secondary_header_flag 29 | desc: | 30 | Indicates whether, or not, a Secondary Header follows the primary 31 | header (always set to 1) 32 | bytes: 0 33 | type: U8 34 | mask: 0x08 35 | enum: 36 | 0: TRUE 37 | 1: 'Present' 38 | - !Field 39 | name: apid 40 | desc: | 41 | Used in conjunction with Type to define the Logical Data Path 42 | bytes: [0, 1] 43 | type: MSB_U16 44 | mask: 0x07FF 45 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/val/testTlmValidator7.yaml: -------------------------------------------------------------------------------- 1 | - !Packet 2 | name: Packet 3 | fields: 4 | - !Field 5 | name: foo 6 | bytes: [0] 7 | type: U8 8 | - !Field 9 | name: bar 10 | bytes: 1 11 | type: U8 12 | - !Field 13 | name: baz 14 | bytes: [9,10] 15 | type: MSB_U16 16 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/val/testValidCmd1.yaml: -------------------------------------------------------------------------------- 1 | - !Command 2 | name: NO_OP 3 | opcode: 0x0001 4 | subsystem: CORE 5 | title: NO_OP 6 | desc: | 7 | Standard NO_OP command. 8 | 9 | - !Command 10 | name: SEQ_START 11 | opcode: 0x002 12 | subsystem: CMD 13 | title: Start Sequence 14 | desc: | 15 | This command starts a specified command sequence. 16 | 17 | arguments: 18 | - !Argument 19 | name: sequence_id 20 | desc: Sequence ID 21 | units: none 22 | type: MSB_U16 23 | bytes: [0,1] 24 | 25 | - !Command 26 | name: SEQ_ENABLE_DISABLE 27 | opcode: 0x0003 28 | subsystem: CMD 29 | title: Enable/Disable Sequence 30 | desc: | 31 | This command enables or disabled the specified sequence. If a 32 | sequence to be disabled is currently executing, it will be 33 | interrupted. 34 | 35 | arguments: 36 | - !Argument 37 | name: sequence_id 38 | desc: Sequence ID 39 | units: none 40 | type: MSB_U16 41 | bytes: [0,1] 42 | 43 | - !Argument 44 | name: enable 45 | desc: Enable 46 | units: none 47 | type: U8 48 | bytes: 2 49 | enum: 50 | 0: DISABLED 51 | 1: ENABLED 52 | 53 | - !Command 54 | name: SEND_FIXED_ARG 55 | opcode: 0x0004 56 | subsystem: CMD 57 | title: Send Fixed Argument 58 | desc: | 59 | This command tests sending a fixed argument. 60 | 61 | arguments: 62 | - !Fixed 63 | name: fixed_arg 64 | desc: Fixed Argument 65 | units: none 66 | type: MSB_U16 67 | bytes: [0,1] 68 | value: 5 69 | 70 | - !Command 71 | name: SEND_STR_ARG 72 | opcode: 0x0005 73 | subsystem: CMD 74 | title: Send string argument value 75 | desc: | 76 | This command tests sending a single string argument. 77 | 78 | arguments: 79 | - !Argument 80 | name: str_arg 81 | desc: String Argument 82 | units: none 83 | type: S16 84 | bytes: [0,15] 85 | -------------------------------------------------------------------------------- /tests/ait/core/testdata/val/testValidTlm1.yaml: -------------------------------------------------------------------------------- 1 | - !Packet 2 | name: CCSDS_Packet 3 | desc: TBD 4 | fields: 5 | - !Field 6 | name: version 7 | desc: Indicates CCSDS Version-1 (does not change) 8 | bytes: 0 9 | type: U8 10 | mask: 0xE0 11 | - !Field 12 | name: SampleTime 13 | type: TIME64 14 | bytes: 1 15 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | py37 4 | py38 5 | py39 6 | py310 7 | docs 8 | lint 9 | 10 | isolated_build = True 11 | 12 | [gh-actions] 13 | python = 14 | 3.7: py37 15 | 3.8: py38 16 | 3.9: py39 17 | 3.10: py310, docs, lint, distcheck 18 | 19 | [testenv] 20 | setenv = AIT_CONFIG = {toxinidir}/config/config.yaml 21 | whitelist_externals = poetry 22 | commands_pre = 23 | poetry install 24 | commands = 25 | poetry run pytest tests/ 26 | 27 | [testenv:docs] 28 | setenv = AIT_CONFIG = {toxinidir}/config/config.yaml 29 | whitelist_externals = poetry 30 | commands_pre = 31 | poetry install 32 | commands = 33 | poetry run sphinx-build doc/source doc/build 34 | basepython = python3.10 35 | 36 | [testenv:lint] 37 | setenv = AIT_CONFIG = {toxinidir}/config/config.yaml 38 | commands_pre = 39 | poetry install 40 | commands= 41 | python -m pre_commit run --color=always {posargs:--all} 42 | basepython = python3.10 43 | 44 | [testenv:distcheck] 45 | skip_install = true 46 | deps = 47 | twine 48 | poetry 49 | commands = 50 | poetry build 51 | poetry run twine check dist/* 52 | --------------------------------------------------------------------------------