{"id":18750337,"url":"https://github.com/ntia/scos-actions","last_synced_at":"2025-10-12T15:10:23.841Z","repository":{"id":37945675,"uuid":"247137089","full_name":"NTIA/scos-actions","owner":"NTIA","description":"Base repository for creating new actions for scos-sensor and supporting new hardware.","archived":false,"fork":false,"pushed_at":"2025-01-17T18:48:08.000Z","size":1722,"stargazers_count":3,"open_issues_count":7,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-26T17:57:18.504Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NTIA.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-03-13T18:27:16.000Z","updated_at":"2024-12-17T22:40:12.000Z","dependencies_parsed_at":"2023-10-23T15:26:00.132Z","dependency_job_id":"56cc9a28-877a-4545-a902-c0deaaaee8c4","html_url":"https://github.com/NTIA/scos-actions","commit_stats":null,"previous_names":[],"tags_count":41,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NTIA%2Fscos-actions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NTIA%2Fscos-actions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NTIA%2Fscos-actions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NTIA%2Fscos-actions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NTIA","download_url":"https://codeload.github.com/NTIA/scos-actions/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248647257,"owners_count":21139081,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-07T17:11:27.975Z","updated_at":"2025-10-12T15:10:23.774Z","avatar_url":"https://github.com/NTIA.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NTIA/ITS SCOS Actions Plugin\n\n[![GitHub release (latest SemVer)][latest-release-semver-badge]][github-releases]\n[![GitHub Actions Test Status][github-actions-test-badge]][github-actions-tox-link]\n[![GitHub all releases][github-download-count-badge]][github-releases]\n[![GitHub issues][github-issue-count-badge]][github-issues]\n[![Code style: black][code-style-badge]][code-style-repo]\n\n[github-actions-tox-link]: https://github.com/NTIA/scos-actions/actions/workflows/tox.yaml\n[github-actions-test-badge]: https://github.com/NTIA/scos-actions/actions/workflows/tox.yaml/badge.svg\n[code-style-badge]: https://img.shields.io/badge/code%20style-black-000000.svg\n[code-style-repo]: https://github.com/psf/black\n[latest-release-semver-badge]: https://img.shields.io/github/v/release/NTIA/scos-actions?display_name=tag\u0026sort=semver\n[github-releases]: https://github.com/NTIA/scos-actions/releases\n[github-download-count-badge]: https://img.shields.io/github/downloads/NTIA/scos-actions/total\n[github-issue-count-badge]: https://img.shields.io/github/issues/NTIA/scos-actions\n[github-issues]: https://github.com/NTIA/scos-actions/issues\n\nThis repository contains common actions and interfaces to be re-used by SCOS Sensor\nplugins. See the [SCOS Sensor documentation](\n\u003chttps://github.com/NTIA/scos-sensor/blob/master/README.md\u003e)\nfor more information about SCOS Sensor, especially the [Architecture](\n\u003chttps://github.com/NTIA/scos-sensor/blob/master/README.md#architecture\u003e\n) and the [Actions and Hardware Support](\n\u003chttps://github.com/NTIA/scos-sensor/blob/master/README.md#actions-and-hardware-support\u003e\n) sections which explain how SCOS Actions is used in the SCOS plugin\narchitecture.\n\n## Table of Contents\n\n- [Overview of Repo Structure](#overview-of-repo-structure)\n- [Running in SCOS Sensor](#running-in-scos-sensor)\n- [Development](#development)\n- [License](#license)\n- [Contact](#contact)\n\n## Overview of Repo Structure\n\n- `scos_actions/actions`: This includes base Action classes and the following\n  common action classes:\n  - `acquire_single_freq_fft`: performs FFTs and calculates mean, median, min, max, and\n    sample statistics at a single center frequency.\n  - `acquire_single_freq_tdomain_iq`: acquires IQ data at a single center frequency.\n  - `acquire_stepped_freq_tdomain_iq`: acquires IQ data at multiple center frequencies.\n  - `calibrate_y_factor`: performs calibration using the Y-Factor method.\n  - `monitor_sigan`: ensures a signal analyzer is available and is able to maintain a\n    connection to the computer.\n  - `sync_gps`: gets GPS location and syncs the host to GPS time\n- `scos_actions/calibration`: This includes an interface for sensor calibration data\n- `scos_actions/configs/actions`: This folder contains the YAML files with the parameters\n  used to initialize the actions described above.\n- `scos_actions/discover`: This includes the code to read YAML files and make actions\n  available to SCOS Sensor.\n- `scos_actions/hardware`: This includes the signal analyzer and GPS interfaces used by\n  actions and the mock signal analyzer. The signal analyzer interface represents functionality\n  common to all signal analyzers. Specific implementations of the signal analyzer interface\n  for particular signal analyzers are provided in separate repositories like\n  [scos-usrp](https://github.com/NTIA/scos-usrp).\n- `scos_actions/metadata`: This includes the `SigMFBuilder` class and related metadata\n  structures used to generate [SigMF](https://github.com/SigMF/SigMF)-compliant metadata.\n- `scos_actions/signal_processing`: This contains various common signal processing\nroutines which are used in actions.\n- `scos_actions/status`: This provides a class to register objects with the SCOS Sensor\n  status endpoint.\n\n## Running in SCOS Sensor\n\nRefer to the [SCOS Sensor documentation](https://github.com/NTIA/scos-sensor#readme) for\ndetailed instructions. To run SCOS Actions in SCOS Sensor with a mock signal analyzer,\nset `MOCK_SIGAN` and `MOCK_SIGAN_RANDOM` equal to 1 in `docker-compose.yml` before\nstarting SCOS Sensor:\n\n```yaml\nservices:\n  ...\n  api:\n    ...\n    environment:\n      ...\n      - MOCK_SIGAN=1\n      - MOCK_SIGAN_RANDOM=1\n```\n\nThe following parameterized actions are offered for testing using a mock signal analyzer;\ntheir parameters are defined in `scos_actions/configs/actions`.\n\n- `test_multi_frequency_iq_action`\n- `test_multi_frequency_y_factor_action`\n- `test_single_frequency_iq_action`\n- `test_single_frequency_m4s_action`\n- `test_single_frequency_y_factor_action`\n\n## Development\n\nThis repository is intended to be used by all SCOS Sensor plugins. Therefore, only\nuniversal actions that apply to most RF measurement systems should be added to\nSCOS Actions. Custom actions for specific hardware should be added to plugins in\nrepositories supporting that specific hardware. New functionality should only be\nadded to the [signal analyzer interface defined in this repository](scos_actions/hardware/sigan_iface.py)\nif the new functionality can be supported by most signal analyzers.\n\n### Requirements and Configuration\n\nSet up a development environment using a tool like [Conda](https://docs.conda.io/en/latest/)\nor [venv](https://docs.python.org/3/library/venv.html#module-venv), with `python\u003e=3.9`. Then,\nfrom the cloned directory, install the development dependencies by running:\n\n```bash\npip install .[dev]\n```\n\nThis will install the project itself, along with development dependencies for pre-commit\nhooks, building distributions, and running tests. Set up pre-commit, which runs\nauto-formatting and code-checking automatically when you make a commit, by running:\n\n```bash\npre-commit install\n```\n\nThe pre-commit tool will auto-format Python code using [Black](https://github.com/psf/black)\nand [isort](https://github.com/pycqa/isort). Other pre-commit hooks are also enabled, and\ncan be found in [`.pre-commit-config.yaml`](.pre-commit-config.yaml).\n\n### Building New Releases\n\nThis project uses [Hatchling](https://github.com/pypa/hatch/tree/master/backend) as a\nbackend. Hatchling makes versioning and building new releases easy. The package version can\nbe updated easily by using any of the following commands.\n\n```bash\nhatchling version major   # 1.0.0 -\u003e 2.0.0\nhatchling version minor   # 1.0.0 -\u003e 1.1.0\nhatchling version micro   # 1.0.0 -\u003e 1.0.1\nhatchling version \"X.X.X\" # 1.0.0 -\u003e X.X.X\n```\n\nTo build a new release (both wheel and sdist/tarball), run:\n\n```bash\nhatchling build\n```\n\n### Running Tests\n\nIdeally, you should add a test to cover any new feature that you add. If you've done\nthat, then running the included test suite is the easiest way to check that everything\nis working. In any case, all tests should be run after making any local modifications\nto ensure that you haven't caused a regression.\n\nThe `scos_actions` package is tested using the [pytest](https://docs.pytest.org/en/stable/)\nframework. Additionally, [tox](https://tox.readthedocs.io/en/latest/) is used to run all\navailable tests in a virtual environment against all supported versions of Python.\nRunning `pytest` directly is faster but running `tox` is a more thorough test.\n\nThe following commands can be used to run tests. Note, for tox to run with all Python\nversions listed in the tox configuration (in [`tox.ini`](tox.ini)), all\nthose versions must be installed on your system. Any missing versions will be skipped.\n\n```bash\npytest          # faster, but less thorough\npytest --cov    # check where test coverage lacks\ntox             # tests code in clean virtual environments, with multiple versions of Python\ntox --recreate  # forces recreation of tox virtual environments\n```\n\n### Adding Actions\n\nTo expose a new action to the API, check out the available\n[action classes](scos_actions/actions/__init__.py). An *action* is a parameterized\nimplementation of an action class. If an existing class covers your needs, you can\nsimply create YAML configs and use the `init` method in\n[`scos_actions.discover`](scos_actions/discover/__init__.py) to make these actions available.\n\n```python\nfrom scos_actions.discover import init\nfrom scos_usrp.hardware import gps, sigan\n\nactions = {\n  \"monitor_usrp\": MonitorSignalAnalyzer(sigan),\n  \"sync_gps\": SyncGps(gps),\n}\n\nyaml_actions, yaml_test_actions = init(yaml_dir=ACTION_DEFINITIONS_DIR)\n\nactions.update(yaml_actions)\n```\n\nIf no existing action class meets your needs, see [Writing Custom Actions](\n    #writing-custom-actions).\n\n#### Creating a YAML config file for an action\n\nActions can be manually initialized in `discover/__init__.py`, but an easier method for\nnon-developers and configuration-management software is to place a YAML file in the\n`configs/actions` directory which contains the action class name and parameter\ndefinitions.\n\nThe file name can be anything. File extensions must be `.yml`.\n\nThe action initialization logic parses all YAML files in this directory and registers\nthe requested actions in the API.\n\nLet's look at an example.\n\n##### Example\n\nLet's say we want to make an instance of the `SingleFrequencyFftAcquisition`.\n\nFirst, create a new YAML file in the\n`scos_actions/configs/actions` directory. In this example we're going to create\nan acquisition for the LTE 700 C band downlink, so we'll call it `acquire_700c_dl.yml`.\n\nNext, we want to find the appropriate string key for the\n`SingleFrequencyFftAcquisition` class. Look in [actions/\\_\\_init\\_\\_.py](\n    scos_actions/actions/__init__.py) at the `action_classes` dictionary. There, we\nsee:\n\n```python\naction_classes = {\n    ...\n    \"single_frequency_fft\": SingleFrequencyFftAcquisition,\n    ...\n}\n```\n\nThat key tells the action loader which class to create an instance of. Put it as the\nfirst non-comment line, followed by a colon:\n\n```yaml\n# File: acquire_700c_dl.yml\n\nsingle_frequency_fft:\n```\n\nThe next step is to see what parameters that class takes and specify the values. Open\nup [actions/acquire_single_freq_fft.py](\n    scos_actions/actions/acquire_single_freq_fft.py) and look at the documentation for\nthe class to see what parameters are available and what units to use, etc.\n\n```python\nclass SingleFrequencyFftAcquisition(MeasurementAction):\n    \"\"\"Perform M4S detection over requested number of single-frequency FFTs.\n\n    The action will set any matching attributes found in the signal\n    analyzer object. The following parameters are required by the action:\n\n        name: name of the action\n        frequency: center frequency in Hz\n        fft_size: number of points in FFT (some 2^n)\n        nffts: number of consecutive FFTs to pass to detector\n\n    For the parameters required by the signal analyzer, see the\n    documentation from the Python package for the signal analyzer being\n    used.\n\n    :param parameters: The dictionary of parameters needed for the\n        action and the signal analyzer.\n    :param sigan: Instance of SignalAnalyzerInterface.\n    \"\"\"\n```\n\nThen look at the docstring for the signal analyzer class being used. This example will\nuse the [MockSignalAnalyzer](scos_actions/hardware/mocks/mock_sigan.py). That file\ncontains the following:\n\n```python\nclass MockSignalAnalyzer(SignalAnalyzerInterface):\n    \"\"\"\n    MockSignalAnalyzer is mock signal analyzer object for testing.\n\n    The following parameters are required for measurements:\n    sample_rate: requested sample rate in samples/second\n    frequency: center frequency in Hz\n    gain: requested gain in dB\n    \"\"\"\n```\n\nLastly, simply modify the YAML file to define any required parameters from the action\nand signal analyzer. Note that the `sigan` parameter is a special parameter that will get\npassed in separately when the action is initialized from the YAML. Therefore, it does\nnot need to be defined in the YAML file.\n\n```yaml\n# File: acquire_700c_dl.yml\n\nsingle_frequency_fft:\n  name: acquire_700c_dl\n  frequency: 751e6\n  gain: 40\n  sample_rate: 15.36e6\n  fft_size: 1024\n  nffts: 300\n```\n\nYou're done.\n\n#### Writing Custom Actions\n\n\"Actions\" are one of the main concepts used by [SCOS Sensor](\n\u003chttps://github.com/NTIA/scos-sensor\u003e). At a high level, they are the things that the\nsensor owner wants the sensor to be able to *do*. At a lower level, they are simply\nPython classes with a special method `__call__`. Actions use [Django Signals](\n\u003chttps://docs.djangoproject.com/en/3.1/topics/signals/\u003e) to provide data and results to\nSCOS Sensor.\n\nStart by looking at the [`Action` base class](scos_actions/actions/interfaces/action.py).\nIt includes some logic to parse a description and summary out of the action class's\ndocstring, and a `__call__` method that must be overridden. Actions are only instantiated\nwith parameters. The signal analyzer implementation will be passed to the action at\nexecution time through the __call__ method's Sensor object.\n\nA new custom action can inherit from the existing action classes to reuse and build\nupon existing functionality. A [`MeasurementAction` base class](scos_actions/actions/interfaces/measurement_action.py),\nwhich inherits from the `Action` class, is also useful for building new actions.\nFor example, [`SingleFrequencyTimeDomainIqAcquisition`](scos_actions/actions/acquire_single_freq_tdomain_iq.py)\ninherits from `MeasurementAction`, while [`SteppedFrequencyTimeDomainIqAcquisition`](scos_actions/actions/acquire_stepped_freq_tdomain_iq.py)\ninherits from `SingleFrequencyTimeDomainIqAcquisition`.\n\nDepending on the type of action, a signal should be sent upon action completion. This\nenables SCOS Sensor to do something with the results of the action. This could range\nfrom storing measurement data to recycling a Docker container or to fixing an unhealthy\nconnection to the signal analyzer. You can see the available signals in\n[`scos_actions/signals.py`](scos_actions/signals.py).\nThe following signals are currently offered for actions:\n\n- `measurement_action_completed` - signal expects task_id, data, and metadata\n- `location_action_completed` - signal expects latitude and longitude\n- `trigger_api_restart` - triggers a restart of the API docker container (where\nSCOS Sensor runs)\n\nNew signals can be added. However, corresponding signal handlers must be added to\nSCOS Sensor to receive the signals and process the results.\n\n##### Adding custom action to SCOS Actions\n\nA custom action meant to be re-used by other plugins can live in SCOS Actions. It can\nbe instantiated using a YAML file, or directly in the `actions` dictionary in the\n`discover/__init__.py` module.\n\n##### Adding system or hardware specific custom action\n\nIn the repository that provides the plugin to support the hardware being used, add the\naction to the `actions` dictionary in the `discover/__init__.py` file. Optionally,\ninitialize the action using a YAML file by importing the YAML initialization code from\nSCOS Actions. For an example of this, see the [Adding Actions subsection](#adding-actions)\nabove.\n\n### Supporting a Different Signal Analyzer\n\n[scos_usrp](https://github.com/NTIA/scos-usrp) adds support for the Ettus B2xx line of\nsignal analyzers to SCOS Sensor. Follow these instructions to add support for\nanother signal analyzer with a Python API.\n\n- Create a new repository called `scos-[signal analyzer name]`.\n- Create a new virtual environment and activate it:\n  `python -m venv ./venv \u0026\u0026 source venv/bin/activate`.\n  Upgrade pip: `python -m pip install --upgrade pip`.\n- In the new repository, add this repository as a dependency and create a\n  class that inherits from the [SignalAnalyzerInterface](scos_actions/hardware/sigan_iface.py)\n  abstract class. Add properties or class variables for the parameters needed to\n  configure the signal analyzer.\n- Create YAML files with the parameters needed to run the actions imported from\n  `scos_actions` using the new signal analyzer. Put them in the new repository in\n  `configs/actions`. This should contain the parameters needed by the action as well as\n  the signal analyzer settings based on which properties or class variables were\n  implemented in the signal analyzer class in the previous step. The measurement actions\n  in SCOS Actions are configured to check if any YAML parameters are available as\n  attributes in the signal analyzer object, and to set them to the given YAML value if\n  available. For example, if the new signal analyzer class has a bandwidth property,\n  simply add a bandwidth parameter to the YAML file. Alternatively, you can create\n  custom actions that are unique to the hardware. See [Adding Actions](#adding-actions)\n  subsection above.\n- In the new repository, add a `discover/__init__.py` file. This should contain a\n  dictionary called `actions` with keys of action names and values of action instances.\n  If the repository also includes new action implementations, it should also expose a\n  dictionary named `action_classes` with keys of actions names and values of action classes.\n  You can use the [init()](scos_actions/discover/__init__.py) and/or the\n  [load_from_yaml()](scos_actions/discover/yaml.py) methods provided in this repository\n  to look for YAML files and initialize actions. You can use the existing\n  action classes [defined in this repository](scos_actions/actions/__init__.py) or\n  [create custom actions](#writing-custom-actions).\n\nIf your signal analyzer doesn't have a Python API, you'll need a Python wrapper that\ncalls out to your signal analyzer's available API and reads the samples back into\nPython. Libraries such as [SWIG](http://www.swig.org/) can automatically generate\nPython wrappers for programs written in C/C++.\n\nThe next step in supporting a different signal analyzer is to create a class that\ninherits from the [GPSInterface](scos_actions/hardware/gps_iface.py) abstract class if\nthe signal analyzer includes GPS capabilities.\nThen add the `sync_gps` and `monitor_sigan` actions to your `actions` dictionary,\npassing the gps object to the `SyncGps` constructor, and the signal analyzer object to\nthe `MonitorSignalAnalyzer` constructor. See the example in the [Adding Actions\nsubsection](#adding-actions) above.\n\nThe final step would be to add a `pyproject.toml` to allow for installation of the new\nrepository as a Python package. You can use the [pyproject.toml](pyproject.toml) in this\nrepository as a reference. You can find more information about Python packaging [here](\n\u003chttps://packaging.python.org/tutorials/packaging-projects/\u003e). Then add the new\nrepository as a dependency to [SCOS Sensor's requirements.txt](\n\u003chttps://github.com/NTIA/scos-sensor/blob/master/src/requirements.txt\u003e)\nusing the following format:\n`\u003cpackage_name\u003e @ git+\u003clink_to_github_repo\u003e@\u003cbranch_name\u003e`. If\nspecific drivers are required for your signal analyzer, you can attempt to link to them\nwithin the package or create a docker image with the necessary files. You can host the\ndocker image as a [GitHub package](\n\u003chttps://docs.github.com/en/free-pro-team@latest/packages/using-github-packages-with-your-projects-ecosystem/configuring-docker-for-use-with-github-packages\u003e\n). Then, when running SCOS Sensor, set the environment variable\n`BASE_IMAGE=\u003cimage tag\u003e`.\n\n## License\n\nSee [LICENSE](LICENSE.md).\n\n## Contact\n\nFor technical questions about SCOS Actions, contact the\n[ITS Spectrum Monitoring Team](mailto:spectrummonitoring@ntia.gov).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fntia%2Fscos-actions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fntia%2Fscos-actions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fntia%2Fscos-actions/lists"}