├── .conda ├── README.md ├── conda-forge.yml └── recipe │ ├── bld.bat │ ├── build.sh │ ├── conda_build_config.yaml │ └── meta.yaml ├── .gitattributes ├── .gitignore ├── CMakeLists.txt ├── COPYING ├── MANIFEST.yml ├── README ├── apps ├── CMakeLists.txt ├── fionna.png ├── gnuradio_logo_glyphs_as_paths.png ├── marcy.bin ├── paint_tx.grc ├── paint_tx_audio.grc ├── ramp.bin └── xhatch.bin ├── cmake ├── Modules │ ├── CMakeParseArgumentsCopy.cmake │ ├── paintConfig.cmake │ └── targetConfig.cmake.in └── cmake_uninstall.cmake.in ├── docs ├── CMakeLists.txt ├── README.paint ├── doxygen │ ├── CMakeLists.txt │ ├── Doxyfile.in │ ├── doxyxml │ │ ├── __init__.py │ │ ├── base.py │ │ ├── doxyindex.py │ │ ├── generated │ │ │ ├── __init__.py │ │ │ ├── compound.py │ │ │ ├── compoundsuper.py │ │ │ ├── index.py │ │ │ └── indexsuper.py │ │ └── text.py │ ├── other │ │ ├── group_defs.dox │ │ └── main_page.dox │ ├── pydoc_macros.h │ └── update_pydoc.py └── paintrx.png ├── examples ├── CMakeLists.txt └── README ├── grc ├── CMakeLists.txt ├── paint_image_source.block.yml └── paint_paint_bc.block.yml ├── include └── paint │ ├── CMakeLists.txt │ ├── api.h │ ├── paint_bc.h │ └── paint_config.h ├── lib ├── CMakeLists.txt ├── paint_bc_impl.cc └── paint_bc_impl.h ├── python ├── CMakeLists.txt ├── __init__.py ├── bindings │ ├── CMakeLists.txt │ ├── README.md │ ├── bind_oot_file.py │ ├── docstrings │ │ ├── README.md │ │ ├── paint_bc_pydoc_template.h │ │ └── paint_config_pydoc_template.h │ ├── header_utils.py │ ├── paint_bc_python.cc │ ├── paint_config_python.cc │ └── python_bindings.cc └── image_source.py └── tgatoluma.c /.conda/README.md: -------------------------------------------------------------------------------- 1 | # gr-paint conda recipe 2 | 3 | This recipe is for creating a package that can be installed into a [conda](https://docs.conda.io/en/latest/) environment. See the [Conda GNU Radio Guide](https://wiki.gnuradio.org/index.php/CondaInstall) for more information on using GNU Radio with conda. 4 | 5 | Packages for GNU Radio and some out-of-tree (OOT) modules are available through the [`conda-forge` channel](https://conda-forge.org/). If this OOT module is already available (search "gnuradio" on [anaconda.org](https://anaconda.org)), it is preferable to use that existing package rather than this recipe. 6 | 7 | #### Users 8 | 9 | - [Building the package](#building-the-package) 10 | 11 | #### Developers 12 | 13 | - [Modifying the recipe](#modifying-the-recipe) 14 | - [Continuous integration](#continuous-integration) 15 | 16 | 17 | ## Building the package 18 | 19 | (See the [Conda GNU Radio Guide](https://wiki.gnuradio.org/index.php/CondaInstall) if you are unfamiliar with how to use conda.) 20 | 21 | 1. Make sure that have `conda-build` and `conda-forge-pinning` installed and updated in your base environment: 22 | 23 | conda activate base 24 | conda install -n base conda-build conda-forge-pinning 25 | conda upgrade -n base conda-build conda-forge-pinning 26 | 27 | 2. Download the source code for this OOT module (which includes this recipe). Typically, this is done by using `git` and cloning the module's repository: 28 | 29 | git clone 30 | cd 31 | 32 | 3. Run `conda-build` on the recipe to create the package: 33 | 34 | (Linux and macOS) 35 | 36 | conda build .conda/recipe/ -m ${CONDA_PREFIX}/conda_build_config.yaml 37 | 38 | (Windows) 39 | 40 | conda build .conda\recipe\ -m %CONDA_PREFIX%\conda_build_config.yaml 41 | 42 | If you plan on using this package within an existing environment which uses a specific version of Python, specify the version of Python using the `--python` flag. You must use a version string that matches one of the strings listed under `python` in the `${CONDA_PREFIX}/conda_build_config.yaml` file, e.g: 43 | 44 | (Linux and macOS) 45 | 46 | conda build .conda/recipe/ -m ${CONDA_PREFIX}/conda_build_config.yaml --python="3.9.* *_cpython" 47 | 48 | (Windows) 49 | 50 | conda build .conda\recipe\ -m %CONDA_PREFIX%\conda_build_config.yaml --python="3.9.* *_cpython" 51 | 52 | If you encounter errors, consult with the OOT module maintainer or the maintainers of the [gnuradio feedstock](https://github.com/conda-forge/gnuradio-feedstock). It is possible that the recipe will need to be updated. 53 | 54 | 4. Install the package into an existing environment 55 | 56 | conda install --use-local -n gnuradio-EXAMPLE 57 | 58 | or create a new environment that includes the package: 59 | 60 | conda create -n test_env gnuradio-EXAMPLE 61 | 62 | 63 | ## Modifying the recipe 64 | 65 | This recipe is derived from a template, and so it is best to check it and make any necessary modifications. Likely changes include: 66 | 67 | - Populating metadata near the bottom of the `recipe/meta.yaml` file 68 | - Adding "host" (build-time) and "run" (run-time) dependencies specific to your module in `recipe/meta.yaml` 69 | - Adding special configuration flags or steps are necessary to carry out the build to the build scripts (`recipe/build.sh` for Linux/macOS and `recipe/bld.bat` for Windows) 70 | 71 | Specifying the versions of GNU Radio that your OOT is compatible with is one of the most important modifications. Following the instructions below, the module will be built against the conda-forge "pinned" version of GNU Radio, which is usually the latest version. 72 | 73 | - To override the pinned version of GNU Radio (e.g. for a branch that builds against an older version), specify the `gnuradio_core` key as instructed in `recipe/conda_build_config.yaml`. 74 | - If the module is compatible with multiple major versions of GNU Radio, and you want to build against multiple of them, you can also add extra versions to `recipe/conda_build_config.yaml` to expand the default build matrix. 75 | 76 | See the [conda-build documentation](https://docs.conda.io/projects/conda-build/en/latest/index.html) for details on how to write a conda recipe. 77 | 78 | 79 | ## Continuous integration 80 | 81 | Only a few steps are needed to use this recipe to build and test this OOT module using CI services. It can also be used to upload packages to [anaconda.org](https://anaconda.org) for others to download and use. 82 | 83 | 1. Make sure that have `conda-smithy` installed in your base conda environment: 84 | 85 | conda activate base 86 | conda install -n base conda-smithy 87 | conda upgrade -n base conda-smithy 88 | 89 | 2. Make any changes to the recipe and `conda-forge.yml` that are necessary. For example, if you plan on uploading packages to your own [anaconda.org](https://anaconda.org) channel, specify the channel name and label as the `channel_targets` key in `recipe/conda_build_config.yaml`. Commit the changes to your repository: 90 | 91 | git commit -a 92 | 93 | 3. "Re-render" the CI scripts by running conda-smithy from the root of your repository: 94 | 95 | conda-smithy rerender --feedstock_config .conda/conda-forge.yml -c auto 96 | 97 | This will create a commit that adds or updates the CI scripts that have been configured with `conda-forge.yml`. If you want to minimize extraneous files, you can remove some of the newly-created files that are not necessary outside of a typical conda-forge feedstock: 98 | 99 | git rm -f .github/workflows/automerge.yml .github/workflows/webservices.yml .circleci/config.yml 100 | git commit --amend -s 101 | 102 | When the CI is executed (on a pull request or commit), it will run one job per configuration file in `.ci_support` to build packages for various platforms, Python versions, and optionally `gnuradio` versions (by adding to `gnuradio_extra_pin` in `recipe/conda_build_config.yaml`). 103 | 104 | **You should repeat this step whenever the recipe is updated or when changes to the conda-forge infrastructure require all CI scripts to be updated.** 105 | 106 | Since the newly created files will be rewritten whenever conda-smithy is run, you should not edit any of the automatically-generated files in e.g. `.ci_support`, `.scripts`, or `.github/workflows/conda-build.yml`. 107 | 108 | 4. (optional) If you want to enable uploads of the packages to [anaconda.org](https://anaconda.org) whenever the CI is run from a commit on the branch specified in `conda-forge.yml`, you need to set an Anaconda Cloud API token to the `BINSTAR_TOKEN` environment variable. To generate a token, follow the instructions [here](https://docs.anaconda.com/anacondaorg/user-guide/tasks/work-with-accounts/#creating-access-tokens). To populate the `BINSTAR_TOKEN` environment variable for CI jobs, add the token as a secret by following, for example, the [Github docs](https://docs.github.com/en/actions/reference/encrypted-secrets). 109 | -------------------------------------------------------------------------------- /.conda/conda-forge.yml: -------------------------------------------------------------------------------- 1 | # See https://conda-forge.org/docs/maintainer/conda_forge_yml.html for 2 | # documentation on possible keys and values. 3 | 4 | build_platform: 5 | linux_aarch64: linux_64 6 | linux_ppc64le: linux_64 7 | osx_arm64: osx_64 8 | clone_depth: 0 9 | github_actions: 10 | store_build_artifacts: true 11 | os_version: 12 | linux_64: cos7 13 | provider: 14 | linux: github_actions 15 | osx: github_actions 16 | win: github_actions 17 | linux_aarch64: github_actions 18 | linux_ppc64le: github_actions 19 | recipe_dir: .conda/recipe 20 | test: native_and_emulated 21 | # skip unnecessary files since this is not a full-fledged conda-forge feedstock 22 | skip_render: 23 | - README.md 24 | - LICENSE.txt 25 | - .gitattributes 26 | - .gitignore 27 | - build-locally.py 28 | - LICENSE 29 | # enable uploads to Anaconda Cloud from specified branches only 30 | upload_on_branch: main 31 | -------------------------------------------------------------------------------- /.conda/recipe/bld.bat: -------------------------------------------------------------------------------- 1 | setlocal EnableDelayedExpansion 2 | @echo on 3 | 4 | :: Make a build folder and change to it 5 | cmake -E make_directory buildconda 6 | cd buildconda 7 | 8 | :: configure 9 | cmake -G "Ninja" ^ 10 | -DCMAKE_BUILD_TYPE:STRING=Release ^ 11 | -DCMAKE_INSTALL_PREFIX:PATH="%LIBRARY_PREFIX%" ^ 12 | -DCMAKE_PREFIX_PATH:PATH="%LIBRARY_PREFIX%" ^ 13 | -DGR_PYTHON_DIR:PATH="%SP_DIR%" ^ 14 | -DENABLE_DOXYGEN=OFF ^ 15 | -DENABLE_TESTING=ON ^ 16 | .. 17 | if errorlevel 1 exit 1 18 | 19 | :: build 20 | cmake --build . --config Release -- -j%CPU_COUNT% 21 | if errorlevel 1 exit 1 22 | 23 | :: install 24 | cmake --build . --config Release --target install 25 | if errorlevel 1 exit 1 26 | 27 | :: test 28 | ctest --build-config Release --output-on-failure --timeout 120 -j%CPU_COUNT% 29 | if errorlevel 1 exit 1 30 | -------------------------------------------------------------------------------- /.conda/recipe/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ex 4 | 5 | cmake -E make_directory buildconda 6 | cd buildconda 7 | 8 | cmake_config_args=( 9 | -DCMAKE_BUILD_TYPE=Release 10 | -DCMAKE_INSTALL_PREFIX=$PREFIX 11 | -DLIB_SUFFIX="" 12 | -DENABLE_DOXYGEN=OFF 13 | -DENABLE_TESTING=ON 14 | ) 15 | 16 | cmake ${CMAKE_ARGS} -G "Ninja" .. "${cmake_config_args[@]}" 17 | cmake --build . --config Release -- -j${CPU_COUNT} 18 | cmake --build . --config Release --target install 19 | 20 | if [[ "${CONDA_BUILD_CROSS_COMPILATION:-}" != "1" || "${CROSSCOMPILING_EMULATOR}" != "" ]]; then 21 | ctest --build-config Release --output-on-failure --timeout 120 -j${CPU_COUNT} 22 | fi 23 | -------------------------------------------------------------------------------- /.conda/recipe/conda_build_config.yaml: -------------------------------------------------------------------------------- 1 | # this is the channel and label where packages will be uploaded to if enabled 2 | # (see ../README.md) 3 | channel_targets: 4 | - gnuradio main 5 | # override the conda-forge pin for gnuradio-core by uncommenting 6 | # and specifying a different version here 7 | #gnuradio_core: 8 | #- "3.10.3" 9 | gnuradio_extra_pin: 10 | # always leave one entry with the empty string 11 | - "" 12 | # add version strings here like to get builds for versions other than 13 | # the conda-forge-wide default or version specified above for gnuradio_core 14 | - "3.9.8" 15 | -------------------------------------------------------------------------------- /.conda/recipe/meta.yaml: -------------------------------------------------------------------------------- 1 | {% set oot_name = "paint" %} 2 | {% set name = "gnuradio-" + oot_name %} 3 | {% set version = (environ.get("GIT_DESCRIBE_TAG_PEP440", "0.0.0." + datetime.datetime.now().strftime("%Y%m%d") + ".dev+" + environ.get("GIT_DESCRIBE_HASH", "local"))|string) %} 4 | 5 | package: 6 | name: {{ name|lower }} 7 | version: {{ version }} 8 | 9 | source: 10 | # use local path or git repository depending on if the build is local or done on CI 11 | path: "../.." # [not os.environ.get("CI")] 12 | git_url: {{ environ.get('FEEDSTOCK_ROOT', "../..") }} # [os.environ.get("CI")] 13 | 14 | build: 15 | number: 0 16 | 17 | requirements: 18 | build: 19 | - {{ compiler("c") }} 20 | - {{ compiler("cxx") }} 21 | - cmake 22 | - git 23 | - ninja 24 | - pkg-config 25 | # cross-compilation requirements 26 | - python # [build_platform != target_platform] 27 | - cross-python_{{ target_platform }} # [build_platform != target_platform] 28 | - numpy # [build_platform != target_platform] 29 | - pybind11 # [build_platform != target_platform] 30 | # Add extra build tool dependencies here 31 | 32 | host: 33 | - gmp # [linux] 34 | # the following two entries are for generating builds against specific GR versions 35 | - gnuradio-core # [not gnuradio_extra_pin] 36 | - gnuradio-core {{ gnuradio_extra_pin }}.* # [gnuradio_extra_pin] 37 | - pip # [win] 38 | - pybind11 39 | - python 40 | - numpy 41 | - volk 42 | # Add/remove library dependencies here 43 | 44 | run: 45 | - numpy 46 | - python 47 | # Add/remove runtime dependencies here 48 | - pillow 49 | 50 | test: 51 | commands: 52 | # Add a list of commands to run to check that the package works. Examples below. 53 | 54 | ## verify that commands run 55 | #- COMMAND --help 56 | 57 | # verify that (some) headers get installed 58 | - test -f $PREFIX/include/{{ oot_name }}/api.h # [not win] 59 | - if not exist %PREFIX%\\Library\\include\\{{ oot_name }}\\api.h exit 1 # [win] 60 | 61 | # verify that libraries get installed 62 | - test -f $PREFIX/lib/lib{{ name }}${SHLIB_EXT} # [not win] 63 | - if not exist %PREFIX%\\Library\\bin\\{{ name }}.dll exit 1 # [win] 64 | - if not exist %PREFIX%\\Library\\lib\\{{ name }}.lib exit 1 # [win] 65 | 66 | # verify that (some) GRC blocks get installed 67 | {% set blocks = ["paint_image_source", "paint_paint_bc"] %} 68 | {% for block in blocks %} 69 | - test -f $PREFIX/share/gnuradio/grc/blocks/{{ block }}.block.yml # [not win] 70 | - if not exist %PREFIX%\\Library\\share\\gnuradio\\grc\\blocks\\{{ block }}.block.yml exit 1 # [win] 71 | {% endfor %} 72 | 73 | imports: 74 | # verify that the python module imports 75 | - {{ oot_name }} 76 | 77 | about: 78 | # For licenses, use the SPDX identifier, e.g: "GPL-2.0-only" instead of 79 | # "GNU General Public License version 2.0". See https://spdx.org/licenses/. 80 | # Include the license text by using the license_file entry set to the path 81 | # of the license text file within the source directory, e.g. "LICENSE". 82 | # See https://docs.conda.io/projects/conda-build/en/latest/resources/define-metadata.html#license-file 83 | 84 | home: https://github.com/drmpeg/gr-paint 85 | license: GPL-3.0-or-later 86 | license_file: COPYING 87 | summary: GNU Radio module for OFDM spectrum painting 88 | description: > 89 | The goal of this project is to build a software-defined OFDM transmitter 90 | that "paints" monochrome images into the waterfall of a receiver. 91 | It is based on https://github.com/polygon/spectrum_painter. 92 | 93 | After installation, a GNU Radio block called "Spectrum Painter" will 94 | be available in the "Paint" category. The block converts a byte stream 95 | of image data into a 4K IFFT OFDM IQ sequence for transmission. 96 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # In GNU Radio 3.9+, pybind11 compares hash values of headers and source 2 | # files against knowns values. Conversion of values to CRLF on checkout 3 | # break those checks so keep LF values when files are subject to said checks 4 | # 5 | *.h text eol=lf 6 | *.c text eol=lf 7 | *.cc text eol=lf 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.pyc 3 | *.pyo 4 | build*/ 5 | examples/grc/*.py 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2011-2020 Free Software Foundation, Inc. 2 | # 3 | # This file was generated by gr_modtool, a tool from the GNU Radio framework 4 | # This file is a part of gr-paint 5 | # 6 | # SPDX-License-Identifier: GPL-3.0-or-later 7 | # 8 | 9 | ######################################################################## 10 | # Project setup 11 | ######################################################################## 12 | cmake_minimum_required(VERSION 3.8) 13 | project(gr-paint CXX C) 14 | enable_testing() 15 | 16 | # Install to PyBOMBS target prefix if defined 17 | if(DEFINED ENV{PYBOMBS_PREFIX}) 18 | set(CMAKE_INSTALL_PREFIX $ENV{PYBOMBS_PREFIX}) 19 | message(STATUS "PyBOMBS installed GNU Radio. Setting CMAKE_INSTALL_PREFIX to $ENV{PYBOMBS_PREFIX}") 20 | endif() 21 | 22 | # Select the release build type by default to get optimization flags 23 | if(NOT CMAKE_BUILD_TYPE) 24 | set(CMAKE_BUILD_TYPE "Release") 25 | message(STATUS "Build type not specified: defaulting to release.") 26 | endif(NOT CMAKE_BUILD_TYPE) 27 | set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "") 28 | 29 | # Make sure our local CMake Modules path comes first 30 | list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake/Modules) 31 | 32 | # Set the version information here 33 | set(VERSION_MAJOR 1) 34 | set(VERSION_API 0) 35 | set(VERSION_ABI 0) 36 | set(VERSION_PATCH git) 37 | 38 | cmake_policy(SET CMP0011 NEW) 39 | 40 | # Enable generation of compile_commands.json for code completion engines 41 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 42 | 43 | ######################################################################## 44 | # Compiler specific setup 45 | ######################################################################## 46 | if((CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR 47 | CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 48 | AND NOT WIN32) 49 | #http://gcc.gnu.org/wiki/Visibility 50 | add_definitions(-fvisibility=hidden) 51 | endif() 52 | 53 | IF(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 54 | SET(CMAKE_CXX_STANDARD 14) 55 | ELSEIF(CMAKE_CXX_COMPILER_ID MATCHES "Clang") 56 | SET(CMAKE_CXX_STANDARD 14) 57 | ELSEIF(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 58 | SET(CMAKE_CXX_STANDARD 14) 59 | ELSE() 60 | message(WARNING "C++ standard could not be set because compiler is not GNU, Clang or MSVC.") 61 | ENDIF() 62 | 63 | IF(CMAKE_C_COMPILER_ID STREQUAL "GNU") 64 | SET(CMAKE_C_STANDARD 11) 65 | ELSEIF(CMAKE_C_COMPILER_ID MATCHES "Clang") 66 | SET(CMAKE_C_STANDARD 11) 67 | ELSEIF(CMAKE_C_COMPILER_ID STREQUAL "MSVC") 68 | SET(CMAKE_C_STANDARD 11) 69 | ELSE() 70 | message(WARNING "C standard could not be set because compiler is not GNU, Clang or MSVC.") 71 | ENDIF() 72 | 73 | ######################################################################## 74 | # Install directories 75 | ######################################################################## 76 | include(FindPkgConfig) 77 | find_package(Gnuradio "3.9" REQUIRED) 78 | find_package(Gnuradio COMPONENTS fft) 79 | include(GrVersion) 80 | 81 | include(GrPlatform) #define LIB_SUFFIX 82 | 83 | if(NOT CMAKE_MODULES_DIR) 84 | set(CMAKE_MODULES_DIR lib${LIB_SUFFIX}/cmake) 85 | endif(NOT CMAKE_MODULES_DIR) 86 | 87 | set(GR_INCLUDE_DIR include/paint) 88 | set(GR_CMAKE_DIR ${CMAKE_MODULES_DIR}/paint) 89 | set(GR_PKG_DATA_DIR ${GR_DATA_DIR}/${CMAKE_PROJECT_NAME}) 90 | set(GR_PKG_DOC_DIR ${GR_DOC_DIR}/${CMAKE_PROJECT_NAME}) 91 | set(GR_PKG_CONF_DIR ${GR_CONF_DIR}/${CMAKE_PROJECT_NAME}/conf.d) 92 | set(GR_PKG_LIBEXEC_DIR ${GR_LIBEXEC_DIR}/${CMAKE_PROJECT_NAME}) 93 | 94 | ######################################################################## 95 | # On Apple only, set install name and use rpath correctly, if not already set 96 | ######################################################################## 97 | if(APPLE) 98 | if(NOT CMAKE_INSTALL_NAME_DIR) 99 | set(CMAKE_INSTALL_NAME_DIR 100 | ${CMAKE_INSTALL_PREFIX}/${GR_LIBRARY_DIR} CACHE 101 | PATH "Library Install Name Destination Directory" FORCE) 102 | endif(NOT CMAKE_INSTALL_NAME_DIR) 103 | if(NOT CMAKE_INSTALL_RPATH) 104 | set(CMAKE_INSTALL_RPATH 105 | ${CMAKE_INSTALL_PREFIX}/${GR_LIBRARY_DIR} CACHE 106 | PATH "Library Install RPath" FORCE) 107 | endif(NOT CMAKE_INSTALL_RPATH) 108 | if(NOT CMAKE_BUILD_WITH_INSTALL_RPATH) 109 | set(CMAKE_BUILD_WITH_INSTALL_RPATH ON CACHE 110 | BOOL "Do Build Using Library Install RPath" FORCE) 111 | endif(NOT CMAKE_BUILD_WITH_INSTALL_RPATH) 112 | endif(APPLE) 113 | 114 | ######################################################################## 115 | # Find gnuradio build dependencies 116 | ######################################################################## 117 | find_package(Doxygen) 118 | 119 | ######################################################################## 120 | # PyBind11 Related 121 | ######################################################################## 122 | 123 | find_package(pybind11 REQUIRED) 124 | execute_process( 125 | COMMAND "${PYTHON_EXECUTABLE}" -c 126 | "try:\n import numpy\n import os\n inc_path = numpy.get_include()\n if os.path.exists(os.path.join(inc_path, 'numpy', 'arrayobject.h')):\n print(inc_path, end='')\nexcept:\n pass" 127 | OUTPUT_VARIABLE PYTHON_NUMPY_INCLUDE_DIR) 128 | 129 | ######################################################################## 130 | # Setup doxygen option 131 | ######################################################################## 132 | if(DOXYGEN_FOUND) 133 | option(ENABLE_DOXYGEN "Build docs using Doxygen" ON) 134 | else(DOXYGEN_FOUND) 135 | option(ENABLE_DOXYGEN "Build docs using Doxygen" OFF) 136 | endif(DOXYGEN_FOUND) 137 | 138 | ######################################################################## 139 | # Create uninstall target 140 | ######################################################################## 141 | configure_file( 142 | ${CMAKE_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in 143 | ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake 144 | @ONLY) 145 | 146 | add_custom_target(uninstall 147 | ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake 148 | ) 149 | 150 | ######################################################################## 151 | # Add subdirectories 152 | ######################################################################## 153 | add_subdirectory(include/paint) 154 | add_subdirectory(lib) 155 | add_subdirectory(apps) 156 | add_subdirectory(examples) 157 | add_subdirectory(docs) 158 | # NOTE: manually update below to use GRC to generate C++ flowgraphs w/o python 159 | if(ENABLE_PYTHON) 160 | message(STATUS "PYTHON and GRC components are enabled") 161 | add_subdirectory(python) 162 | add_subdirectory(grc) 163 | else(ENABLE_PYTHON) 164 | message(STATUS "PYTHON and GRC components are disabled") 165 | endif(ENABLE_PYTHON) 166 | 167 | ######################################################################## 168 | # Install cmake search helper for this library 169 | ######################################################################## 170 | 171 | install(FILES cmake/Modules/paintConfig.cmake 172 | DESTINATION ${CMAKE_MODULES_DIR}/paint 173 | ) 174 | 175 | install(FILES MANIFEST.yml 176 | RENAME MANIFEST-${VERSION_MAJOR}.${VERSION_API}.${VERSION_ABI}${VERSION_PATCH}.yml 177 | DESTINATION share/gnuradio/manifests/paint) 178 | -------------------------------------------------------------------------------- /MANIFEST.yml: -------------------------------------------------------------------------------- 1 | title: The PAINT OOT Module 2 | version: 1.0 3 | brief: An OFDM spectrum painter/transmitter 4 | tags: # Tags are arbitrary, but look at CGRAN what other authors are using 5 | - OFDM 6 | - Fun 7 | author: 8 | - Ron Economos 9 | copyright_owner: 10 | - Ron Economos 11 | license: GPL-3.0-or-later 12 | gr_supported_version: 13 | - 3.7 14 | - 3.8 15 | - 3.9 16 | - 3.10 17 | repo: https://github.com/drmpeg/gr-paint 18 | website: https://github.com/drmpeg/gr-paint 19 | #icon: # Put a URL to a square image here that will be used as an icon 20 | description: |- 21 | This project implements a software-defined OFDM transmitter that "paints" monochrome images into a receiver waterfall. 22 | It is based on https://github.com/polygon/spectrum_painter 23 | file_format: 1 24 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | # Copyright 2015,2016,2021 Ron Economos 2 | # 3 | # This file is part of gr-paint 4 | # 5 | # gr-paint is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3, or (at your option) 8 | # any later version. 9 | # 10 | # gr-paint is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with gr-paint; see the file COPYING. If not, write to 17 | # the Free Software Foundation, Inc., 51 Franklin Street, 18 | # Boston, MA 02110-1301, USA. 19 | 20 | gr-paint 21 | ====== 22 | 23 | Author: Ron Economos 24 | Email: 25 | 26 | The goal of this project is to build a software-defined OFDM transmitter 27 | that "paints" monochrome images into the waterfall of a receiver. 28 | It is based on https://github.com/polygon/spectrum_painter 29 | 30 | After installation, a GNU Radio block called "Spectrum Painter" will 31 | be available in the "Paint" category. The block converts a byte stream 32 | of image data into a 4K IFFT OFDM IQ sequence for transmission. 33 | 34 | There are two block parameters. 35 | 36 | 1) Image Width. This is the horizontal size of the image to be transmitted. 37 | 38 | 2) Line Repeats. This is the number of times a line is repeated. This is 39 | used to adjust the aspect ratio of the image to be more or less correct. 40 | Aspect ratio is also affected by sample rate, waterfall update time and 41 | the size of the waterfall window. 42 | 43 | A GNU Radio Companion flow graph (paint_tx.grc) is provided in the apps 44 | directory. It transmits a test file with a UHD sink setup for an Ettus 45 | B2X0 SDR. Be sure to select the correct sink for your device. 46 | 47 | An image conversion utility (tgatoluma) is included to create image 48 | files that work with GNU Radio (raw binary). The usage is: 49 | 50 | tgatoluma 51 | 52 | For example: 53 | 54 | tgatoluma image.tga image.bin 55 | 56 | The utility will print the horizontal size, and this must be entered into 57 | the "Image Width" parameter of the block. 58 | 59 | The popular program ImageMagick can be used to convert any image to TGA 60 | format. For example: 61 | 62 | convert image.png image.tga 63 | 64 | For waterfalls that start at the top, the image needs to be flipped. 65 | 66 | convert -flip image.jpg image.tga 67 | 68 | Three test images are included. 69 | 70 | 1) marcy.bin This is a 1920x1080 frame from a cartoon. 71 | 72 | 2) ramp.bin. This is a 1920x1080 test pattern with a grayscale. Used 73 | to set the correct contrast with the minimum and maximum levels of the 74 | QT GUI Waterfall Sink. 75 | 76 | 3) xhatch.bin This is a 1920x1080 test pattern with circles. Used to set 77 | the aspect ratio correctly. 78 | 79 | A tutorial for creating high quality images with Gqrx is available here: 80 | 81 | https://gist.github.com/drmpeg/31a9a7dd6918856aeb60 82 | 83 | UPDATE: Chris Kuethe KJ6GVE has submitted an "Image File Source" block 84 | that allows any image format to be used and avoids the extra steps of 85 | using ImageMagick and tgatoluma. The flow graph has been updated to include 86 | the new block and a test PNG image is provided. You still have to enter 87 | the correct image width into the Spectrum Painter block (the Image File 88 | Source block will print the image width for you). 89 | 90 | UPDATE 11/09/2015: I've added the OFDM Cyclic Prefixer block to the test 91 | flow graph. This can be used to fine tune the aspect ratio of the image 92 | in much finer granularity than line repeats. The CP Length can be any value, 93 | it doesn't have to be a power of two like you see in regular OFDM systems. 94 | 95 | I've also added sin(x)/x correction for bladeRF and other SDR transmitters 96 | that don't compensate for DAC zero-order hold. Note that the Ettus B2X0 97 | does have sin(x)/x compensation, so it's not turned on in the test flow 98 | graph. 99 | 100 | Build instructions: 101 | 102 | mkdir build 103 | cd build 104 | cmake ../ 105 | make 106 | sudo make install 107 | sudo ldconfig 108 | 109 | Note: cmake ../ will install into the default directory /usr/local. If 110 | you've installed GNU Radio with a package manager like apt, then gr-paint 111 | should be installed in /usr. To accomplish this, use: 112 | 113 | cmake -DCMAKE_INSTALL_PREFIX=/usr ../ 114 | 115 | Contributions are welcome! 116 | 117 | -------------------------------------------------------------------------------- /apps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2011 Free Software Foundation, Inc. 2 | # 3 | # This file was generated by gr_modtool, a tool from the GNU Radio framework 4 | # This file is a part of gr-paint 5 | # 6 | # SPDX-License-Identifier: GPL-3.0-or-later 7 | # 8 | 9 | include(GrPython) 10 | 11 | GR_PYTHON_INSTALL( 12 | PROGRAMS 13 | DESTINATION bin 14 | ) 15 | -------------------------------------------------------------------------------- /apps/fionna.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drmpeg/gr-paint/38295f04bce73408aee4bb1272bc83016a4d2646/apps/fionna.png -------------------------------------------------------------------------------- /apps/gnuradio_logo_glyphs_as_paths.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drmpeg/gr-paint/38295f04bce73408aee4bb1272bc83016a4d2646/apps/gnuradio_logo_glyphs_as_paths.png -------------------------------------------------------------------------------- /apps/marcy.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drmpeg/gr-paint/38295f04bce73408aee4bb1272bc83016a4d2646/apps/marcy.bin -------------------------------------------------------------------------------- /apps/paint_tx.grc: -------------------------------------------------------------------------------- 1 | options: 2 | parameters: 3 | author: '' 4 | catch_exceptions: 'True' 5 | category: Custom 6 | cmake_opt: '' 7 | comment: '' 8 | copyright: '' 9 | description: '' 10 | gen_cmake: 'On' 11 | gen_linking: dynamic 12 | generate_options: qt_gui 13 | hier_block_src_path: '.:' 14 | id: paint_tx 15 | max_nouts: '0' 16 | output_language: python 17 | placement: (0,0) 18 | qt_qss_theme: '' 19 | realtime_scheduling: '' 20 | run: 'True' 21 | run_command: '{python} -u {filename}' 22 | run_options: prompt 23 | sizing_mode: fixed 24 | thread_safe_setters: '' 25 | title: '' 26 | states: 27 | bus_sink: false 28 | bus_source: false 29 | bus_structure: null 30 | coordinate: [8, 8] 31 | rotation: 0 32 | state: enabled 33 | 34 | blocks: 35 | - name: frequency 36 | id: variable 37 | parameters: 38 | comment: '' 39 | value: '915000000' 40 | states: 41 | bus_sink: false 42 | bus_source: false 43 | bus_structure: null 44 | coordinate: [8, 155] 45 | rotation: 0 46 | state: enabled 47 | - name: samp_rate 48 | id: variable 49 | parameters: 50 | comment: '' 51 | value: '2000000' 52 | states: 53 | bus_sink: false 54 | bus_source: false 55 | bus_structure: null 56 | coordinate: [8, 83] 57 | rotation: 0 58 | state: enabled 59 | - name: tx_gain 60 | id: variable_qtgui_range 61 | parameters: 62 | comment: '' 63 | gui_hint: '' 64 | label: '' 65 | min_len: '200' 66 | orient: QtCore.Qt.Horizontal 67 | rangeType: float 68 | start: '0' 69 | step: '0.25' 70 | stop: '89' 71 | value: '70' 72 | widget: counter_slider 73 | states: 74 | bus_sink: false 75 | bus_source: false 76 | bus_structure: null 77 | coordinate: [8, 224] 78 | rotation: 0 79 | state: enabled 80 | - name: blocks_file_source_0 81 | id: blocks_file_source 82 | parameters: 83 | affinity: '' 84 | alias: '' 85 | begin_tag: pmt.PMT_NIL 86 | comment: '' 87 | file: marcy.bin 88 | length: '0' 89 | maxoutbuf: '0' 90 | minoutbuf: '0' 91 | offset: '0' 92 | repeat: 'True' 93 | type: byte 94 | vlen: '1' 95 | states: 96 | bus_sink: false 97 | bus_source: false 98 | bus_structure: null 99 | coordinate: [160, 76.0] 100 | rotation: 0 101 | state: enabled 102 | - name: blocks_null_sink_0 103 | id: blocks_null_sink 104 | parameters: 105 | affinity: '' 106 | alias: '' 107 | bus_structure_sink: '[[0,],]' 108 | comment: '' 109 | num_inputs: '1' 110 | type: complex 111 | vlen: '1' 112 | states: 113 | bus_sink: false 114 | bus_source: false 115 | bus_structure: null 116 | coordinate: [968, 368.0] 117 | rotation: 0 118 | state: enabled 119 | - name: blocks_stream_to_vector_0 120 | id: blocks_stream_to_vector 121 | parameters: 122 | affinity: '' 123 | alias: '' 124 | comment: '' 125 | maxoutbuf: '0' 126 | minoutbuf: '0' 127 | num_items: '4096' 128 | type: complex 129 | vlen: '1' 130 | states: 131 | bus_sink: false 132 | bus_source: false 133 | bus_structure: null 134 | coordinate: [712, 112.0] 135 | rotation: 0 136 | state: enabled 137 | - name: blocks_throttle_0 138 | id: blocks_throttle 139 | parameters: 140 | affinity: '' 141 | alias: '' 142 | comment: '' 143 | ignoretag: 'True' 144 | maxoutbuf: '0' 145 | minoutbuf: '0' 146 | samples_per_second: samp_rate 147 | type: complex 148 | vlen: '1' 149 | states: 150 | bus_sink: false 151 | bus_source: false 152 | bus_structure: null 153 | coordinate: [752, 364.0] 154 | rotation: 0 155 | state: enabled 156 | - name: blocks_vector_to_stream_0 157 | id: blocks_vector_to_stream 158 | parameters: 159 | affinity: '' 160 | alias: '' 161 | comment: '' 162 | maxoutbuf: '0' 163 | minoutbuf: '0' 164 | num_items: '4096' 165 | type: complex 166 | vlen: '1' 167 | states: 168 | bus_sink: false 169 | bus_source: false 170 | bus_structure: null 171 | coordinate: [984, 208.0] 172 | rotation: 180 173 | state: enabled 174 | - name: digital_ofdm_cyclic_prefixer_0 175 | id: digital_ofdm_cyclic_prefixer 176 | parameters: 177 | affinity: '' 178 | alias: '' 179 | comment: '' 180 | cp_len: '192' 181 | input_size: '4096' 182 | maxoutbuf: '0' 183 | minoutbuf: '0' 184 | rolloff: '0' 185 | tagname: '' 186 | states: 187 | bus_sink: false 188 | bus_source: false 189 | bus_structure: null 190 | coordinate: [488, 444.0] 191 | rotation: 0 192 | state: enabled 193 | - name: fft_vxx_0 194 | id: fft_vxx 195 | parameters: 196 | affinity: '' 197 | alias: '' 198 | comment: '' 199 | fft_size: '4096' 200 | forward: 'True' 201 | maxoutbuf: '0' 202 | minoutbuf: '0' 203 | nthreads: '1' 204 | shift: 'True' 205 | type: complex 206 | window: window.rectangular(4096) 207 | states: 208 | bus_sink: false 209 | bus_source: false 210 | bus_structure: null 211 | coordinate: [984, 76.0] 212 | rotation: 0 213 | state: enabled 214 | - name: paint_image_source_0 215 | id: paint_image_source 216 | parameters: 217 | affinity: '' 218 | alias: '' 219 | autocontrast: '0' 220 | bt709_map: '1' 221 | comment: '' 222 | image_file: fionna.png 223 | image_flip: '0' 224 | image_invert: '0' 225 | maxoutbuf: '0' 226 | minoutbuf: '0' 227 | repeatmode: '1' 228 | states: 229 | bus_sink: false 230 | bus_source: false 231 | bus_structure: null 232 | coordinate: [160, 216.0] 233 | rotation: 0 234 | state: disabled 235 | - name: paint_paint_bc_0 236 | id: paint_paint_bc 237 | parameters: 238 | affinity: '' 239 | alias: '' 240 | comment: '' 241 | equalization: EQUALIZATION_OFF 242 | maxoutbuf: '0' 243 | minoutbuf: '0' 244 | randomsrc: INTERNAL 245 | repeats: '12' 246 | width: '1920' 247 | states: 248 | bus_sink: false 249 | bus_source: false 250 | bus_structure: null 251 | coordinate: [424, 92.0] 252 | rotation: 0 253 | state: enabled 254 | - name: qtgui_const_sink_x_0 255 | id: qtgui_const_sink_x 256 | parameters: 257 | affinity: '' 258 | alias: '' 259 | alpha1: '1.0' 260 | alpha10: '1.0' 261 | alpha2: '1.0' 262 | alpha3: '1.0' 263 | alpha4: '1.0' 264 | alpha5: '1.0' 265 | alpha6: '1.0' 266 | alpha7: '1.0' 267 | alpha8: '1.0' 268 | alpha9: '1.0' 269 | autoscale: 'False' 270 | axislabels: 'True' 271 | color1: '"blue"' 272 | color10: '"red"' 273 | color2: '"red"' 274 | color3: '"red"' 275 | color4: '"red"' 276 | color5: '"red"' 277 | color6: '"red"' 278 | color7: '"red"' 279 | color8: '"red"' 280 | color9: '"red"' 281 | comment: '' 282 | grid: 'False' 283 | gui_hint: '' 284 | label1: '' 285 | label10: '' 286 | label2: '' 287 | label3: '' 288 | label4: '' 289 | label5: '' 290 | label6: '' 291 | label7: '' 292 | label8: '' 293 | label9: '' 294 | legend: 'True' 295 | marker1: '0' 296 | marker10: '0' 297 | marker2: '0' 298 | marker3: '0' 299 | marker4: '0' 300 | marker5: '0' 301 | marker6: '0' 302 | marker7: '0' 303 | marker8: '0' 304 | marker9: '0' 305 | name: '""' 306 | nconnections: '1' 307 | size: '4096' 308 | style1: '0' 309 | style10: '0' 310 | style2: '0' 311 | style3: '0' 312 | style4: '0' 313 | style5: '0' 314 | style6: '0' 315 | style7: '0' 316 | style8: '0' 317 | style9: '0' 318 | tr_chan: '0' 319 | tr_level: '0.0' 320 | tr_mode: qtgui.TRIG_MODE_FREE 321 | tr_slope: qtgui.TRIG_SLOPE_POS 322 | tr_tag: '""' 323 | type: complex 324 | update_time: '0.10' 325 | width1: '1' 326 | width10: '1' 327 | width2: '1' 328 | width3: '1' 329 | width4: '1' 330 | width5: '1' 331 | width6: '1' 332 | width7: '1' 333 | width8: '1' 334 | width9: '1' 335 | xmax: '15' 336 | xmin: '-15' 337 | ymax: '15' 338 | ymin: '-15' 339 | states: 340 | bus_sink: false 341 | bus_source: false 342 | bus_structure: null 343 | coordinate: [984, 284.0] 344 | rotation: 0 345 | state: enabled 346 | - name: qtgui_waterfall_sink_x_0 347 | id: qtgui_waterfall_sink_x 348 | parameters: 349 | affinity: '' 350 | alias: '' 351 | alpha1: '1.0' 352 | alpha10: '1.0' 353 | alpha2: '1.0' 354 | alpha3: '1.0' 355 | alpha4: '1.0' 356 | alpha5: '1.0' 357 | alpha6: '1.0' 358 | alpha7: '1.0' 359 | alpha8: '1.0' 360 | alpha9: '1.0' 361 | axislabels: 'True' 362 | bw: samp_rate 363 | color1: '0' 364 | color10: '0' 365 | color2: '0' 366 | color3: '0' 367 | color4: '0' 368 | color5: '0' 369 | color6: '0' 370 | color7: '0' 371 | color8: '0' 372 | color9: '0' 373 | comment: '' 374 | fc: '0' 375 | fftsize: '4096' 376 | freqhalf: 'True' 377 | grid: 'False' 378 | gui_hint: '' 379 | int_max: '-51' 380 | int_min: '-77' 381 | label1: '' 382 | label10: '' 383 | label2: '' 384 | label3: '' 385 | label4: '' 386 | label5: '' 387 | label6: '' 388 | label7: '' 389 | label8: '' 390 | label9: '' 391 | legend: 'True' 392 | maxoutbuf: '0' 393 | minoutbuf: '0' 394 | name: '""' 395 | nconnections: '1' 396 | showports: 'True' 397 | type: complex 398 | update_time: '0.10' 399 | wintype: window.WIN_RECTANGULAR 400 | states: 401 | bus_sink: false 402 | bus_source: false 403 | bus_structure: null 404 | coordinate: [680, 204.0] 405 | rotation: 0 406 | state: true 407 | - name: uhd_usrp_sink_0 408 | id: uhd_usrp_sink 409 | parameters: 410 | affinity: '' 411 | alias: '' 412 | ant0: '' 413 | ant1: '' 414 | ant10: '' 415 | ant11: '' 416 | ant12: '' 417 | ant13: '' 418 | ant14: '' 419 | ant15: '' 420 | ant16: '' 421 | ant17: '' 422 | ant18: '' 423 | ant19: '' 424 | ant2: '' 425 | ant20: '' 426 | ant21: '' 427 | ant22: '' 428 | ant23: '' 429 | ant24: '' 430 | ant25: '' 431 | ant26: '' 432 | ant27: '' 433 | ant28: '' 434 | ant29: '' 435 | ant3: '' 436 | ant30: '' 437 | ant31: '' 438 | ant4: '' 439 | ant5: '' 440 | ant6: '' 441 | ant7: '' 442 | ant8: '' 443 | ant9: '' 444 | bw0: '0' 445 | bw1: '0' 446 | bw10: '0' 447 | bw11: '0' 448 | bw12: '0' 449 | bw13: '0' 450 | bw14: '0' 451 | bw15: '0' 452 | bw16: '0' 453 | bw17: '0' 454 | bw18: '0' 455 | bw19: '0' 456 | bw2: '0' 457 | bw20: '0' 458 | bw21: '0' 459 | bw22: '0' 460 | bw23: '0' 461 | bw24: '0' 462 | bw25: '0' 463 | bw26: '0' 464 | bw27: '0' 465 | bw28: '0' 466 | bw29: '0' 467 | bw3: '0' 468 | bw30: '0' 469 | bw31: '0' 470 | bw4: '0' 471 | bw5: '0' 472 | bw6: '0' 473 | bw7: '0' 474 | bw8: '0' 475 | bw9: '0' 476 | center_freq0: uhd.tune_request(frequency, 950000) 477 | center_freq1: '0' 478 | center_freq10: '0' 479 | center_freq11: '0' 480 | center_freq12: '0' 481 | center_freq13: '0' 482 | center_freq14: '0' 483 | center_freq15: '0' 484 | center_freq16: '0' 485 | center_freq17: '0' 486 | center_freq18: '0' 487 | center_freq19: '0' 488 | center_freq2: '0' 489 | center_freq20: '0' 490 | center_freq21: '0' 491 | center_freq22: '0' 492 | center_freq23: '0' 493 | center_freq24: '0' 494 | center_freq25: '0' 495 | center_freq26: '0' 496 | center_freq27: '0' 497 | center_freq28: '0' 498 | center_freq29: '0' 499 | center_freq3: '0' 500 | center_freq30: '0' 501 | center_freq31: '0' 502 | center_freq4: '0' 503 | center_freq5: '0' 504 | center_freq6: '0' 505 | center_freq7: '0' 506 | center_freq8: '0' 507 | center_freq9: '0' 508 | clock_rate: '0.0' 509 | clock_source0: '' 510 | clock_source1: '' 511 | clock_source2: '' 512 | clock_source3: '' 513 | clock_source4: '' 514 | clock_source5: '' 515 | clock_source6: '' 516 | clock_source7: '' 517 | comment: '' 518 | dev_addr: '"send_frame_size=8192,num_send_frames=128,master_clock_rate=" + str(samp_rate*4)' 519 | dev_args: '""' 520 | gain0: tx_gain 521 | gain1: '0' 522 | gain10: '0' 523 | gain11: '0' 524 | gain12: '0' 525 | gain13: '0' 526 | gain14: '0' 527 | gain15: '0' 528 | gain16: '0' 529 | gain17: '0' 530 | gain18: '0' 531 | gain19: '0' 532 | gain2: '0' 533 | gain20: '0' 534 | gain21: '0' 535 | gain22: '0' 536 | gain23: '0' 537 | gain24: '0' 538 | gain25: '0' 539 | gain26: '0' 540 | gain27: '0' 541 | gain28: '0' 542 | gain29: '0' 543 | gain3: '0' 544 | gain30: '0' 545 | gain31: '0' 546 | gain4: '0' 547 | gain5: '0' 548 | gain6: '0' 549 | gain7: '0' 550 | gain8: '0' 551 | gain9: '0' 552 | gain_type0: default 553 | gain_type1: default 554 | gain_type10: default 555 | gain_type11: default 556 | gain_type12: default 557 | gain_type13: default 558 | gain_type14: default 559 | gain_type15: default 560 | gain_type16: default 561 | gain_type17: default 562 | gain_type18: default 563 | gain_type19: default 564 | gain_type2: default 565 | gain_type20: default 566 | gain_type21: default 567 | gain_type22: default 568 | gain_type23: default 569 | gain_type24: default 570 | gain_type25: default 571 | gain_type26: default 572 | gain_type27: default 573 | gain_type28: default 574 | gain_type29: default 575 | gain_type3: default 576 | gain_type30: default 577 | gain_type31: default 578 | gain_type4: default 579 | gain_type5: default 580 | gain_type6: default 581 | gain_type7: default 582 | gain_type8: default 583 | gain_type9: default 584 | len_tag_name: '' 585 | lo_export0: 'False' 586 | lo_export1: 'False' 587 | lo_export10: 'False' 588 | lo_export11: 'False' 589 | lo_export12: 'False' 590 | lo_export13: 'False' 591 | lo_export14: 'False' 592 | lo_export15: 'False' 593 | lo_export16: 'False' 594 | lo_export17: 'False' 595 | lo_export18: 'False' 596 | lo_export19: 'False' 597 | lo_export2: 'False' 598 | lo_export20: 'False' 599 | lo_export21: 'False' 600 | lo_export22: 'False' 601 | lo_export23: 'False' 602 | lo_export24: 'False' 603 | lo_export25: 'False' 604 | lo_export26: 'False' 605 | lo_export27: 'False' 606 | lo_export28: 'False' 607 | lo_export29: 'False' 608 | lo_export3: 'False' 609 | lo_export30: 'False' 610 | lo_export31: 'False' 611 | lo_export4: 'False' 612 | lo_export5: 'False' 613 | lo_export6: 'False' 614 | lo_export7: 'False' 615 | lo_export8: 'False' 616 | lo_export9: 'False' 617 | lo_source0: internal 618 | lo_source1: internal 619 | lo_source10: internal 620 | lo_source11: internal 621 | lo_source12: internal 622 | lo_source13: internal 623 | lo_source14: internal 624 | lo_source15: internal 625 | lo_source16: internal 626 | lo_source17: internal 627 | lo_source18: internal 628 | lo_source19: internal 629 | lo_source2: internal 630 | lo_source20: internal 631 | lo_source21: internal 632 | lo_source22: internal 633 | lo_source23: internal 634 | lo_source24: internal 635 | lo_source25: internal 636 | lo_source26: internal 637 | lo_source27: internal 638 | lo_source28: internal 639 | lo_source29: internal 640 | lo_source3: internal 641 | lo_source30: internal 642 | lo_source31: internal 643 | lo_source4: internal 644 | lo_source5: internal 645 | lo_source6: internal 646 | lo_source7: internal 647 | lo_source8: internal 648 | lo_source9: internal 649 | maxoutbuf: '0' 650 | minoutbuf: '0' 651 | nchan: '1' 652 | num_mboards: '1' 653 | otw: '' 654 | samp_rate: samp_rate 655 | sd_spec0: '' 656 | sd_spec1: '' 657 | sd_spec2: '' 658 | sd_spec3: '' 659 | sd_spec4: '' 660 | sd_spec5: '' 661 | sd_spec6: '' 662 | sd_spec7: '' 663 | show_lo_controls: 'False' 664 | start_time: '-1.0' 665 | stream_args: '' 666 | stream_chans: '[]' 667 | sync: none 668 | time_source0: '' 669 | time_source1: '' 670 | time_source2: '' 671 | time_source3: '' 672 | time_source4: '' 673 | time_source5: '' 674 | time_source6: '' 675 | time_source7: '' 676 | type: fc32 677 | states: 678 | bus_sink: false 679 | bus_source: false 680 | bus_structure: null 681 | coordinate: [968, 408.0] 682 | rotation: 0 683 | state: disabled 684 | - name: virtual_sink_0 685 | id: virtual_sink 686 | parameters: 687 | alias: '' 688 | comment: '' 689 | stream_id: paint-cyclic 690 | states: 691 | bus_sink: false 692 | bus_source: false 693 | bus_structure: null 694 | coordinate: [984, 20.0] 695 | rotation: 0 696 | state: true 697 | - name: virtual_source_0 698 | id: virtual_source 699 | parameters: 700 | alias: '' 701 | comment: '' 702 | stream_id: paint-cyclic 703 | states: 704 | bus_sink: false 705 | bus_source: false 706 | bus_structure: null 707 | coordinate: [168, 460.0] 708 | rotation: 0 709 | state: true 710 | 711 | connections: 712 | - [blocks_file_source_0, '0', paint_paint_bc_0, '0'] 713 | - [blocks_stream_to_vector_0, '0', fft_vxx_0, '0'] 714 | - [blocks_stream_to_vector_0, '0', virtual_sink_0, '0'] 715 | - [blocks_throttle_0, '0', blocks_null_sink_0, '0'] 716 | - [blocks_vector_to_stream_0, '0', qtgui_const_sink_x_0, '0'] 717 | - [digital_ofdm_cyclic_prefixer_0, '0', blocks_throttle_0, '0'] 718 | - [digital_ofdm_cyclic_prefixer_0, '0', uhd_usrp_sink_0, '0'] 719 | - [fft_vxx_0, '0', blocks_vector_to_stream_0, '0'] 720 | - [paint_image_source_0, '0', paint_paint_bc_0, '0'] 721 | - [paint_paint_bc_0, '0', blocks_stream_to_vector_0, '0'] 722 | - [paint_paint_bc_0, '0', qtgui_waterfall_sink_x_0, '0'] 723 | - [virtual_source_0, '0', digital_ofdm_cyclic_prefixer_0, '0'] 724 | 725 | metadata: 726 | file_format: 1 727 | -------------------------------------------------------------------------------- /apps/paint_tx_audio.grc: -------------------------------------------------------------------------------- 1 | options: 2 | parameters: 3 | author: '' 4 | catch_exceptions: 'True' 5 | category: Custom 6 | cmake_opt: '' 7 | comment: '' 8 | copyright: '' 9 | description: '' 10 | gen_cmake: 'On' 11 | gen_linking: dynamic 12 | generate_options: qt_gui 13 | hier_block_src_path: '.:' 14 | id: paint_tx_audio 15 | max_nouts: '0' 16 | output_language: python 17 | placement: (0,0) 18 | qt_qss_theme: '' 19 | realtime_scheduling: '' 20 | run: 'True' 21 | run_command: '{python} -u {filename}' 22 | run_options: prompt 23 | sizing_mode: fixed 24 | thread_safe_setters: '' 25 | title: '' 26 | window_size: 1280, 1024 27 | states: 28 | bus_sink: false 29 | bus_source: false 30 | bus_structure: null 31 | coordinate: [8, 8] 32 | rotation: 0 33 | state: enabled 34 | 35 | blocks: 36 | - name: frequency 37 | id: variable 38 | parameters: 39 | comment: '' 40 | value: '915000000' 41 | states: 42 | bus_sink: false 43 | bus_source: false 44 | bus_structure: null 45 | coordinate: [8, 155] 46 | rotation: 0 47 | state: enabled 48 | - name: samp_rate 49 | id: variable 50 | parameters: 51 | comment: '' 52 | value: int(48e3) 53 | states: 54 | bus_sink: false 55 | bus_source: false 56 | bus_structure: null 57 | coordinate: [8, 83] 58 | rotation: 0 59 | state: enabled 60 | - name: analog_sig_source_x_0 61 | id: analog_sig_source_x 62 | parameters: 63 | affinity: '' 64 | alias: '' 65 | amp: '1' 66 | comment: '' 67 | freq: samp_rate/4.0 68 | maxoutbuf: '0' 69 | minoutbuf: '0' 70 | offset: '0' 71 | phase: '0' 72 | samp_rate: samp_rate 73 | type: complex 74 | waveform: analog.GR_COS_WAVE 75 | states: 76 | bus_sink: false 77 | bus_source: false 78 | bus_structure: null 79 | coordinate: [448, 196.0] 80 | rotation: 180 81 | state: enabled 82 | - name: audio_sink_0 83 | id: audio_sink 84 | parameters: 85 | affinity: '' 86 | alias: '' 87 | comment: '' 88 | device_name: '' 89 | num_inputs: '1' 90 | ok_to_block: 'True' 91 | samp_rate: samp_rate 92 | states: 93 | bus_sink: false 94 | bus_source: false 95 | bus_structure: null 96 | coordinate: [992, 428.0] 97 | rotation: 0 98 | state: enabled 99 | - name: blocks_complex_to_real_0 100 | id: blocks_complex_to_real 101 | parameters: 102 | affinity: '' 103 | alias: '' 104 | comment: '' 105 | maxoutbuf: '0' 106 | minoutbuf: '0' 107 | vlen: '1' 108 | states: 109 | bus_sink: false 110 | bus_source: false 111 | bus_structure: null 112 | coordinate: [664, 360.0] 113 | rotation: 0 114 | state: enabled 115 | - name: blocks_multiply_xx_0 116 | id: blocks_multiply_xx 117 | parameters: 118 | affinity: '' 119 | alias: '' 120 | comment: '' 121 | maxoutbuf: '0' 122 | minoutbuf: '0' 123 | num_inputs: '2' 124 | type: complex 125 | vlen: '1' 126 | states: 127 | bus_sink: false 128 | bus_source: false 129 | bus_structure: null 130 | coordinate: [448, 344.0] 131 | rotation: 0 132 | state: enabled 133 | - name: paint_image_source_0 134 | id: paint_image_source 135 | parameters: 136 | affinity: '' 137 | alias: '' 138 | autocontrast: '0' 139 | bt709_map: '1' 140 | comment: '' 141 | image_file: gnuradio_logo_glyphs_as_paths.png 142 | image_flip: '1' 143 | image_invert: '0' 144 | maxoutbuf: '0' 145 | minoutbuf: '0' 146 | repeatmode: '1' 147 | states: 148 | bus_sink: false 149 | bus_source: false 150 | bus_structure: null 151 | coordinate: [448, 36.0] 152 | rotation: 180 153 | state: enabled 154 | - name: paint_paint_bc_0 155 | id: paint_paint_bc 156 | parameters: 157 | affinity: '' 158 | alias: '' 159 | comment: '' 160 | equalization: EQUALIZATION_OFF 161 | maxoutbuf: '0' 162 | minoutbuf: '0' 163 | randomsrc: INTERNAL 164 | repeats: '1' 165 | width: '288' 166 | states: 167 | bus_sink: false 168 | bus_source: false 169 | bus_structure: null 170 | coordinate: [136, 220.0] 171 | rotation: 180 172 | state: enabled 173 | - name: qtgui_time_sink_x_0 174 | id: qtgui_time_sink_x 175 | parameters: 176 | affinity: '' 177 | alias: '' 178 | alpha1: '1.0' 179 | alpha10: '1.0' 180 | alpha2: '1.0' 181 | alpha3: '1.0' 182 | alpha4: '1.0' 183 | alpha5: '1.0' 184 | alpha6: '1.0' 185 | alpha7: '1.0' 186 | alpha8: '1.0' 187 | alpha9: '1.0' 188 | autoscale: 'False' 189 | axislabels: 'True' 190 | color1: blue 191 | color10: dark blue 192 | color2: red 193 | color3: green 194 | color4: black 195 | color5: cyan 196 | color6: magenta 197 | color7: yellow 198 | color8: dark red 199 | color9: dark green 200 | comment: '' 201 | ctrlpanel: 'False' 202 | entags: 'True' 203 | grid: 'False' 204 | gui_hint: '' 205 | label1: '' 206 | label10: '' 207 | label2: '' 208 | label3: '' 209 | label4: '' 210 | label5: '' 211 | label6: '' 212 | label7: '' 213 | label8: '' 214 | label9: '' 215 | legend: 'True' 216 | marker1: '-1' 217 | marker10: '-1' 218 | marker2: '-1' 219 | marker3: '-1' 220 | marker4: '-1' 221 | marker5: '-1' 222 | marker6: '-1' 223 | marker7: '-1' 224 | marker8: '-1' 225 | marker9: '-1' 226 | name: '""' 227 | nconnections: '1' 228 | size: '1024' 229 | srate: samp_rate 230 | stemplot: 'False' 231 | style1: '1' 232 | style10: '1' 233 | style2: '1' 234 | style3: '1' 235 | style4: '1' 236 | style5: '1' 237 | style6: '1' 238 | style7: '1' 239 | style8: '1' 240 | style9: '1' 241 | tr_chan: '0' 242 | tr_delay: '0' 243 | tr_level: '0.0' 244 | tr_mode: qtgui.TRIG_MODE_FREE 245 | tr_slope: qtgui.TRIG_SLOPE_POS 246 | tr_tag: '""' 247 | type: float 248 | update_time: '0.10' 249 | width1: '1' 250 | width10: '1' 251 | width2: '1' 252 | width3: '1' 253 | width4: '1' 254 | width5: '1' 255 | width6: '1' 256 | width7: '1' 257 | width8: '1' 258 | width9: '1' 259 | ylabel: Amplitude 260 | ymax: '1' 261 | ymin: '-1' 262 | yunit: '""' 263 | states: 264 | bus_sink: false 265 | bus_source: false 266 | bus_structure: null 267 | coordinate: [992, 220.0] 268 | rotation: 0 269 | state: enabled 270 | - name: qtgui_waterfall_sink_x_0_0 271 | id: qtgui_waterfall_sink_x 272 | parameters: 273 | affinity: '' 274 | alias: '' 275 | alpha1: '1.0' 276 | alpha10: '1.0' 277 | alpha2: '1.0' 278 | alpha3: '1.0' 279 | alpha4: '1.0' 280 | alpha5: '1.0' 281 | alpha6: '1.0' 282 | alpha7: '1.0' 283 | alpha8: '1.0' 284 | alpha9: '1.0' 285 | axislabels: 'True' 286 | bw: samp_rate 287 | color1: '0' 288 | color10: '0' 289 | color2: '0' 290 | color3: '0' 291 | color4: '0' 292 | color5: '0' 293 | color6: '0' 294 | color7: '0' 295 | color8: '0' 296 | color9: '0' 297 | comment: '' 298 | fc: frequency 299 | fftsize: '4096' 300 | freqhalf: 'True' 301 | grid: 'False' 302 | gui_hint: '' 303 | int_max: '-51' 304 | int_min: '-77' 305 | label1: '' 306 | label10: '' 307 | label2: '' 308 | label3: '' 309 | label4: '' 310 | label5: '' 311 | label6: '' 312 | label7: '' 313 | label8: '' 314 | label9: '' 315 | legend: 'True' 316 | maxoutbuf: '0' 317 | minoutbuf: '0' 318 | name: '""' 319 | nconnections: '1' 320 | showports: 'True' 321 | type: float 322 | update_time: '0.1' 323 | wintype: window.WIN_RECTANGULAR 324 | states: 325 | bus_sink: false 326 | bus_source: false 327 | bus_structure: null 328 | coordinate: [992, 312.0] 329 | rotation: 0 330 | state: enabled 331 | - name: rational_resampler_xxx_0 332 | id: rational_resampler_xxx 333 | parameters: 334 | affinity: '' 335 | alias: '' 336 | comment: '' 337 | decim: '1' 338 | fbw: '0' 339 | interp: '2' 340 | maxoutbuf: '0' 341 | minoutbuf: '0' 342 | taps: '' 343 | type: ccc 344 | states: 345 | bus_sink: false 346 | bus_source: false 347 | bus_structure: null 348 | coordinate: [136, 348.0] 349 | rotation: 0 350 | state: enabled 351 | 352 | connections: 353 | - [analog_sig_source_x_0, '0', blocks_multiply_xx_0, '0'] 354 | - [blocks_complex_to_real_0, '0', audio_sink_0, '0'] 355 | - [blocks_complex_to_real_0, '0', qtgui_time_sink_x_0, '0'] 356 | - [blocks_complex_to_real_0, '0', qtgui_waterfall_sink_x_0_0, '0'] 357 | - [blocks_multiply_xx_0, '0', blocks_complex_to_real_0, '0'] 358 | - [paint_image_source_0, '0', paint_paint_bc_0, '0'] 359 | - [paint_paint_bc_0, '0', rational_resampler_xxx_0, '0'] 360 | - [rational_resampler_xxx_0, '0', blocks_multiply_xx_0, '1'] 361 | 362 | metadata: 363 | file_format: 1 364 | -------------------------------------------------------------------------------- /apps/ramp.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drmpeg/gr-paint/38295f04bce73408aee4bb1272bc83016a4d2646/apps/ramp.bin -------------------------------------------------------------------------------- /apps/xhatch.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drmpeg/gr-paint/38295f04bce73408aee4bb1272bc83016a4d2646/apps/xhatch.bin -------------------------------------------------------------------------------- /cmake/Modules/CMakeParseArgumentsCopy.cmake: -------------------------------------------------------------------------------- 1 | # CMAKE_PARSE_ARGUMENTS( args...) 2 | # 3 | # CMAKE_PARSE_ARGUMENTS() is intended to be used in macros or functions for 4 | # parsing the arguments given to that macro or function. 5 | # It processes the arguments and defines a set of variables which hold the 6 | # values of the respective options. 7 | # 8 | # The argument contains all options for the respective macro, 9 | # i.e. keywords which can be used when calling the macro without any value 10 | # following, like e.g. the OPTIONAL keyword of the install() command. 11 | # 12 | # The argument contains all keywords for this macro 13 | # which are followed by one value, like e.g. DESTINATION keyword of the 14 | # install() command. 15 | # 16 | # The argument contains all keywords for this macro 17 | # which can be followed by more than one value, like e.g. the TARGETS or 18 | # FILES keywords of the install() command. 19 | # 20 | # When done, CMAKE_PARSE_ARGUMENTS() will have defined for each of the 21 | # keywords listed in , and 22 | # a variable composed of the given 23 | # followed by "_" and the name of the respective keyword. 24 | # These variables will then hold the respective value from the argument list. 25 | # For the keywords this will be TRUE or FALSE. 26 | # 27 | # All remaining arguments are collected in a variable 28 | # _UNPARSED_ARGUMENTS, this can be checked afterwards to see whether 29 | # your macro was called with unrecognized parameters. 30 | # 31 | # As an example here a my_install() macro, which takes similar arguments as the 32 | # real install() command: 33 | # 34 | # function(MY_INSTALL) 35 | # set(options OPTIONAL FAST) 36 | # set(oneValueArgs DESTINATION RENAME) 37 | # set(multiValueArgs TARGETS CONFIGURATIONS) 38 | # cmake_parse_arguments(MY_INSTALL "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) 39 | # ... 40 | # 41 | # Assume my_install() has been called like this: 42 | # my_install(TARGETS foo bar DESTINATION bin OPTIONAL blub) 43 | # 44 | # After the cmake_parse_arguments() call the macro will have set the following 45 | # variables: 46 | # MY_INSTALL_OPTIONAL = TRUE 47 | # MY_INSTALL_FAST = FALSE (this option was not used when calling my_install() 48 | # MY_INSTALL_DESTINATION = "bin" 49 | # MY_INSTALL_RENAME = "" (was not used) 50 | # MY_INSTALL_TARGETS = "foo;bar" 51 | # MY_INSTALL_CONFIGURATIONS = "" (was not used) 52 | # MY_INSTALL_UNPARSED_ARGUMENTS = "blub" (no value expected after "OPTIONAL" 53 | # 54 | # You can the continue and process these variables. 55 | # 56 | # Keywords terminate lists of values, e.g. if directly after a one_value_keyword 57 | # another recognized keyword follows, this is interpreted as the beginning of 58 | # the new option. 59 | # E.g. my_install(TARGETS foo DESTINATION OPTIONAL) would result in 60 | # MY_INSTALL_DESTINATION set to "OPTIONAL", but MY_INSTALL_DESTINATION would 61 | # be empty and MY_INSTALL_OPTIONAL would be set to TRUE therefore. 62 | 63 | #============================================================================= 64 | # Copyright 2010 Alexander Neundorf 65 | # 66 | # Distributed under the OSI-approved BSD License (the "License"); 67 | # see accompanying file Copyright.txt for details. 68 | # 69 | # This software is distributed WITHOUT ANY WARRANTY; without even the 70 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 71 | # See the License for more information. 72 | #============================================================================= 73 | # (To distribute this file outside of CMake, substitute the full 74 | # License text for the above reference.) 75 | 76 | 77 | if(__CMAKE_PARSE_ARGUMENTS_INCLUDED) 78 | return() 79 | endif() 80 | set(__CMAKE_PARSE_ARGUMENTS_INCLUDED TRUE) 81 | 82 | 83 | function(CMAKE_PARSE_ARGUMENTS prefix _optionNames _singleArgNames _multiArgNames) 84 | # first set all result variables to empty/FALSE 85 | foreach(arg_name ${_singleArgNames} ${_multiArgNames}) 86 | set(${prefix}_${arg_name}) 87 | endforeach(arg_name) 88 | 89 | foreach(option ${_optionNames}) 90 | set(${prefix}_${option} FALSE) 91 | endforeach(option) 92 | 93 | set(${prefix}_UNPARSED_ARGUMENTS) 94 | 95 | set(insideValues FALSE) 96 | set(currentArgName) 97 | 98 | # now iterate over all arguments and fill the result variables 99 | foreach(currentArg ${ARGN}) 100 | list(FIND _optionNames "${currentArg}" optionIndex) # ... then this marks the end of the arguments belonging to this keyword 101 | list(FIND _singleArgNames "${currentArg}" singleArgIndex) # ... then this marks the end of the arguments belonging to this keyword 102 | list(FIND _multiArgNames "${currentArg}" multiArgIndex) # ... then this marks the end of the arguments belonging to this keyword 103 | 104 | if(${optionIndex} EQUAL -1 AND ${singleArgIndex} EQUAL -1 AND ${multiArgIndex} EQUAL -1) 105 | if(insideValues) 106 | if("${insideValues}" STREQUAL "SINGLE") 107 | set(${prefix}_${currentArgName} ${currentArg}) 108 | set(insideValues FALSE) 109 | elseif("${insideValues}" STREQUAL "MULTI") 110 | list(APPEND ${prefix}_${currentArgName} ${currentArg}) 111 | endif() 112 | else(insideValues) 113 | list(APPEND ${prefix}_UNPARSED_ARGUMENTS ${currentArg}) 114 | endif(insideValues) 115 | else() 116 | if(NOT ${optionIndex} EQUAL -1) 117 | set(${prefix}_${currentArg} TRUE) 118 | set(insideValues FALSE) 119 | elseif(NOT ${singleArgIndex} EQUAL -1) 120 | set(currentArgName ${currentArg}) 121 | set(${prefix}_${currentArgName}) 122 | set(insideValues "SINGLE") 123 | elseif(NOT ${multiArgIndex} EQUAL -1) 124 | set(currentArgName ${currentArg}) 125 | set(${prefix}_${currentArgName}) 126 | set(insideValues "MULTI") 127 | endif() 128 | endif() 129 | 130 | endforeach(currentArg) 131 | 132 | # propagate the result variables to the caller: 133 | foreach(arg_name ${_singleArgNames} ${_multiArgNames} ${_optionNames}) 134 | set(${prefix}_${arg_name} ${${prefix}_${arg_name}} PARENT_SCOPE) 135 | endforeach(arg_name) 136 | set(${prefix}_UNPARSED_ARGUMENTS ${${prefix}_UNPARSED_ARGUMENTS} PARENT_SCOPE) 137 | 138 | endfunction(CMAKE_PARSE_ARGUMENTS _options _singleArgs _multiArgs) 139 | -------------------------------------------------------------------------------- /cmake/Modules/paintConfig.cmake: -------------------------------------------------------------------------------- 1 | INCLUDE(FindPkgConfig) 2 | PKG_CHECK_MODULES(PC_PAINT paint) 3 | 4 | FIND_PATH( 5 | PAINT_INCLUDE_DIRS 6 | NAMES paint/api.h 7 | HINTS $ENV{PAINT_DIR}/include 8 | ${PC_PAINT_INCLUDEDIR} 9 | PATHS ${CMAKE_INSTALL_PREFIX}/include 10 | /usr/local/include 11 | /usr/include 12 | ) 13 | 14 | FIND_LIBRARY( 15 | PAINT_LIBRARIES 16 | NAMES gnuradio-paint 17 | HINTS $ENV{PAINT_DIR}/lib 18 | ${PC_PAINT_LIBDIR} 19 | PATHS ${CMAKE_INSTALL_PREFIX}/lib 20 | ${CMAKE_INSTALL_PREFIX}/lib64 21 | /usr/local/lib 22 | /usr/local/lib64 23 | /usr/lib 24 | /usr/lib64 25 | ) 26 | 27 | include("${CMAKE_CURRENT_LIST_DIR}/paintTarget.cmake") 28 | 29 | INCLUDE(FindPackageHandleStandardArgs) 30 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(PAINT DEFAULT_MSG PAINT_LIBRARIES PAINT_INCLUDE_DIRS) 31 | MARK_AS_ADVANCED(PAINT_LIBRARIES PAINT_INCLUDE_DIRS) 32 | -------------------------------------------------------------------------------- /cmake/Modules/targetConfig.cmake.in: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Free Software Foundation, Inc. 2 | # 3 | # This file is part of GNU Radio 4 | # 5 | # SPDX-License-Identifier: GPL-3.0-or-later 6 | # 7 | 8 | include(CMakeFindDependencyMacro) 9 | 10 | set(target_deps "@TARGET_DEPENDENCIES@") 11 | foreach(dep IN LISTS target_deps) 12 | find_dependency(${dep}) 13 | endforeach() 14 | include("${CMAKE_CURRENT_LIST_DIR}/@TARGET@Targets.cmake") 15 | -------------------------------------------------------------------------------- /cmake/cmake_uninstall.cmake.in: -------------------------------------------------------------------------------- 1 | # http://www.vtk.org/Wiki/CMake_FAQ#Can_I_do_.22make_uninstall.22_with_CMake.3F 2 | 3 | IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 4 | MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") 5 | ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 6 | 7 | FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) 8 | STRING(REGEX REPLACE "\n" ";" files "${files}") 9 | FOREACH(file ${files}) 10 | MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") 11 | IF(EXISTS "$ENV{DESTDIR}${file}") 12 | EXEC_PROGRAM( 13 | "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" 14 | OUTPUT_VARIABLE rm_out 15 | RETURN_VALUE rm_retval 16 | ) 17 | IF(NOT "${rm_retval}" STREQUAL 0) 18 | MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") 19 | ENDIF(NOT "${rm_retval}" STREQUAL 0) 20 | ELSEIF(IS_SYMLINK "$ENV{DESTDIR}${file}") 21 | EXEC_PROGRAM( 22 | "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" 23 | OUTPUT_VARIABLE rm_out 24 | RETURN_VALUE rm_retval 25 | ) 26 | IF(NOT "${rm_retval}" STREQUAL 0) 27 | MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") 28 | ENDIF(NOT "${rm_retval}" STREQUAL 0) 29 | ELSE(EXISTS "$ENV{DESTDIR}${file}") 30 | MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") 31 | ENDIF(EXISTS "$ENV{DESTDIR}${file}") 32 | ENDFOREACH(file) 33 | -------------------------------------------------------------------------------- /docs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2011 Free Software Foundation, Inc. 2 | # 3 | # This file was generated by gr_modtool, a tool from the GNU Radio framework 4 | # This file is a part of gr-paint 5 | # 6 | # SPDX-License-Identifier: GPL-3.0-or-later 7 | # 8 | 9 | ######################################################################## 10 | # Setup dependencies 11 | ######################################################################## 12 | find_package(Doxygen) 13 | 14 | ######################################################################## 15 | # Begin conditional configuration 16 | ######################################################################## 17 | if(ENABLE_DOXYGEN) 18 | 19 | ######################################################################## 20 | # Add subdirectories 21 | ######################################################################## 22 | add_subdirectory(doxygen) 23 | 24 | endif(ENABLE_DOXYGEN) 25 | -------------------------------------------------------------------------------- /docs/README.paint: -------------------------------------------------------------------------------- 1 | This is the paint-write-a-block package meant as a guide to building 2 | out-of-tree packages. To use the paint blocks, the Python namespaces 3 | is in 'paint', which is imported as: 4 | 5 | import paint 6 | 7 | See the Doxygen documentation for details about the blocks available 8 | in this package. A quick listing of the details can be found in Python 9 | after importing by using: 10 | 11 | help(paint) 12 | -------------------------------------------------------------------------------- /docs/doxygen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2011 Free Software Foundation, Inc. 2 | # 3 | # This file was generated by gr_modtool, a tool from the GNU Radio framework 4 | # This file is a part of gr-paint 5 | # 6 | # SPDX-License-Identifier: GPL-3.0-or-later 7 | # 8 | 9 | ######################################################################## 10 | # Create the doxygen configuration file 11 | ######################################################################## 12 | file(TO_NATIVE_PATH ${CMAKE_SOURCE_DIR} top_srcdir) 13 | file(TO_NATIVE_PATH ${CMAKE_BINARY_DIR} top_builddir) 14 | file(TO_NATIVE_PATH ${CMAKE_SOURCE_DIR} abs_top_srcdir) 15 | file(TO_NATIVE_PATH ${CMAKE_BINARY_DIR} abs_top_builddir) 16 | 17 | set(HAVE_DOT ${DOXYGEN_DOT_FOUND}) 18 | set(enable_html_docs YES) 19 | set(enable_latex_docs NO) 20 | set(enable_mathjax NO) 21 | set(enable_xml_docs YES) 22 | 23 | configure_file( 24 | ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in 25 | ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile 26 | @ONLY) 27 | 28 | set(BUILT_DIRS ${CMAKE_CURRENT_BINARY_DIR}/xml ${CMAKE_CURRENT_BINARY_DIR}/html) 29 | 30 | ######################################################################## 31 | # Make and install doxygen docs 32 | ######################################################################## 33 | add_custom_command( 34 | OUTPUT ${BUILT_DIRS} 35 | COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile 36 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 37 | COMMENT "Generating documentation with doxygen" 38 | ) 39 | 40 | add_custom_target(doxygen_target ALL DEPENDS ${BUILT_DIRS}) 41 | 42 | install(DIRECTORY ${BUILT_DIRS} DESTINATION ${GR_PKG_DOC_DIR}) 43 | -------------------------------------------------------------------------------- /docs/doxygen/doxyxml/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2010 Free Software Foundation, Inc. 3 | # 4 | # This file was generated by gr_modtool, a tool from the GNU Radio framework 5 | # This file is a part of gr-paint 6 | # 7 | # SPDX-License-Identifier: GPL-3.0-or-later 8 | # 9 | # 10 | """ 11 | Python interface to contents of doxygen xml documentation. 12 | 13 | Example use: 14 | See the contents of the example folder for the C++ and 15 | doxygen-generated xml used in this example. 16 | 17 | >>> # Parse the doxygen docs. 18 | >>> import os 19 | >>> this_dir = os.path.dirname(globals()['__file__']) 20 | >>> xml_path = this_dir + "/example/xml/" 21 | >>> di = DoxyIndex(xml_path) 22 | 23 | Get a list of all top-level objects. 24 | 25 | >>> print([mem.name() for mem in di.members()]) 26 | [u'Aadvark', u'aadvarky_enough', u'main'] 27 | 28 | Get all functions. 29 | 30 | >>> print([mem.name() for mem in di.in_category(DoxyFunction)]) 31 | [u'aadvarky_enough', u'main'] 32 | 33 | Check if an object is present. 34 | 35 | >>> di.has_member(u'Aadvark') 36 | True 37 | >>> di.has_member(u'Fish') 38 | False 39 | 40 | Get an item by name and check its properties. 41 | 42 | >>> aad = di.get_member(u'Aadvark') 43 | >>> print(aad.brief_description) 44 | Models the mammal Aadvark. 45 | >>> print(aad.detailed_description) 46 | Sadly the model is incomplete and cannot capture all aspects of an aadvark yet. 47 | 48 | This line is uninformative and is only to test line breaks in the comments. 49 | >>> [mem.name() for mem in aad.members()] 50 | [u'aadvarkness', u'print', u'Aadvark', u'get_aadvarkness'] 51 | >>> aad.get_member(u'print').brief_description 52 | u'Outputs the vital aadvark statistics.' 53 | 54 | """ 55 | 56 | from .doxyindex import DoxyIndex, DoxyFunction, DoxyParam, DoxyClass, DoxyFile, DoxyNamespace, DoxyGroup, DoxyFriend, DoxyOther 57 | 58 | def _test(): 59 | import os 60 | this_dir = os.path.dirname(globals()['__file__']) 61 | xml_path = this_dir + "/example/xml/" 62 | di = DoxyIndex(xml_path) 63 | # Get the Aadvark class 64 | aad = di.get_member('Aadvark') 65 | aad.brief_description 66 | import doctest 67 | return doctest.testmod() 68 | 69 | if __name__ == "__main__": 70 | _test() 71 | 72 | -------------------------------------------------------------------------------- /docs/doxygen/doxyxml/base.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2010 Free Software Foundation, Inc. 3 | # 4 | # This file was generated by gr_modtool, a tool from the GNU Radio framework 5 | # This file is a part of gr-paint 6 | # 7 | # SPDX-License-Identifier: GPL-3.0-or-later 8 | # 9 | # 10 | """ 11 | A base class is created. 12 | 13 | Classes based upon this are used to make more user-friendly interfaces 14 | to the doxygen xml docs than the generated classes provide. 15 | """ 16 | 17 | import os 18 | import pdb 19 | 20 | from xml.parsers.expat import ExpatError 21 | 22 | from .generated import compound 23 | 24 | 25 | class Base(object): 26 | 27 | class Duplicate(Exception): 28 | pass 29 | 30 | class NoSuchMember(Exception): 31 | pass 32 | 33 | class ParsingError(Exception): 34 | pass 35 | 36 | def __init__(self, parse_data, top=None): 37 | self._parsed = False 38 | self._error = False 39 | self._parse_data = parse_data 40 | self._members = [] 41 | self._dict_members = {} 42 | self._in_category = {} 43 | self._data = {} 44 | if top is not None: 45 | self._xml_path = top._xml_path 46 | # Set up holder of references 47 | else: 48 | top = self 49 | self._refs = {} 50 | self._xml_path = parse_data 51 | self.top = top 52 | 53 | @classmethod 54 | def from_refid(cls, refid, top=None): 55 | """ Instantiate class from a refid rather than parsing object. """ 56 | # First check to see if its already been instantiated. 57 | if top is not None and refid in top._refs: 58 | return top._refs[refid] 59 | # Otherwise create a new instance and set refid. 60 | inst = cls(None, top=top) 61 | inst.refid = refid 62 | inst.add_ref(inst) 63 | return inst 64 | 65 | @classmethod 66 | def from_parse_data(cls, parse_data, top=None): 67 | refid = getattr(parse_data, 'refid', None) 68 | if refid is not None and top is not None and refid in top._refs: 69 | return top._refs[refid] 70 | inst = cls(parse_data, top=top) 71 | if refid is not None: 72 | inst.refid = refid 73 | inst.add_ref(inst) 74 | return inst 75 | 76 | def add_ref(self, obj): 77 | if hasattr(obj, 'refid'): 78 | self.top._refs[obj.refid] = obj 79 | 80 | mem_classes = [] 81 | 82 | def get_cls(self, mem): 83 | for cls in self.mem_classes: 84 | if cls.can_parse(mem): 85 | return cls 86 | raise Exception(("Did not find a class for object '%s'." \ 87 | % (mem.get_name()))) 88 | 89 | def convert_mem(self, mem): 90 | try: 91 | cls = self.get_cls(mem) 92 | converted = cls.from_parse_data(mem, self.top) 93 | if converted is None: 94 | raise Exception('No class matched this object.') 95 | self.add_ref(converted) 96 | return converted 97 | except Exception as e: 98 | print(e) 99 | 100 | @classmethod 101 | def includes(cls, inst): 102 | return isinstance(inst, cls) 103 | 104 | @classmethod 105 | def can_parse(cls, obj): 106 | return False 107 | 108 | def _parse(self): 109 | self._parsed = True 110 | 111 | def _get_dict_members(self, cat=None): 112 | """ 113 | For given category a dictionary is returned mapping member names to 114 | members of that category. For names that are duplicated the name is 115 | mapped to None. 116 | """ 117 | self.confirm_no_error() 118 | if cat not in self._dict_members: 119 | new_dict = {} 120 | for mem in self.in_category(cat): 121 | if mem.name() not in new_dict: 122 | new_dict[mem.name()] = mem 123 | else: 124 | new_dict[mem.name()] = self.Duplicate 125 | self._dict_members[cat] = new_dict 126 | return self._dict_members[cat] 127 | 128 | def in_category(self, cat): 129 | self.confirm_no_error() 130 | if cat is None: 131 | return self._members 132 | if cat not in self._in_category: 133 | self._in_category[cat] = [mem for mem in self._members 134 | if cat.includes(mem)] 135 | return self._in_category[cat] 136 | 137 | def get_member(self, name, cat=None): 138 | self.confirm_no_error() 139 | # Check if it's in a namespace or class. 140 | bits = name.split('::') 141 | first = bits[0] 142 | rest = '::'.join(bits[1:]) 143 | member = self._get_dict_members(cat).get(first, self.NoSuchMember) 144 | # Raise any errors that are returned. 145 | if member in set([self.NoSuchMember, self.Duplicate]): 146 | raise member() 147 | if rest: 148 | return member.get_member(rest, cat=cat) 149 | return member 150 | 151 | def has_member(self, name, cat=None): 152 | try: 153 | mem = self.get_member(name, cat=cat) 154 | return True 155 | except self.NoSuchMember: 156 | return False 157 | 158 | def data(self): 159 | self.confirm_no_error() 160 | return self._data 161 | 162 | def members(self): 163 | self.confirm_no_error() 164 | return self._members 165 | 166 | def process_memberdefs(self): 167 | mdtss = [] 168 | for sec in self._retrieved_data.compounddef.sectiondef: 169 | mdtss += sec.memberdef 170 | # At the moment we lose all information associated with sections. 171 | # Sometimes a memberdef is in several sectiondef. 172 | # We make sure we don't get duplicates here. 173 | uniques = set([]) 174 | for mem in mdtss: 175 | converted = self.convert_mem(mem) 176 | pair = (mem.name, mem.__class__) 177 | if pair not in uniques: 178 | uniques.add(pair) 179 | self._members.append(converted) 180 | 181 | def retrieve_data(self): 182 | filename = os.path.join(self._xml_path, self.refid + '.xml') 183 | try: 184 | self._retrieved_data = compound.parse(filename) 185 | except ExpatError: 186 | print('Error in xml in file %s' % filename) 187 | self._error = True 188 | self._retrieved_data = None 189 | 190 | def check_parsed(self): 191 | if not self._parsed: 192 | self._parse() 193 | 194 | def confirm_no_error(self): 195 | self.check_parsed() 196 | if self._error: 197 | raise self.ParsingError() 198 | 199 | def error(self): 200 | self.check_parsed() 201 | return self._error 202 | 203 | def name(self): 204 | # first see if we can do it without processing. 205 | if self._parse_data is not None: 206 | return self._parse_data.name 207 | self.check_parsed() 208 | return self._retrieved_data.compounddef.name 209 | -------------------------------------------------------------------------------- /docs/doxygen/doxyxml/doxyindex.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2010 Free Software Foundation, Inc. 3 | # 4 | # This file was generated by gr_modtool, a tool from the GNU Radio framework 5 | # This file is a part of gr-paint 6 | # 7 | # SPDX-License-Identifier: GPL-3.0-or-later 8 | # 9 | # 10 | """ 11 | Classes providing more user-friendly interfaces to the doxygen xml 12 | docs than the generated classes provide. 13 | """ 14 | 15 | import os 16 | 17 | from .generated import index 18 | from .base import Base 19 | from .text import description 20 | 21 | class DoxyIndex(Base): 22 | """ 23 | Parses a doxygen xml directory. 24 | """ 25 | 26 | __module__ = "gnuradio.utils.doxyxml" 27 | 28 | def _parse(self): 29 | if self._parsed: 30 | return 31 | super(DoxyIndex, self)._parse() 32 | self._root = index.parse(os.path.join(self._xml_path, 'index.xml')) 33 | for mem in self._root.compound: 34 | converted = self.convert_mem(mem) 35 | # For files and namespaces we want the contents to be 36 | # accessible directly from the parent rather than having 37 | # to go through the file object. 38 | if self.get_cls(mem) == DoxyFile: 39 | if mem.name.endswith('.h'): 40 | self._members += converted.members() 41 | self._members.append(converted) 42 | elif self.get_cls(mem) == DoxyNamespace: 43 | self._members += converted.members() 44 | self._members.append(converted) 45 | else: 46 | self._members.append(converted) 47 | 48 | 49 | class DoxyCompMem(Base): 50 | 51 | 52 | kind = None 53 | 54 | def __init__(self, *args, **kwargs): 55 | super(DoxyCompMem, self).__init__(*args, **kwargs) 56 | 57 | @classmethod 58 | def can_parse(cls, obj): 59 | return obj.kind == cls.kind 60 | 61 | def set_descriptions(self, parse_data): 62 | bd = description(getattr(parse_data, 'briefdescription', None)) 63 | dd = description(getattr(parse_data, 'detaileddescription', None)) 64 | self._data['brief_description'] = bd 65 | self._data['detailed_description'] = dd 66 | 67 | def set_parameters(self, data): 68 | vs = [ddc.value for ddc in data.detaileddescription.content_] 69 | pls = [] 70 | for v in vs: 71 | if hasattr(v, 'parameterlist'): 72 | pls += v.parameterlist 73 | pis = [] 74 | for pl in pls: 75 | pis += pl.parameteritem 76 | dpis = [] 77 | for pi in pis: 78 | dpi = DoxyParameterItem(pi) 79 | dpi._parse() 80 | dpis.append(dpi) 81 | self._data['params'] = dpis 82 | 83 | 84 | class DoxyCompound(DoxyCompMem): 85 | pass 86 | 87 | class DoxyMember(DoxyCompMem): 88 | pass 89 | 90 | class DoxyFunction(DoxyMember): 91 | 92 | __module__ = "gnuradio.utils.doxyxml" 93 | 94 | kind = 'function' 95 | 96 | def _parse(self): 97 | if self._parsed: 98 | return 99 | super(DoxyFunction, self)._parse() 100 | self.set_descriptions(self._parse_data) 101 | self.set_parameters(self._parse_data) 102 | if not self._data['params']: 103 | # If the params weren't set by a comment then just grab the names. 104 | self._data['params'] = [] 105 | prms = self._parse_data.param 106 | for prm in prms: 107 | self._data['params'].append(DoxyParam(prm)) 108 | 109 | brief_description = property(lambda self: self.data()['brief_description']) 110 | detailed_description = property(lambda self: self.data()['detailed_description']) 111 | params = property(lambda self: self.data()['params']) 112 | 113 | Base.mem_classes.append(DoxyFunction) 114 | 115 | 116 | class DoxyParam(DoxyMember): 117 | 118 | __module__ = "gnuradio.utils.doxyxml" 119 | 120 | def _parse(self): 121 | if self._parsed: 122 | return 123 | super(DoxyParam, self)._parse() 124 | self.set_descriptions(self._parse_data) 125 | self._data['declname'] = self._parse_data.declname 126 | 127 | @property 128 | def description(self): 129 | descriptions = [] 130 | if self.brief_description: 131 | descriptions.append(self.brief_description) 132 | if self.detailed_description: 133 | descriptions.append(self.detailed_description) 134 | return '\n\n'.join(descriptions) 135 | 136 | brief_description = property(lambda self: self.data()['brief_description']) 137 | detailed_description = property(lambda self: self.data()['detailed_description']) 138 | name = property(lambda self: self.data()['declname']) 139 | 140 | class DoxyParameterItem(DoxyMember): 141 | """A different representation of a parameter in Doxygen.""" 142 | 143 | def _parse(self): 144 | if self._parsed: 145 | return 146 | super(DoxyParameterItem, self)._parse() 147 | names = [] 148 | for nl in self._parse_data.parameternamelist: 149 | for pn in nl.parametername: 150 | names.append(description(pn)) 151 | # Just take first name 152 | self._data['name'] = names[0] 153 | # Get description 154 | pd = description(self._parse_data.get_parameterdescription()) 155 | self._data['description'] = pd 156 | 157 | description = property(lambda self: self.data()['description']) 158 | name = property(lambda self: self.data()['name']) 159 | 160 | 161 | class DoxyClass(DoxyCompound): 162 | 163 | __module__ = "gnuradio.utils.doxyxml" 164 | 165 | kind = 'class' 166 | 167 | def _parse(self): 168 | if self._parsed: 169 | return 170 | super(DoxyClass, self)._parse() 171 | self.retrieve_data() 172 | if self._error: 173 | return 174 | self.set_descriptions(self._retrieved_data.compounddef) 175 | self.set_parameters(self._retrieved_data.compounddef) 176 | # Sectiondef.kind tells about whether private or public. 177 | # We just ignore this for now. 178 | self.process_memberdefs() 179 | 180 | brief_description = property(lambda self: self.data()['brief_description']) 181 | detailed_description = property(lambda self: self.data()['detailed_description']) 182 | params = property(lambda self: self.data()['params']) 183 | 184 | Base.mem_classes.append(DoxyClass) 185 | 186 | 187 | class DoxyFile(DoxyCompound): 188 | 189 | __module__ = "gnuradio.utils.doxyxml" 190 | 191 | kind = 'file' 192 | 193 | def _parse(self): 194 | if self._parsed: 195 | return 196 | super(DoxyFile, self)._parse() 197 | self.retrieve_data() 198 | self.set_descriptions(self._retrieved_data.compounddef) 199 | if self._error: 200 | return 201 | self.process_memberdefs() 202 | 203 | brief_description = property(lambda self: self.data()['brief_description']) 204 | detailed_description = property(lambda self: self.data()['detailed_description']) 205 | 206 | Base.mem_classes.append(DoxyFile) 207 | 208 | 209 | class DoxyNamespace(DoxyCompound): 210 | 211 | __module__ = "gnuradio.utils.doxyxml" 212 | 213 | kind = 'namespace' 214 | 215 | def _parse(self): 216 | if self._parsed: 217 | return 218 | super(DoxyNamespace, self)._parse() 219 | self.retrieve_data() 220 | self.set_descriptions(self._retrieved_data.compounddef) 221 | if self._error: 222 | return 223 | self.process_memberdefs() 224 | 225 | Base.mem_classes.append(DoxyNamespace) 226 | 227 | 228 | class DoxyGroup(DoxyCompound): 229 | 230 | __module__ = "gnuradio.utils.doxyxml" 231 | 232 | kind = 'group' 233 | 234 | def _parse(self): 235 | if self._parsed: 236 | return 237 | super(DoxyGroup, self)._parse() 238 | self.retrieve_data() 239 | if self._error: 240 | return 241 | cdef = self._retrieved_data.compounddef 242 | self._data['title'] = description(cdef.title) 243 | # Process inner groups 244 | grps = cdef.innergroup 245 | for grp in grps: 246 | converted = DoxyGroup.from_refid(grp.refid, top=self.top) 247 | self._members.append(converted) 248 | # Process inner classes 249 | klasses = cdef.innerclass 250 | for kls in klasses: 251 | converted = DoxyClass.from_refid(kls.refid, top=self.top) 252 | self._members.append(converted) 253 | # Process normal members 254 | self.process_memberdefs() 255 | 256 | title = property(lambda self: self.data()['title']) 257 | 258 | 259 | Base.mem_classes.append(DoxyGroup) 260 | 261 | 262 | class DoxyFriend(DoxyMember): 263 | 264 | __module__ = "gnuradio.utils.doxyxml" 265 | 266 | kind = 'friend' 267 | 268 | Base.mem_classes.append(DoxyFriend) 269 | 270 | 271 | class DoxyOther(Base): 272 | 273 | __module__ = "gnuradio.utils.doxyxml" 274 | 275 | kinds = set(['variable', 'struct', 'union', 'define', 'typedef', 'enum', 276 | 'dir', 'page', 'signal', 'slot', 'property']) 277 | 278 | @classmethod 279 | def can_parse(cls, obj): 280 | return obj.kind in cls.kinds 281 | 282 | Base.mem_classes.append(DoxyOther) 283 | -------------------------------------------------------------------------------- /docs/doxygen/doxyxml/generated/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains generated files produced by generateDS.py. 3 | 4 | These do the real work of parsing the doxygen xml files but the 5 | resultant classes are not very friendly to navigate so the rest of the 6 | doxyxml module processes them further. 7 | """ 8 | -------------------------------------------------------------------------------- /docs/doxygen/doxyxml/generated/compound.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Generated Mon Feb 9 19:08:05 2009 by generateDS.py. 5 | """ 6 | 7 | 8 | from xml.dom import minidom 9 | from xml.dom import Node 10 | 11 | import sys 12 | 13 | from . import compoundsuper as supermod 14 | from .compoundsuper import MixedContainer 15 | 16 | 17 | class DoxygenTypeSub(supermod.DoxygenType): 18 | def __init__(self, version=None, compounddef=None): 19 | supermod.DoxygenType.__init__(self, version, compounddef) 20 | 21 | def find(self, details): 22 | 23 | return self.compounddef.find(details) 24 | 25 | supermod.DoxygenType.subclass = DoxygenTypeSub 26 | # end class DoxygenTypeSub 27 | 28 | 29 | class compounddefTypeSub(supermod.compounddefType): 30 | def __init__(self, kind=None, prot=None, id=None, compoundname='', title='', basecompoundref=None, derivedcompoundref=None, includes=None, includedby=None, incdepgraph=None, invincdepgraph=None, innerdir=None, innerfile=None, innerclass=None, innernamespace=None, innerpage=None, innergroup=None, templateparamlist=None, sectiondef=None, briefdescription=None, detaileddescription=None, inheritancegraph=None, collaborationgraph=None, programlisting=None, location=None, listofallmembers=None): 31 | supermod.compounddefType.__init__(self, kind, prot, id, compoundname, title, basecompoundref, derivedcompoundref, includes, includedby, incdepgraph, invincdepgraph, innerdir, innerfile, innerclass, innernamespace, innerpage, innergroup, templateparamlist, sectiondef, briefdescription, detaileddescription, inheritancegraph, collaborationgraph, programlisting, location, listofallmembers) 32 | 33 | def find(self, details): 34 | 35 | if self.id == details.refid: 36 | return self 37 | 38 | for sectiondef in self.sectiondef: 39 | result = sectiondef.find(details) 40 | if result: 41 | return result 42 | 43 | 44 | supermod.compounddefType.subclass = compounddefTypeSub 45 | # end class compounddefTypeSub 46 | 47 | 48 | class listofallmembersTypeSub(supermod.listofallmembersType): 49 | def __init__(self, member=None): 50 | supermod.listofallmembersType.__init__(self, member) 51 | supermod.listofallmembersType.subclass = listofallmembersTypeSub 52 | # end class listofallmembersTypeSub 53 | 54 | 55 | class memberRefTypeSub(supermod.memberRefType): 56 | def __init__(self, virt=None, prot=None, refid=None, ambiguityscope=None, scope='', name=''): 57 | supermod.memberRefType.__init__(self, virt, prot, refid, ambiguityscope, scope, name) 58 | supermod.memberRefType.subclass = memberRefTypeSub 59 | # end class memberRefTypeSub 60 | 61 | 62 | class compoundRefTypeSub(supermod.compoundRefType): 63 | def __init__(self, virt=None, prot=None, refid=None, valueOf_='', mixedclass_=None, content_=None): 64 | supermod.compoundRefType.__init__(self, mixedclass_, content_) 65 | supermod.compoundRefType.subclass = compoundRefTypeSub 66 | # end class compoundRefTypeSub 67 | 68 | 69 | class reimplementTypeSub(supermod.reimplementType): 70 | def __init__(self, refid=None, valueOf_='', mixedclass_=None, content_=None): 71 | supermod.reimplementType.__init__(self, mixedclass_, content_) 72 | supermod.reimplementType.subclass = reimplementTypeSub 73 | # end class reimplementTypeSub 74 | 75 | 76 | class incTypeSub(supermod.incType): 77 | def __init__(self, local=None, refid=None, valueOf_='', mixedclass_=None, content_=None): 78 | supermod.incType.__init__(self, mixedclass_, content_) 79 | supermod.incType.subclass = incTypeSub 80 | # end class incTypeSub 81 | 82 | 83 | class refTypeSub(supermod.refType): 84 | def __init__(self, prot=None, refid=None, valueOf_='', mixedclass_=None, content_=None): 85 | supermod.refType.__init__(self, mixedclass_, content_) 86 | supermod.refType.subclass = refTypeSub 87 | # end class refTypeSub 88 | 89 | 90 | 91 | class refTextTypeSub(supermod.refTextType): 92 | def __init__(self, refid=None, kindref=None, external=None, valueOf_='', mixedclass_=None, content_=None): 93 | supermod.refTextType.__init__(self, mixedclass_, content_) 94 | 95 | supermod.refTextType.subclass = refTextTypeSub 96 | # end class refTextTypeSub 97 | 98 | class sectiondefTypeSub(supermod.sectiondefType): 99 | 100 | 101 | def __init__(self, kind=None, header='', description=None, memberdef=None): 102 | supermod.sectiondefType.__init__(self, kind, header, description, memberdef) 103 | 104 | def find(self, details): 105 | 106 | for memberdef in self.memberdef: 107 | if memberdef.id == details.refid: 108 | return memberdef 109 | 110 | return None 111 | 112 | 113 | supermod.sectiondefType.subclass = sectiondefTypeSub 114 | # end class sectiondefTypeSub 115 | 116 | 117 | class memberdefTypeSub(supermod.memberdefType): 118 | def __init__(self, initonly=None, kind=None, volatile=None, const=None, raise_=None, virt=None, readable=None, prot=None, explicit=None, new=None, final=None, writable=None, add=None, static=None, remove=None, sealed=None, mutable=None, gettable=None, inline=None, settable=None, id=None, templateparamlist=None, type_=None, definition='', argsstring='', name='', read='', write='', bitfield='', reimplements=None, reimplementedby=None, param=None, enumvalue=None, initializer=None, exceptions=None, briefdescription=None, detaileddescription=None, inbodydescription=None, location=None, references=None, referencedby=None): 119 | supermod.memberdefType.__init__(self, initonly, kind, volatile, const, raise_, virt, readable, prot, explicit, new, final, writable, add, static, remove, sealed, mutable, gettable, inline, settable, id, templateparamlist, type_, definition, argsstring, name, read, write, bitfield, reimplements, reimplementedby, param, enumvalue, initializer, exceptions, briefdescription, detaileddescription, inbodydescription, location, references, referencedby) 120 | supermod.memberdefType.subclass = memberdefTypeSub 121 | # end class memberdefTypeSub 122 | 123 | 124 | class descriptionTypeSub(supermod.descriptionType): 125 | def __init__(self, title='', para=None, sect1=None, internal=None, mixedclass_=None, content_=None): 126 | supermod.descriptionType.__init__(self, mixedclass_, content_) 127 | supermod.descriptionType.subclass = descriptionTypeSub 128 | # end class descriptionTypeSub 129 | 130 | 131 | class enumvalueTypeSub(supermod.enumvalueType): 132 | def __init__(self, prot=None, id=None, name='', initializer=None, briefdescription=None, detaileddescription=None, mixedclass_=None, content_=None): 133 | supermod.enumvalueType.__init__(self, mixedclass_, content_) 134 | supermod.enumvalueType.subclass = enumvalueTypeSub 135 | # end class enumvalueTypeSub 136 | 137 | 138 | class templateparamlistTypeSub(supermod.templateparamlistType): 139 | def __init__(self, param=None): 140 | supermod.templateparamlistType.__init__(self, param) 141 | supermod.templateparamlistType.subclass = templateparamlistTypeSub 142 | # end class templateparamlistTypeSub 143 | 144 | 145 | class paramTypeSub(supermod.paramType): 146 | def __init__(self, type_=None, declname='', defname='', array='', defval=None, briefdescription=None): 147 | supermod.paramType.__init__(self, type_, declname, defname, array, defval, briefdescription) 148 | supermod.paramType.subclass = paramTypeSub 149 | # end class paramTypeSub 150 | 151 | 152 | class linkedTextTypeSub(supermod.linkedTextType): 153 | def __init__(self, ref=None, mixedclass_=None, content_=None): 154 | supermod.linkedTextType.__init__(self, mixedclass_, content_) 155 | supermod.linkedTextType.subclass = linkedTextTypeSub 156 | # end class linkedTextTypeSub 157 | 158 | 159 | class graphTypeSub(supermod.graphType): 160 | def __init__(self, node=None): 161 | supermod.graphType.__init__(self, node) 162 | supermod.graphType.subclass = graphTypeSub 163 | # end class graphTypeSub 164 | 165 | 166 | class nodeTypeSub(supermod.nodeType): 167 | def __init__(self, id=None, label='', link=None, childnode=None): 168 | supermod.nodeType.__init__(self, id, label, link, childnode) 169 | supermod.nodeType.subclass = nodeTypeSub 170 | # end class nodeTypeSub 171 | 172 | 173 | class childnodeTypeSub(supermod.childnodeType): 174 | def __init__(self, relation=None, refid=None, edgelabel=None): 175 | supermod.childnodeType.__init__(self, relation, refid, edgelabel) 176 | supermod.childnodeType.subclass = childnodeTypeSub 177 | # end class childnodeTypeSub 178 | 179 | 180 | class linkTypeSub(supermod.linkType): 181 | def __init__(self, refid=None, external=None, valueOf_=''): 182 | supermod.linkType.__init__(self, refid, external) 183 | supermod.linkType.subclass = linkTypeSub 184 | # end class linkTypeSub 185 | 186 | 187 | class listingTypeSub(supermod.listingType): 188 | def __init__(self, codeline=None): 189 | supermod.listingType.__init__(self, codeline) 190 | supermod.listingType.subclass = listingTypeSub 191 | # end class listingTypeSub 192 | 193 | 194 | class codelineTypeSub(supermod.codelineType): 195 | def __init__(self, external=None, lineno=None, refkind=None, refid=None, highlight=None): 196 | supermod.codelineType.__init__(self, external, lineno, refkind, refid, highlight) 197 | supermod.codelineType.subclass = codelineTypeSub 198 | # end class codelineTypeSub 199 | 200 | 201 | class highlightTypeSub(supermod.highlightType): 202 | def __init__(self, class_=None, sp=None, ref=None, mixedclass_=None, content_=None): 203 | supermod.highlightType.__init__(self, mixedclass_, content_) 204 | supermod.highlightType.subclass = highlightTypeSub 205 | # end class highlightTypeSub 206 | 207 | 208 | class referenceTypeSub(supermod.referenceType): 209 | def __init__(self, endline=None, startline=None, refid=None, compoundref=None, valueOf_='', mixedclass_=None, content_=None): 210 | supermod.referenceType.__init__(self, mixedclass_, content_) 211 | supermod.referenceType.subclass = referenceTypeSub 212 | # end class referenceTypeSub 213 | 214 | 215 | class locationTypeSub(supermod.locationType): 216 | def __init__(self, bodystart=None, line=None, bodyend=None, bodyfile=None, file=None, valueOf_=''): 217 | supermod.locationType.__init__(self, bodystart, line, bodyend, bodyfile, file) 218 | supermod.locationType.subclass = locationTypeSub 219 | # end class locationTypeSub 220 | 221 | 222 | class docSect1TypeSub(supermod.docSect1Type): 223 | def __init__(self, id=None, title='', para=None, sect2=None, internal=None, mixedclass_=None, content_=None): 224 | supermod.docSect1Type.__init__(self, mixedclass_, content_) 225 | supermod.docSect1Type.subclass = docSect1TypeSub 226 | # end class docSect1TypeSub 227 | 228 | 229 | class docSect2TypeSub(supermod.docSect2Type): 230 | def __init__(self, id=None, title='', para=None, sect3=None, internal=None, mixedclass_=None, content_=None): 231 | supermod.docSect2Type.__init__(self, mixedclass_, content_) 232 | supermod.docSect2Type.subclass = docSect2TypeSub 233 | # end class docSect2TypeSub 234 | 235 | 236 | class docSect3TypeSub(supermod.docSect3Type): 237 | def __init__(self, id=None, title='', para=None, sect4=None, internal=None, mixedclass_=None, content_=None): 238 | supermod.docSect3Type.__init__(self, mixedclass_, content_) 239 | supermod.docSect3Type.subclass = docSect3TypeSub 240 | # end class docSect3TypeSub 241 | 242 | 243 | class docSect4TypeSub(supermod.docSect4Type): 244 | def __init__(self, id=None, title='', para=None, internal=None, mixedclass_=None, content_=None): 245 | supermod.docSect4Type.__init__(self, mixedclass_, content_) 246 | supermod.docSect4Type.subclass = docSect4TypeSub 247 | # end class docSect4TypeSub 248 | 249 | 250 | class docInternalTypeSub(supermod.docInternalType): 251 | def __init__(self, para=None, sect1=None, mixedclass_=None, content_=None): 252 | supermod.docInternalType.__init__(self, mixedclass_, content_) 253 | supermod.docInternalType.subclass = docInternalTypeSub 254 | # end class docInternalTypeSub 255 | 256 | 257 | class docInternalS1TypeSub(supermod.docInternalS1Type): 258 | def __init__(self, para=None, sect2=None, mixedclass_=None, content_=None): 259 | supermod.docInternalS1Type.__init__(self, mixedclass_, content_) 260 | supermod.docInternalS1Type.subclass = docInternalS1TypeSub 261 | # end class docInternalS1TypeSub 262 | 263 | 264 | class docInternalS2TypeSub(supermod.docInternalS2Type): 265 | def __init__(self, para=None, sect3=None, mixedclass_=None, content_=None): 266 | supermod.docInternalS2Type.__init__(self, mixedclass_, content_) 267 | supermod.docInternalS2Type.subclass = docInternalS2TypeSub 268 | # end class docInternalS2TypeSub 269 | 270 | 271 | class docInternalS3TypeSub(supermod.docInternalS3Type): 272 | def __init__(self, para=None, sect3=None, mixedclass_=None, content_=None): 273 | supermod.docInternalS3Type.__init__(self, mixedclass_, content_) 274 | supermod.docInternalS3Type.subclass = docInternalS3TypeSub 275 | # end class docInternalS3TypeSub 276 | 277 | 278 | class docInternalS4TypeSub(supermod.docInternalS4Type): 279 | def __init__(self, para=None, mixedclass_=None, content_=None): 280 | supermod.docInternalS4Type.__init__(self, mixedclass_, content_) 281 | supermod.docInternalS4Type.subclass = docInternalS4TypeSub 282 | # end class docInternalS4TypeSub 283 | 284 | 285 | class docURLLinkSub(supermod.docURLLink): 286 | def __init__(self, url=None, valueOf_='', mixedclass_=None, content_=None): 287 | supermod.docURLLink.__init__(self, mixedclass_, content_) 288 | supermod.docURLLink.subclass = docURLLinkSub 289 | # end class docURLLinkSub 290 | 291 | 292 | class docAnchorTypeSub(supermod.docAnchorType): 293 | def __init__(self, id=None, valueOf_='', mixedclass_=None, content_=None): 294 | supermod.docAnchorType.__init__(self, mixedclass_, content_) 295 | supermod.docAnchorType.subclass = docAnchorTypeSub 296 | # end class docAnchorTypeSub 297 | 298 | 299 | class docFormulaTypeSub(supermod.docFormulaType): 300 | def __init__(self, id=None, valueOf_='', mixedclass_=None, content_=None): 301 | supermod.docFormulaType.__init__(self, mixedclass_, content_) 302 | supermod.docFormulaType.subclass = docFormulaTypeSub 303 | # end class docFormulaTypeSub 304 | 305 | 306 | class docIndexEntryTypeSub(supermod.docIndexEntryType): 307 | def __init__(self, primaryie='', secondaryie=''): 308 | supermod.docIndexEntryType.__init__(self, primaryie, secondaryie) 309 | supermod.docIndexEntryType.subclass = docIndexEntryTypeSub 310 | # end class docIndexEntryTypeSub 311 | 312 | 313 | class docListTypeSub(supermod.docListType): 314 | def __init__(self, listitem=None): 315 | supermod.docListType.__init__(self, listitem) 316 | supermod.docListType.subclass = docListTypeSub 317 | # end class docListTypeSub 318 | 319 | 320 | class docListItemTypeSub(supermod.docListItemType): 321 | def __init__(self, para=None): 322 | supermod.docListItemType.__init__(self, para) 323 | supermod.docListItemType.subclass = docListItemTypeSub 324 | # end class docListItemTypeSub 325 | 326 | 327 | class docSimpleSectTypeSub(supermod.docSimpleSectType): 328 | def __init__(self, kind=None, title=None, para=None): 329 | supermod.docSimpleSectType.__init__(self, kind, title, para) 330 | supermod.docSimpleSectType.subclass = docSimpleSectTypeSub 331 | # end class docSimpleSectTypeSub 332 | 333 | 334 | class docVarListEntryTypeSub(supermod.docVarListEntryType): 335 | def __init__(self, term=None): 336 | supermod.docVarListEntryType.__init__(self, term) 337 | supermod.docVarListEntryType.subclass = docVarListEntryTypeSub 338 | # end class docVarListEntryTypeSub 339 | 340 | 341 | class docRefTextTypeSub(supermod.docRefTextType): 342 | def __init__(self, refid=None, kindref=None, external=None, valueOf_='', mixedclass_=None, content_=None): 343 | supermod.docRefTextType.__init__(self, mixedclass_, content_) 344 | supermod.docRefTextType.subclass = docRefTextTypeSub 345 | # end class docRefTextTypeSub 346 | 347 | 348 | class docTableTypeSub(supermod.docTableType): 349 | def __init__(self, rows=None, cols=None, row=None, caption=None): 350 | supermod.docTableType.__init__(self, rows, cols, row, caption) 351 | supermod.docTableType.subclass = docTableTypeSub 352 | # end class docTableTypeSub 353 | 354 | 355 | class docRowTypeSub(supermod.docRowType): 356 | def __init__(self, entry=None): 357 | supermod.docRowType.__init__(self, entry) 358 | supermod.docRowType.subclass = docRowTypeSub 359 | # end class docRowTypeSub 360 | 361 | 362 | class docEntryTypeSub(supermod.docEntryType): 363 | def __init__(self, thead=None, para=None): 364 | supermod.docEntryType.__init__(self, thead, para) 365 | supermod.docEntryType.subclass = docEntryTypeSub 366 | # end class docEntryTypeSub 367 | 368 | 369 | class docHeadingTypeSub(supermod.docHeadingType): 370 | def __init__(self, level=None, valueOf_='', mixedclass_=None, content_=None): 371 | supermod.docHeadingType.__init__(self, mixedclass_, content_) 372 | supermod.docHeadingType.subclass = docHeadingTypeSub 373 | # end class docHeadingTypeSub 374 | 375 | 376 | class docImageTypeSub(supermod.docImageType): 377 | def __init__(self, width=None, type_=None, name=None, height=None, valueOf_='', mixedclass_=None, content_=None): 378 | supermod.docImageType.__init__(self, mixedclass_, content_) 379 | supermod.docImageType.subclass = docImageTypeSub 380 | # end class docImageTypeSub 381 | 382 | 383 | class docDotFileTypeSub(supermod.docDotFileType): 384 | def __init__(self, name=None, valueOf_='', mixedclass_=None, content_=None): 385 | supermod.docDotFileType.__init__(self, mixedclass_, content_) 386 | supermod.docDotFileType.subclass = docDotFileTypeSub 387 | # end class docDotFileTypeSub 388 | 389 | 390 | class docTocItemTypeSub(supermod.docTocItemType): 391 | def __init__(self, id=None, valueOf_='', mixedclass_=None, content_=None): 392 | supermod.docTocItemType.__init__(self, mixedclass_, content_) 393 | supermod.docTocItemType.subclass = docTocItemTypeSub 394 | # end class docTocItemTypeSub 395 | 396 | 397 | class docTocListTypeSub(supermod.docTocListType): 398 | def __init__(self, tocitem=None): 399 | supermod.docTocListType.__init__(self, tocitem) 400 | supermod.docTocListType.subclass = docTocListTypeSub 401 | # end class docTocListTypeSub 402 | 403 | 404 | class docLanguageTypeSub(supermod.docLanguageType): 405 | def __init__(self, langid=None, para=None): 406 | supermod.docLanguageType.__init__(self, langid, para) 407 | supermod.docLanguageType.subclass = docLanguageTypeSub 408 | # end class docLanguageTypeSub 409 | 410 | 411 | class docParamListTypeSub(supermod.docParamListType): 412 | def __init__(self, kind=None, parameteritem=None): 413 | supermod.docParamListType.__init__(self, kind, parameteritem) 414 | supermod.docParamListType.subclass = docParamListTypeSub 415 | # end class docParamListTypeSub 416 | 417 | 418 | class docParamListItemSub(supermod.docParamListItem): 419 | def __init__(self, parameternamelist=None, parameterdescription=None): 420 | supermod.docParamListItem.__init__(self, parameternamelist, parameterdescription) 421 | supermod.docParamListItem.subclass = docParamListItemSub 422 | # end class docParamListItemSub 423 | 424 | 425 | class docParamNameListSub(supermod.docParamNameList): 426 | def __init__(self, parametername=None): 427 | supermod.docParamNameList.__init__(self, parametername) 428 | supermod.docParamNameList.subclass = docParamNameListSub 429 | # end class docParamNameListSub 430 | 431 | 432 | class docParamNameSub(supermod.docParamName): 433 | def __init__(self, direction=None, ref=None, mixedclass_=None, content_=None): 434 | supermod.docParamName.__init__(self, mixedclass_, content_) 435 | supermod.docParamName.subclass = docParamNameSub 436 | # end class docParamNameSub 437 | 438 | 439 | class docXRefSectTypeSub(supermod.docXRefSectType): 440 | def __init__(self, id=None, xreftitle=None, xrefdescription=None): 441 | supermod.docXRefSectType.__init__(self, id, xreftitle, xrefdescription) 442 | supermod.docXRefSectType.subclass = docXRefSectTypeSub 443 | # end class docXRefSectTypeSub 444 | 445 | 446 | class docCopyTypeSub(supermod.docCopyType): 447 | def __init__(self, link=None, para=None, sect1=None, internal=None): 448 | supermod.docCopyType.__init__(self, link, para, sect1, internal) 449 | supermod.docCopyType.subclass = docCopyTypeSub 450 | # end class docCopyTypeSub 451 | 452 | 453 | class docCharTypeSub(supermod.docCharType): 454 | def __init__(self, char=None, valueOf_=''): 455 | supermod.docCharType.__init__(self, char) 456 | supermod.docCharType.subclass = docCharTypeSub 457 | # end class docCharTypeSub 458 | 459 | class docParaTypeSub(supermod.docParaType): 460 | def __init__(self, char=None, valueOf_=''): 461 | supermod.docParaType.__init__(self, char) 462 | 463 | self.parameterlist = [] 464 | self.simplesects = [] 465 | self.content = [] 466 | 467 | def buildChildren(self, child_, nodeName_): 468 | supermod.docParaType.buildChildren(self, child_, nodeName_) 469 | 470 | if child_.nodeType == Node.TEXT_NODE: 471 | obj_ = self.mixedclass_(MixedContainer.CategoryText, 472 | MixedContainer.TypeNone, '', child_.nodeValue) 473 | self.content.append(obj_) 474 | elif child_.nodeType == Node.ELEMENT_NODE and \ 475 | nodeName_ == "ref": 476 | obj_ = supermod.docRefTextType.factory() 477 | obj_.build(child_) 478 | self.content.append(obj_) 479 | elif child_.nodeType == Node.ELEMENT_NODE and \ 480 | nodeName_ == 'parameterlist': 481 | obj_ = supermod.docParamListType.factory() 482 | obj_.build(child_) 483 | self.parameterlist.append(obj_) 484 | elif child_.nodeType == Node.ELEMENT_NODE and \ 485 | nodeName_ == 'simplesect': 486 | obj_ = supermod.docSimpleSectType.factory() 487 | obj_.build(child_) 488 | self.simplesects.append(obj_) 489 | 490 | 491 | supermod.docParaType.subclass = docParaTypeSub 492 | # end class docParaTypeSub 493 | 494 | 495 | 496 | def parse(inFilename): 497 | doc = minidom.parse(inFilename) 498 | rootNode = doc.documentElement 499 | rootObj = supermod.DoxygenType.factory() 500 | rootObj.build(rootNode) 501 | return rootObj 502 | 503 | 504 | -------------------------------------------------------------------------------- /docs/doxygen/doxyxml/generated/index.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Generated Mon Feb 9 19:08:05 2009 by generateDS.py. 5 | """ 6 | 7 | from xml.dom import minidom 8 | 9 | import os 10 | import sys 11 | from . import compound 12 | 13 | from . import indexsuper as supermod 14 | 15 | class DoxygenTypeSub(supermod.DoxygenType): 16 | def __init__(self, version=None, compound=None): 17 | supermod.DoxygenType.__init__(self, version, compound) 18 | 19 | def find_compounds_and_members(self, details): 20 | """ 21 | Returns a list of all compounds and their members which match details 22 | """ 23 | 24 | results = [] 25 | for compound in self.compound: 26 | members = compound.find_members(details) 27 | if members: 28 | results.append([compound, members]) 29 | else: 30 | if details.match(compound): 31 | results.append([compound, []]) 32 | 33 | return results 34 | 35 | supermod.DoxygenType.subclass = DoxygenTypeSub 36 | # end class DoxygenTypeSub 37 | 38 | 39 | class CompoundTypeSub(supermod.CompoundType): 40 | def __init__(self, kind=None, refid=None, name='', member=None): 41 | supermod.CompoundType.__init__(self, kind, refid, name, member) 42 | 43 | def find_members(self, details): 44 | """ 45 | Returns a list of all members which match details 46 | """ 47 | 48 | results = [] 49 | 50 | for member in self.member: 51 | if details.match(member): 52 | results.append(member) 53 | 54 | return results 55 | 56 | supermod.CompoundType.subclass = CompoundTypeSub 57 | # end class CompoundTypeSub 58 | 59 | 60 | class MemberTypeSub(supermod.MemberType): 61 | 62 | def __init__(self, kind=None, refid=None, name=''): 63 | supermod.MemberType.__init__(self, kind, refid, name) 64 | 65 | supermod.MemberType.subclass = MemberTypeSub 66 | # end class MemberTypeSub 67 | 68 | 69 | def parse(inFilename): 70 | 71 | doc = minidom.parse(inFilename) 72 | rootNode = doc.documentElement 73 | rootObj = supermod.DoxygenType.factory() 74 | rootObj.build(rootNode) 75 | 76 | return rootObj 77 | 78 | -------------------------------------------------------------------------------- /docs/doxygen/doxyxml/generated/indexsuper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 4 | # Generated Thu Jun 11 18:43:54 2009 by generateDS.py. 5 | # 6 | 7 | 8 | import sys 9 | 10 | from xml.dom import minidom 11 | from xml.dom import Node 12 | 13 | # 14 | # User methods 15 | # 16 | # Calls to the methods in these classes are generated by generateDS.py. 17 | # You can replace these methods by re-implementing the following class 18 | # in a module named generatedssuper.py. 19 | 20 | try: 21 | from generatedssuper import GeneratedsSuper 22 | except ImportError as exp: 23 | 24 | class GeneratedsSuper(object): 25 | def format_string(self, input_data, input_name=''): 26 | return input_data 27 | def format_integer(self, input_data, input_name=''): 28 | return '%d' % input_data 29 | def format_float(self, input_data, input_name=''): 30 | return '%f' % input_data 31 | def format_double(self, input_data, input_name=''): 32 | return '%e' % input_data 33 | def format_boolean(self, input_data, input_name=''): 34 | return '%s' % input_data 35 | 36 | 37 | # 38 | # If you have installed IPython you can uncomment and use the following. 39 | # IPython is available from http://ipython.scipy.org/. 40 | # 41 | 42 | ## from IPython.Shell import IPShellEmbed 43 | ## args = '' 44 | ## ipshell = IPShellEmbed(args, 45 | ## banner = 'Dropping into IPython', 46 | ## exit_msg = 'Leaving Interpreter, back to program.') 47 | 48 | # Then use the following line where and when you want to drop into the 49 | # IPython shell: 50 | # ipshell(' -- Entering ipshell.\nHit Ctrl-D to exit') 51 | 52 | # 53 | # Globals 54 | # 55 | 56 | ExternalEncoding = 'ascii' 57 | 58 | # 59 | # Support/utility functions. 60 | # 61 | 62 | def showIndent(outfile, level): 63 | for idx in range(level): 64 | outfile.write(' ') 65 | 66 | def quote_xml(inStr): 67 | s1 = (isinstance(inStr, str) and inStr or 68 | '%s' % inStr) 69 | s1 = s1.replace('&', '&') 70 | s1 = s1.replace('<', '<') 71 | s1 = s1.replace('>', '>') 72 | return s1 73 | 74 | def quote_attrib(inStr): 75 | s1 = (isinstance(inStr, str) and inStr or 76 | '%s' % inStr) 77 | s1 = s1.replace('&', '&') 78 | s1 = s1.replace('<', '<') 79 | s1 = s1.replace('>', '>') 80 | if '"' in s1: 81 | if "'" in s1: 82 | s1 = '"%s"' % s1.replace('"', """) 83 | else: 84 | s1 = "'%s'" % s1 85 | else: 86 | s1 = '"%s"' % s1 87 | return s1 88 | 89 | def quote_python(inStr): 90 | s1 = inStr 91 | if s1.find("'") == -1: 92 | if s1.find('\n') == -1: 93 | return "'%s'" % s1 94 | else: 95 | return "'''%s'''" % s1 96 | else: 97 | if s1.find('"') != -1: 98 | s1 = s1.replace('"', '\\"') 99 | if s1.find('\n') == -1: 100 | return '"%s"' % s1 101 | else: 102 | return '"""%s"""' % s1 103 | 104 | 105 | class MixedContainer(object): 106 | # Constants for category: 107 | CategoryNone = 0 108 | CategoryText = 1 109 | CategorySimple = 2 110 | CategoryComplex = 3 111 | # Constants for content_type: 112 | TypeNone = 0 113 | TypeText = 1 114 | TypeString = 2 115 | TypeInteger = 3 116 | TypeFloat = 4 117 | TypeDecimal = 5 118 | TypeDouble = 6 119 | TypeBoolean = 7 120 | def __init__(self, category, content_type, name, value): 121 | self.category = category 122 | self.content_type = content_type 123 | self.name = name 124 | self.value = value 125 | def getCategory(self): 126 | return self.category 127 | def getContenttype(self, content_type): 128 | return self.content_type 129 | def getValue(self): 130 | return self.value 131 | def getName(self): 132 | return self.name 133 | def export(self, outfile, level, name, namespace): 134 | if self.category == MixedContainer.CategoryText: 135 | outfile.write(self.value) 136 | elif self.category == MixedContainer.CategorySimple: 137 | self.exportSimple(outfile, level, name) 138 | else: # category == MixedContainer.CategoryComplex 139 | self.value.export(outfile, level, namespace,name) 140 | def exportSimple(self, outfile, level, name): 141 | if self.content_type == MixedContainer.TypeString: 142 | outfile.write('<%s>%s' % (self.name, self.value, self.name)) 143 | elif self.content_type == MixedContainer.TypeInteger or \ 144 | self.content_type == MixedContainer.TypeBoolean: 145 | outfile.write('<%s>%d' % (self.name, self.value, self.name)) 146 | elif self.content_type == MixedContainer.TypeFloat or \ 147 | self.content_type == MixedContainer.TypeDecimal: 148 | outfile.write('<%s>%f' % (self.name, self.value, self.name)) 149 | elif self.content_type == MixedContainer.TypeDouble: 150 | outfile.write('<%s>%g' % (self.name, self.value, self.name)) 151 | def exportLiteral(self, outfile, level, name): 152 | if self.category == MixedContainer.CategoryText: 153 | showIndent(outfile, level) 154 | outfile.write('MixedContainer(%d, %d, "%s", "%s"),\n' % \ 155 | (self.category, self.content_type, self.name, self.value)) 156 | elif self.category == MixedContainer.CategorySimple: 157 | showIndent(outfile, level) 158 | outfile.write('MixedContainer(%d, %d, "%s", "%s"),\n' % \ 159 | (self.category, self.content_type, self.name, self.value)) 160 | else: # category == MixedContainer.CategoryComplex 161 | showIndent(outfile, level) 162 | outfile.write('MixedContainer(%d, %d, "%s",\n' % \ 163 | (self.category, self.content_type, self.name,)) 164 | self.value.exportLiteral(outfile, level + 1) 165 | showIndent(outfile, level) 166 | outfile.write(')\n') 167 | 168 | 169 | class _MemberSpec(object): 170 | def __init__(self, name='', data_type='', container=0): 171 | self.name = name 172 | self.data_type = data_type 173 | self.container = container 174 | def set_name(self, name): self.name = name 175 | def get_name(self): return self.name 176 | def set_data_type(self, data_type): self.data_type = data_type 177 | def get_data_type(self): return self.data_type 178 | def set_container(self, container): self.container = container 179 | def get_container(self): return self.container 180 | 181 | 182 | # 183 | # Data representation classes. 184 | # 185 | 186 | class DoxygenType(GeneratedsSuper): 187 | subclass = None 188 | superclass = None 189 | def __init__(self, version=None, compound=None): 190 | self.version = version 191 | if compound is None: 192 | self.compound = [] 193 | else: 194 | self.compound = compound 195 | def factory(*args_, **kwargs_): 196 | if DoxygenType.subclass: 197 | return DoxygenType.subclass(*args_, **kwargs_) 198 | else: 199 | return DoxygenType(*args_, **kwargs_) 200 | factory = staticmethod(factory) 201 | def get_compound(self): return self.compound 202 | def set_compound(self, compound): self.compound = compound 203 | def add_compound(self, value): self.compound.append(value) 204 | def insert_compound(self, index, value): self.compound[index] = value 205 | def get_version(self): return self.version 206 | def set_version(self, version): self.version = version 207 | def export(self, outfile, level, namespace_='', name_='DoxygenType', namespacedef_=''): 208 | showIndent(outfile, level) 209 | outfile.write('<%s%s %s' % (namespace_, name_, namespacedef_, )) 210 | self.exportAttributes(outfile, level, namespace_, name_='DoxygenType') 211 | if self.hasContent_(): 212 | outfile.write('>\n') 213 | self.exportChildren(outfile, level + 1, namespace_, name_) 214 | showIndent(outfile, level) 215 | outfile.write('\n' % (namespace_, name_)) 216 | else: 217 | outfile.write(' />\n') 218 | def exportAttributes(self, outfile, level, namespace_='', name_='DoxygenType'): 219 | outfile.write(' version=%s' % (self.format_string(quote_attrib(self.version).encode(ExternalEncoding), input_name='version'), )) 220 | def exportChildren(self, outfile, level, namespace_='', name_='DoxygenType'): 221 | for compound_ in self.compound: 222 | compound_.export(outfile, level, namespace_, name_='compound') 223 | def hasContent_(self): 224 | if ( 225 | self.compound is not None 226 | ): 227 | return True 228 | else: 229 | return False 230 | def exportLiteral(self, outfile, level, name_='DoxygenType'): 231 | level += 1 232 | self.exportLiteralAttributes(outfile, level, name_) 233 | if self.hasContent_(): 234 | self.exportLiteralChildren(outfile, level, name_) 235 | def exportLiteralAttributes(self, outfile, level, name_): 236 | if self.version is not None: 237 | showIndent(outfile, level) 238 | outfile.write('version = %s,\n' % (self.version,)) 239 | def exportLiteralChildren(self, outfile, level, name_): 240 | showIndent(outfile, level) 241 | outfile.write('compound=[\n') 242 | level += 1 243 | for compound in self.compound: 244 | showIndent(outfile, level) 245 | outfile.write('model_.compound(\n') 246 | compound.exportLiteral(outfile, level, name_='compound') 247 | showIndent(outfile, level) 248 | outfile.write('),\n') 249 | level -= 1 250 | showIndent(outfile, level) 251 | outfile.write('],\n') 252 | def build(self, node_): 253 | attrs = node_.attributes 254 | self.buildAttributes(attrs) 255 | for child_ in node_.childNodes: 256 | nodeName_ = child_.nodeName.split(':')[-1] 257 | self.buildChildren(child_, nodeName_) 258 | def buildAttributes(self, attrs): 259 | if attrs.get('version'): 260 | self.version = attrs.get('version').value 261 | def buildChildren(self, child_, nodeName_): 262 | if child_.nodeType == Node.ELEMENT_NODE and \ 263 | nodeName_ == 'compound': 264 | obj_ = CompoundType.factory() 265 | obj_.build(child_) 266 | self.compound.append(obj_) 267 | # end class DoxygenType 268 | 269 | 270 | class CompoundType(GeneratedsSuper): 271 | subclass = None 272 | superclass = None 273 | def __init__(self, kind=None, refid=None, name=None, member=None): 274 | self.kind = kind 275 | self.refid = refid 276 | self.name = name 277 | if member is None: 278 | self.member = [] 279 | else: 280 | self.member = member 281 | def factory(*args_, **kwargs_): 282 | if CompoundType.subclass: 283 | return CompoundType.subclass(*args_, **kwargs_) 284 | else: 285 | return CompoundType(*args_, **kwargs_) 286 | factory = staticmethod(factory) 287 | def get_name(self): return self.name 288 | def set_name(self, name): self.name = name 289 | def get_member(self): return self.member 290 | def set_member(self, member): self.member = member 291 | def add_member(self, value): self.member.append(value) 292 | def insert_member(self, index, value): self.member[index] = value 293 | def get_kind(self): return self.kind 294 | def set_kind(self, kind): self.kind = kind 295 | def get_refid(self): return self.refid 296 | def set_refid(self, refid): self.refid = refid 297 | def export(self, outfile, level, namespace_='', name_='CompoundType', namespacedef_=''): 298 | showIndent(outfile, level) 299 | outfile.write('<%s%s %s' % (namespace_, name_, namespacedef_, )) 300 | self.exportAttributes(outfile, level, namespace_, name_='CompoundType') 301 | if self.hasContent_(): 302 | outfile.write('>\n') 303 | self.exportChildren(outfile, level + 1, namespace_, name_) 304 | showIndent(outfile, level) 305 | outfile.write('\n' % (namespace_, name_)) 306 | else: 307 | outfile.write(' />\n') 308 | def exportAttributes(self, outfile, level, namespace_='', name_='CompoundType'): 309 | outfile.write(' kind=%s' % (quote_attrib(self.kind), )) 310 | outfile.write(' refid=%s' % (self.format_string(quote_attrib(self.refid).encode(ExternalEncoding), input_name='refid'), )) 311 | def exportChildren(self, outfile, level, namespace_='', name_='CompoundType'): 312 | if self.name is not None: 313 | showIndent(outfile, level) 314 | outfile.write('<%sname>%s\n' % (namespace_, self.format_string(quote_xml(self.name).encode(ExternalEncoding), input_name='name'), namespace_)) 315 | for member_ in self.member: 316 | member_.export(outfile, level, namespace_, name_='member') 317 | def hasContent_(self): 318 | if ( 319 | self.name is not None or 320 | self.member is not None 321 | ): 322 | return True 323 | else: 324 | return False 325 | def exportLiteral(self, outfile, level, name_='CompoundType'): 326 | level += 1 327 | self.exportLiteralAttributes(outfile, level, name_) 328 | if self.hasContent_(): 329 | self.exportLiteralChildren(outfile, level, name_) 330 | def exportLiteralAttributes(self, outfile, level, name_): 331 | if self.kind is not None: 332 | showIndent(outfile, level) 333 | outfile.write('kind = "%s",\n' % (self.kind,)) 334 | if self.refid is not None: 335 | showIndent(outfile, level) 336 | outfile.write('refid = %s,\n' % (self.refid,)) 337 | def exportLiteralChildren(self, outfile, level, name_): 338 | showIndent(outfile, level) 339 | outfile.write('name=%s,\n' % quote_python(self.name).encode(ExternalEncoding)) 340 | showIndent(outfile, level) 341 | outfile.write('member=[\n') 342 | level += 1 343 | for member in self.member: 344 | showIndent(outfile, level) 345 | outfile.write('model_.member(\n') 346 | member.exportLiteral(outfile, level, name_='member') 347 | showIndent(outfile, level) 348 | outfile.write('),\n') 349 | level -= 1 350 | showIndent(outfile, level) 351 | outfile.write('],\n') 352 | def build(self, node_): 353 | attrs = node_.attributes 354 | self.buildAttributes(attrs) 355 | for child_ in node_.childNodes: 356 | nodeName_ = child_.nodeName.split(':')[-1] 357 | self.buildChildren(child_, nodeName_) 358 | def buildAttributes(self, attrs): 359 | if attrs.get('kind'): 360 | self.kind = attrs.get('kind').value 361 | if attrs.get('refid'): 362 | self.refid = attrs.get('refid').value 363 | def buildChildren(self, child_, nodeName_): 364 | if child_.nodeType == Node.ELEMENT_NODE and \ 365 | nodeName_ == 'name': 366 | name_ = '' 367 | for text__content_ in child_.childNodes: 368 | name_ += text__content_.nodeValue 369 | self.name = name_ 370 | elif child_.nodeType == Node.ELEMENT_NODE and \ 371 | nodeName_ == 'member': 372 | obj_ = MemberType.factory() 373 | obj_.build(child_) 374 | self.member.append(obj_) 375 | # end class CompoundType 376 | 377 | 378 | class MemberType(GeneratedsSuper): 379 | subclass = None 380 | superclass = None 381 | def __init__(self, kind=None, refid=None, name=None): 382 | self.kind = kind 383 | self.refid = refid 384 | self.name = name 385 | def factory(*args_, **kwargs_): 386 | if MemberType.subclass: 387 | return MemberType.subclass(*args_, **kwargs_) 388 | else: 389 | return MemberType(*args_, **kwargs_) 390 | factory = staticmethod(factory) 391 | def get_name(self): return self.name 392 | def set_name(self, name): self.name = name 393 | def get_kind(self): return self.kind 394 | def set_kind(self, kind): self.kind = kind 395 | def get_refid(self): return self.refid 396 | def set_refid(self, refid): self.refid = refid 397 | def export(self, outfile, level, namespace_='', name_='MemberType', namespacedef_=''): 398 | showIndent(outfile, level) 399 | outfile.write('<%s%s %s' % (namespace_, name_, namespacedef_, )) 400 | self.exportAttributes(outfile, level, namespace_, name_='MemberType') 401 | if self.hasContent_(): 402 | outfile.write('>\n') 403 | self.exportChildren(outfile, level + 1, namespace_, name_) 404 | showIndent(outfile, level) 405 | outfile.write('\n' % (namespace_, name_)) 406 | else: 407 | outfile.write(' />\n') 408 | def exportAttributes(self, outfile, level, namespace_='', name_='MemberType'): 409 | outfile.write(' kind=%s' % (quote_attrib(self.kind), )) 410 | outfile.write(' refid=%s' % (self.format_string(quote_attrib(self.refid).encode(ExternalEncoding), input_name='refid'), )) 411 | def exportChildren(self, outfile, level, namespace_='', name_='MemberType'): 412 | if self.name is not None: 413 | showIndent(outfile, level) 414 | outfile.write('<%sname>%s\n' % (namespace_, self.format_string(quote_xml(self.name).encode(ExternalEncoding), input_name='name'), namespace_)) 415 | def hasContent_(self): 416 | if ( 417 | self.name is not None 418 | ): 419 | return True 420 | else: 421 | return False 422 | def exportLiteral(self, outfile, level, name_='MemberType'): 423 | level += 1 424 | self.exportLiteralAttributes(outfile, level, name_) 425 | if self.hasContent_(): 426 | self.exportLiteralChildren(outfile, level, name_) 427 | def exportLiteralAttributes(self, outfile, level, name_): 428 | if self.kind is not None: 429 | showIndent(outfile, level) 430 | outfile.write('kind = "%s",\n' % (self.kind,)) 431 | if self.refid is not None: 432 | showIndent(outfile, level) 433 | outfile.write('refid = %s,\n' % (self.refid,)) 434 | def exportLiteralChildren(self, outfile, level, name_): 435 | showIndent(outfile, level) 436 | outfile.write('name=%s,\n' % quote_python(self.name).encode(ExternalEncoding)) 437 | def build(self, node_): 438 | attrs = node_.attributes 439 | self.buildAttributes(attrs) 440 | for child_ in node_.childNodes: 441 | nodeName_ = child_.nodeName.split(':')[-1] 442 | self.buildChildren(child_, nodeName_) 443 | def buildAttributes(self, attrs): 444 | if attrs.get('kind'): 445 | self.kind = attrs.get('kind').value 446 | if attrs.get('refid'): 447 | self.refid = attrs.get('refid').value 448 | def buildChildren(self, child_, nodeName_): 449 | if child_.nodeType == Node.ELEMENT_NODE and \ 450 | nodeName_ == 'name': 451 | name_ = '' 452 | for text__content_ in child_.childNodes: 453 | name_ += text__content_.nodeValue 454 | self.name = name_ 455 | # end class MemberType 456 | 457 | 458 | USAGE_TEXT = """ 459 | Usage: python .py [ -s ] 460 | Options: 461 | -s Use the SAX parser, not the minidom parser. 462 | """ 463 | 464 | def usage(): 465 | print(USAGE_TEXT) 466 | sys.exit(1) 467 | 468 | 469 | def parse(inFileName): 470 | doc = minidom.parse(inFileName) 471 | rootNode = doc.documentElement 472 | rootObj = DoxygenType.factory() 473 | rootObj.build(rootNode) 474 | # Enable Python to collect the space used by the DOM. 475 | doc = None 476 | sys.stdout.write('\n') 477 | rootObj.export(sys.stdout, 0, name_="doxygenindex", 478 | namespacedef_='') 479 | return rootObj 480 | 481 | 482 | def parseString(inString): 483 | doc = minidom.parseString(inString) 484 | rootNode = doc.documentElement 485 | rootObj = DoxygenType.factory() 486 | rootObj.build(rootNode) 487 | # Enable Python to collect the space used by the DOM. 488 | doc = None 489 | sys.stdout.write('\n') 490 | rootObj.export(sys.stdout, 0, name_="doxygenindex", 491 | namespacedef_='') 492 | return rootObj 493 | 494 | 495 | def parseLiteral(inFileName): 496 | doc = minidom.parse(inFileName) 497 | rootNode = doc.documentElement 498 | rootObj = DoxygenType.factory() 499 | rootObj.build(rootNode) 500 | # Enable Python to collect the space used by the DOM. 501 | doc = None 502 | sys.stdout.write('from index import *\n\n') 503 | sys.stdout.write('rootObj = doxygenindex(\n') 504 | rootObj.exportLiteral(sys.stdout, 0, name_="doxygenindex") 505 | sys.stdout.write(')\n') 506 | return rootObj 507 | 508 | 509 | def main(): 510 | args = sys.argv[1:] 511 | if len(args) == 1: 512 | parse(args[0]) 513 | else: 514 | usage() 515 | 516 | 517 | 518 | 519 | if __name__ == '__main__': 520 | main() 521 | #import pdb 522 | #pdb.run('main()') 523 | -------------------------------------------------------------------------------- /docs/doxygen/doxyxml/text.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2010 Free Software Foundation, Inc. 3 | # 4 | # This file was generated by gr_modtool, a tool from the GNU Radio framework 5 | # This file is a part of gr-paint 6 | # 7 | # SPDX-License-Identifier: GPL-3.0-or-later 8 | # 9 | # 10 | """ 11 | Utilities for extracting text from generated classes. 12 | """ 13 | 14 | def is_string(txt): 15 | if isinstance(txt, str): 16 | return True 17 | try: 18 | if isinstance(txt, str): 19 | return True 20 | except NameError: 21 | pass 22 | return False 23 | 24 | def description(obj): 25 | if obj is None: 26 | return None 27 | return description_bit(obj).strip() 28 | 29 | def description_bit(obj): 30 | if hasattr(obj, 'content'): 31 | contents = [description_bit(item) for item in obj.content] 32 | result = ''.join(contents) 33 | elif hasattr(obj, 'content_'): 34 | contents = [description_bit(item) for item in obj.content_] 35 | result = ''.join(contents) 36 | elif hasattr(obj, 'value'): 37 | result = description_bit(obj.value) 38 | elif is_string(obj): 39 | return obj 40 | else: 41 | raise Exception('Expecting a string or something with content, content_ or value attribute') 42 | # If this bit is a paragraph then add one some line breaks. 43 | if hasattr(obj, 'name') and obj.name == 'para': 44 | result += "\n\n" 45 | return result 46 | -------------------------------------------------------------------------------- /docs/doxygen/other/group_defs.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | * \defgroup block GNU Radio PAINT C++ Signal Processing Blocks 3 | * \brief All C++ blocks that can be used from the PAINT GNU Radio 4 | * module are listed here or in the subcategories below. 5 | * 6 | */ 7 | 8 | -------------------------------------------------------------------------------- /docs/doxygen/other/main_page.dox: -------------------------------------------------------------------------------- 1 | /*! \mainpage 2 | 3 | Welcome to the GNU Radio PAINT Block 4 | 5 | This is the intro page for the Doxygen manual generated for the PAINT 6 | block (docs/doxygen/other/main_page.dox). Edit it to add more detailed 7 | documentation about the new GNU Radio modules contained in this 8 | project. 9 | 10 | */ 11 | -------------------------------------------------------------------------------- /docs/doxygen/pydoc_macros.h: -------------------------------------------------------------------------------- 1 | #ifndef PYDOC_MACROS_H 2 | #define PYDOC_MACROS_H 3 | 4 | #define __EXPAND(x) x 5 | #define __COUNT(_1, _2, _3, _4, _5, _6, _7, COUNT, ...) COUNT 6 | #define __VA_SIZE(...) __EXPAND(__COUNT(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1)) 7 | #define __CAT1(a, b) a##b 8 | #define __CAT2(a, b) __CAT1(a, b) 9 | #define __DOC1(n1) __doc_##n1 10 | #define __DOC2(n1, n2) __doc_##n1##_##n2 11 | #define __DOC3(n1, n2, n3) __doc_##n1##_##n2##_##n3 12 | #define __DOC4(n1, n2, n3, n4) __doc_##n1##_##n2##_##n3##_##n4 13 | #define __DOC5(n1, n2, n3, n4, n5) __doc_##n1##_##n2##_##n3##_##n4##_##n5 14 | #define __DOC6(n1, n2, n3, n4, n5, n6) __doc_##n1##_##n2##_##n3##_##n4##_##n5##_##n6 15 | #define __DOC7(n1, n2, n3, n4, n5, n6, n7) \ 16 | __doc_##n1##_##n2##_##n3##_##n4##_##n5##_##n6##_##n7 17 | #define DOC(...) __EXPAND(__EXPAND(__CAT2(__DOC, __VA_SIZE(__VA_ARGS__)))(__VA_ARGS__)) 18 | 19 | #endif // PYDOC_MACROS_H -------------------------------------------------------------------------------- /docs/doxygen/update_pydoc.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2010-2012 Free Software Foundation, Inc. 3 | # 4 | # This file was generated by gr_modtool, a tool from the GNU Radio framework 5 | # This file is a part of gnuradio 6 | # 7 | # SPDX-License-Identifier: GPL-3.0-or-later 8 | # 9 | # 10 | """ 11 | Updates the *pydoc_h files for a module 12 | Execute using: python update_pydoc.py xml_path outputfilename 13 | 14 | The file instructs Pybind11 to transfer the doxygen comments into the 15 | python docstrings. 16 | 17 | """ 18 | 19 | import os, sys, time, glob, re, json 20 | from argparse import ArgumentParser 21 | 22 | from doxyxml import DoxyIndex, DoxyClass, DoxyFriend, DoxyFunction, DoxyFile 23 | from doxyxml import DoxyOther, base 24 | 25 | def py_name(name): 26 | bits = name.split('_') 27 | return '_'.join(bits[1:]) 28 | 29 | def make_name(name): 30 | bits = name.split('_') 31 | return bits[0] + '_make_' + '_'.join(bits[1:]) 32 | 33 | 34 | class Block(object): 35 | """ 36 | Checks if doxyxml produced objects correspond to a gnuradio block. 37 | """ 38 | 39 | @classmethod 40 | def includes(cls, item): 41 | if not isinstance(item, DoxyClass): 42 | return False 43 | # Check for a parsing error. 44 | if item.error(): 45 | return False 46 | friendname = make_name(item.name()) 47 | is_a_block = item.has_member(friendname, DoxyFriend) 48 | # But now sometimes the make function isn't a friend so check again. 49 | if not is_a_block: 50 | is_a_block = di.has_member(friendname, DoxyFunction) 51 | return is_a_block 52 | 53 | class Block2(object): 54 | """ 55 | Checks if doxyxml produced objects correspond to a new style 56 | gnuradio block. 57 | """ 58 | 59 | @classmethod 60 | def includes(cls, item): 61 | if not isinstance(item, DoxyClass): 62 | return False 63 | # Check for a parsing error. 64 | if item.error(): 65 | return False 66 | is_a_block2 = item.has_member('make', DoxyFunction) and item.has_member('sptr', DoxyOther) 67 | return is_a_block2 68 | 69 | 70 | def utoascii(text): 71 | """ 72 | Convert unicode text into ascii and escape quotes and backslashes. 73 | """ 74 | if text is None: 75 | return '' 76 | out = text.encode('ascii', 'replace') 77 | # swig will require us to replace blackslash with 4 backslashes 78 | # TODO: evaluate what this should be for pybind11 79 | out = out.replace(b'\\', b'\\\\\\\\') 80 | out = out.replace(b'"', b'\\"').decode('ascii') 81 | return str(out) 82 | 83 | 84 | def combine_descriptions(obj): 85 | """ 86 | Combines the brief and detailed descriptions of an object together. 87 | """ 88 | description = [] 89 | bd = obj.brief_description.strip() 90 | dd = obj.detailed_description.strip() 91 | if bd: 92 | description.append(bd) 93 | if dd: 94 | description.append(dd) 95 | return utoascii('\n\n'.join(description)).strip() 96 | 97 | def format_params(parameteritems): 98 | output = ['Args:'] 99 | template = ' {0} : {1}' 100 | for pi in parameteritems: 101 | output.append(template.format(pi.name, pi.description)) 102 | return '\n'.join(output) 103 | 104 | entry_templ = '%feature("docstring") {name} "{docstring}"' 105 | def make_entry(obj, name=None, templ="{description}", description=None, params=[]): 106 | """ 107 | Create a docstring key/value pair, where the key is the object name. 108 | 109 | obj - a doxyxml object from which documentation will be extracted. 110 | name - the name of the C object (defaults to obj.name()) 111 | templ - an optional template for the docstring containing only one 112 | variable named 'description'. 113 | description - if this optional variable is set then it's value is 114 | used as the description instead of extracting it from obj. 115 | """ 116 | if name is None: 117 | name=obj.name() 118 | if hasattr(obj,'_parse_data') and hasattr(obj._parse_data,'definition'): 119 | name=obj._parse_data.definition.split(' ')[-1] 120 | if "operator " in name: 121 | return '' 122 | if description is None: 123 | description = combine_descriptions(obj) 124 | if params: 125 | description += '\n\n' 126 | description += utoascii(format_params(params)) 127 | docstring = templ.format(description=description) 128 | 129 | return {name: docstring} 130 | 131 | 132 | def make_class_entry(klass, description=None, ignored_methods=[], params=None): 133 | """ 134 | Create a class docstring key/value pair. 135 | """ 136 | if params is None: 137 | params = klass.params 138 | output = {} 139 | output.update(make_entry(klass, description=description, params=params)) 140 | for func in klass.in_category(DoxyFunction): 141 | if func.name() not in ignored_methods: 142 | name = klass.name() + '::' + func.name() 143 | output.update(make_entry(func, name=name)) 144 | return output 145 | 146 | 147 | def make_block_entry(di, block): 148 | """ 149 | Create class and function docstrings of a gnuradio block 150 | """ 151 | descriptions = [] 152 | # Get the documentation associated with the class. 153 | class_desc = combine_descriptions(block) 154 | if class_desc: 155 | descriptions.append(class_desc) 156 | # Get the documentation associated with the make function 157 | make_func = di.get_member(make_name(block.name()), DoxyFunction) 158 | make_func_desc = combine_descriptions(make_func) 159 | if make_func_desc: 160 | descriptions.append(make_func_desc) 161 | # Get the documentation associated with the file 162 | try: 163 | block_file = di.get_member(block.name() + ".h", DoxyFile) 164 | file_desc = combine_descriptions(block_file) 165 | if file_desc: 166 | descriptions.append(file_desc) 167 | except base.Base.NoSuchMember: 168 | # Don't worry if we can't find a matching file. 169 | pass 170 | # And join them all together to make a super duper description. 171 | super_description = "\n\n".join(descriptions) 172 | # Associate the combined description with the class and 173 | # the make function. 174 | output = {} 175 | output.update(make_class_entry(block, description=super_description)) 176 | output.update(make_entry(make_func, description=super_description, 177 | params=block.params)) 178 | return output 179 | 180 | def make_block2_entry(di, block): 181 | """ 182 | Create class and function docstrings of a new style gnuradio block 183 | """ 184 | # For new style blocks all the relevant documentation should be 185 | # associated with the 'make' method. 186 | class_description = combine_descriptions(block) 187 | make_func = block.get_member('make', DoxyFunction) 188 | make_description = combine_descriptions(make_func) 189 | description = class_description + "\n\nConstructor Specific Documentation:\n\n" + make_description 190 | # Associate the combined description with the class and 191 | # the make function. 192 | output = {} 193 | output.update(make_class_entry( 194 | block, description=description, 195 | ignored_methods=['make'], params=make_func.params)) 196 | makename = block.name() + '::make' 197 | output.update(make_entry( 198 | make_func, name=makename, description=description, 199 | params=make_func.params)) 200 | return output 201 | 202 | def get_docstrings_dict(di, custom_output=None): 203 | 204 | output = {} 205 | if custom_output: 206 | output.update(custom_output) 207 | 208 | # Create docstrings for the blocks. 209 | blocks = di.in_category(Block) 210 | blocks2 = di.in_category(Block2) 211 | 212 | make_funcs = set([]) 213 | for block in blocks: 214 | try: 215 | make_func = di.get_member(make_name(block.name()), DoxyFunction) 216 | # Don't want to risk writing to output twice. 217 | if make_func.name() not in make_funcs: 218 | make_funcs.add(make_func.name()) 219 | output.update(make_block_entry(di, block)) 220 | except block.ParsingError: 221 | sys.stderr.write('Parsing error for block {0}\n'.format(block.name())) 222 | raise 223 | 224 | for block in blocks2: 225 | try: 226 | make_func = block.get_member('make', DoxyFunction) 227 | make_func_name = block.name() +'::make' 228 | # Don't want to risk writing to output twice. 229 | if make_func_name not in make_funcs: 230 | make_funcs.add(make_func_name) 231 | output.update(make_block2_entry(di, block)) 232 | except block.ParsingError: 233 | sys.stderr.write('Parsing error for block {0}\n'.format(block.name())) 234 | raise 235 | 236 | # Create docstrings for functions 237 | # Don't include the make functions since they have already been dealt with. 238 | funcs = [f for f in di.in_category(DoxyFunction) 239 | if f.name() not in make_funcs and not f.name().startswith('std::')] 240 | for f in funcs: 241 | try: 242 | output.update(make_entry(f)) 243 | except f.ParsingError: 244 | sys.stderr.write('Parsing error for function {0}\n'.format(f.name())) 245 | 246 | # Create docstrings for classes 247 | block_names = [block.name() for block in blocks] 248 | block_names += [block.name() for block in blocks2] 249 | klasses = [k for k in di.in_category(DoxyClass) 250 | if k.name() not in block_names and not k.name().startswith('std::')] 251 | for k in klasses: 252 | try: 253 | output.update(make_class_entry(k)) 254 | except k.ParsingError: 255 | sys.stderr.write('Parsing error for class {0}\n'.format(k.name())) 256 | 257 | # Docstrings are not created for anything that is not a function or a class. 258 | # If this excludes anything important please add it here. 259 | 260 | return output 261 | 262 | def sub_docstring_in_pydoc_h(pydoc_files, docstrings_dict, output_dir, filter_str=None): 263 | if filter_str: 264 | docstrings_dict = {k: v for k, v in docstrings_dict.items() if k.startswith(filter_str)} 265 | 266 | with open(os.path.join(output_dir,'docstring_status'),'w') as status_file: 267 | 268 | for pydoc_file in pydoc_files: 269 | if filter_str: 270 | filter_str2 = "::".join((filter_str,os.path.split(pydoc_file)[-1].split('_pydoc_template.h')[0])) 271 | docstrings_dict2 = {k: v for k, v in docstrings_dict.items() if k.startswith(filter_str2)} 272 | else: 273 | docstrings_dict2 = docstrings_dict 274 | 275 | 276 | 277 | file_in = open(pydoc_file,'r').read() 278 | for key, value in docstrings_dict2.items(): 279 | file_in_tmp = file_in 280 | try: 281 | doc_key = key.split("::") 282 | # if 'gr' in doc_key: 283 | # doc_key.remove('gr') 284 | doc_key = '_'.join(doc_key) 285 | regexp = r'(__doc_{} =\sR\"doc\()[^)]*(\)doc\")'.format(doc_key) 286 | regexp = re.compile(regexp, re.MULTILINE) 287 | 288 | (file_in, nsubs) = regexp.subn(r'\1'+value+r'\2', file_in, count=1) 289 | if nsubs == 1: 290 | status_file.write("PASS: " + pydoc_file + "\n") 291 | except KeyboardInterrupt: 292 | raise KeyboardInterrupt 293 | except: # be permissive, TODO log, but just leave the docstring blank 294 | status_file.write("FAIL: " + pydoc_file + "\n") 295 | file_in = file_in_tmp 296 | 297 | output_pathname = os.path.join(output_dir, os.path.basename(pydoc_file).replace('_template.h','.h')) 298 | # FIXME: Remove this debug print 299 | print('output docstrings to {}'.format(output_pathname)) 300 | with open(output_pathname,'w') as file_out: 301 | file_out.write(file_in) 302 | 303 | def copy_docstring_templates(pydoc_files, output_dir): 304 | with open(os.path.join(output_dir,'docstring_status'),'w') as status_file: 305 | for pydoc_file in pydoc_files: 306 | file_in = open(pydoc_file,'r').read() 307 | output_pathname = os.path.join(output_dir, os.path.basename(pydoc_file).replace('_template.h','.h')) 308 | # FIXME: Remove this debug print 309 | print('copy docstrings to {}'.format(output_pathname)) 310 | with open(output_pathname,'w') as file_out: 311 | file_out.write(file_in) 312 | status_file.write("DONE") 313 | 314 | def argParse(): 315 | """Parses commandline args.""" 316 | desc='Scrape the doxygen generated xml for docstrings to insert into python bindings' 317 | parser = ArgumentParser(description=desc) 318 | 319 | parser.add_argument("function", help="Operation to perform on docstrings", choices=["scrape","sub","copy"]) 320 | 321 | parser.add_argument("--xml_path") 322 | parser.add_argument("--bindings_dir") 323 | parser.add_argument("--output_dir") 324 | parser.add_argument("--json_path") 325 | parser.add_argument("--filter", default=None) 326 | 327 | return parser.parse_args() 328 | 329 | if __name__ == "__main__": 330 | # Parse command line options and set up doxyxml. 331 | args = argParse() 332 | if args.function.lower() == 'scrape': 333 | di = DoxyIndex(args.xml_path) 334 | docstrings_dict = get_docstrings_dict(di) 335 | with open(args.json_path, 'w') as fp: 336 | json.dump(docstrings_dict, fp) 337 | elif args.function.lower() == 'sub': 338 | with open(args.json_path, 'r') as fp: 339 | docstrings_dict = json.load(fp) 340 | pydoc_files = glob.glob(os.path.join(args.bindings_dir,'*_pydoc_template.h')) 341 | sub_docstring_in_pydoc_h(pydoc_files, docstrings_dict, args.output_dir, args.filter) 342 | elif args.function.lower() == 'copy': 343 | pydoc_files = glob.glob(os.path.join(args.bindings_dir,'*_pydoc_template.h')) 344 | copy_docstring_templates(pydoc_files, args.output_dir) 345 | 346 | 347 | -------------------------------------------------------------------------------- /docs/paintrx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drmpeg/gr-paint/38295f04bce73408aee4bb1272bc83016a4d2646/docs/paintrx.png -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | install(FILES DESTINATION share/gnuradio/examples/paint) 2 | -------------------------------------------------------------------------------- /examples/README: -------------------------------------------------------------------------------- 1 | It is considered good practice to add examples in here to demonstrate the 2 | functionality of your OOT module. Python scripts, GRC flow graphs or other 3 | code can go here. 4 | 5 | -------------------------------------------------------------------------------- /grc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2011 Free Software Foundation, Inc. 2 | # 3 | # This file was generated by gr_modtool, a tool from the GNU Radio framework 4 | # This file is a part of gr-paint 5 | # 6 | # SPDX-License-Identifier: GPL-3.0-or-later 7 | # 8 | 9 | install(FILES 10 | paint_paint_bc.block.yml 11 | paint_image_source.block.yml DESTINATION share/gnuradio/grc/blocks 12 | ) 13 | -------------------------------------------------------------------------------- /grc/paint_image_source.block.yml: -------------------------------------------------------------------------------- 1 | # auto-generated by grc.converter 2 | 3 | id: paint_image_source 4 | label: Image File Source 5 | category: '[Paint]' 6 | 7 | parameters: 8 | - id: image_file 9 | label: Image File 10 | dtype: file_open 11 | - id: image_flip 12 | label: Flip image? 13 | dtype: enum 14 | default: 'False' 15 | options: ['0', '1'] 16 | option_labels: ['No', 'Yes'] 17 | - id: bt709_map 18 | label: ITU-R BT.709 19 | dtype: enum 20 | default: 'True' 21 | options: ['1', '0'] 22 | option_labels: ['Yes', 'No'] 23 | - id: image_invert 24 | label: Invert brightness? 25 | dtype: enum 26 | default: 'False' 27 | options: ['0', '1'] 28 | option_labels: ['No', 'Yes'] 29 | - id: autocontrast 30 | label: '"Enhance" contrast?' 31 | dtype: enum 32 | default: 'False' 33 | options: ['0', '1'] 34 | option_labels: ['No', 'Yes'] 35 | - id: repeatmode 36 | label: Repeat 37 | dtype: enum 38 | default: 'Yes' 39 | options: ['0', '1', '2'] 40 | option_labels: ['No', 'Yes', 'Yes, with file re-read'] 41 | 42 | outputs: 43 | - domain: stream 44 | dtype: byte 45 | 46 | templates: 47 | imports: import paint 48 | make: paint.image_source(${image_file}, ${image_flip}, ${bt709_map}, ${image_invert}, 49 | ${autocontrast}, ${repeatmode}) 50 | 51 | file_format: 1 52 | -------------------------------------------------------------------------------- /grc/paint_paint_bc.block.yml: -------------------------------------------------------------------------------- 1 | # auto-generated by grc.converter 2 | 3 | id: paint_paint_bc 4 | label: Spectrum Painter 5 | category: '[Paint]' 6 | 7 | parameters: 8 | - id: width 9 | label: Image Width 10 | dtype: int 11 | default: '1920' 12 | - id: repeats 13 | label: Line Repeats 14 | dtype: int 15 | default: '8' 16 | - id: equalization 17 | label: Sin(x)/x Equalization 18 | dtype: enum 19 | options: [EQUALIZATION_OFF, EQUALIZATION_ON] 20 | option_labels: ['Off', 'On'] 21 | option_attributes: 22 | val: [paint.EQUALIZATION_OFF, paint.EQUALIZATION_ON] 23 | - id: randomsrc 24 | label: Random Source 25 | dtype: enum 26 | options: [INTERNAL, EXTERNAL] 27 | option_attributes: 28 | ports: [1, 2] 29 | val: [paint.INTERNAL, paint.EXTERNAL] 30 | hide: part 31 | 32 | inputs: 33 | - domain: stream 34 | dtype: byte 35 | multiplicity: ${ randomsrc.ports } 36 | 37 | outputs: 38 | - domain: stream 39 | dtype: complex 40 | asserts: 41 | - ${ width < 4097 } 42 | 43 | templates: 44 | imports: import paint 45 | make: paint.paint_bc(${width}, ${repeats}, ${equalization.val}, ${randomsrc.val}, 46 | ${randomsrc.ports}) 47 | 48 | file_format: 1 49 | -------------------------------------------------------------------------------- /include/paint/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2011,2012 Free Software Foundation, Inc. 2 | # 3 | # This file was generated by gr_modtool, a tool from the GNU Radio framework 4 | # This file is a part of gr-paint 5 | # 6 | # SPDX-License-Identifier: GPL-3.0-or-later 7 | # 8 | 9 | ######################################################################## 10 | # Install public header files 11 | ######################################################################## 12 | install(FILES 13 | api.h 14 | paint_config.h 15 | paint_bc.h DESTINATION include/paint 16 | ) 17 | -------------------------------------------------------------------------------- /include/paint/api.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Free Software Foundation, Inc. 3 | * 4 | * This file was generated by gr_modtool, a tool from the GNU Radio framework 5 | * This file is a part of gr-paint 6 | * 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | * 9 | */ 10 | 11 | #ifndef INCLUDED_PAINT_API_H 12 | #define INCLUDED_PAINT_API_H 13 | 14 | #include 15 | 16 | #ifdef gnuradio_paint_EXPORTS 17 | #define PAINT_API __GR_ATTR_EXPORT 18 | #else 19 | #define PAINT_API __GR_ATTR_IMPORT 20 | #endif 21 | 22 | #endif /* INCLUDED_PAINT_API_H */ 23 | -------------------------------------------------------------------------------- /include/paint/paint_bc.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2015 Ron Economos. 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | 22 | #ifndef INCLUDED_PAINT_PAINT_BC_H 23 | #define INCLUDED_PAINT_PAINT_BC_H 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | namespace gr { 30 | namespace paint { 31 | 32 | /*! 33 | * \brief <+description of block+> 34 | * \ingroup paint 35 | * 36 | */ 37 | class PAINT_API paint_bc : virtual public gr::block 38 | { 39 | public: 40 | typedef std::shared_ptr sptr; 41 | 42 | /*! 43 | * \brief Return a shared_ptr to a new instance of paint::paint_bc. 44 | * 45 | * To avoid accidental use of raw pointers, paint::paint_bc's 46 | * constructor is in a private implementation 47 | * class. paint::paint_bc::make is the public interface for 48 | * creating new instances. 49 | */ 50 | static sptr make(int width, int repeats, int equalization, int randomsrc, int inputs); 51 | }; 52 | 53 | } // namespace paint 54 | } // namespace gr 55 | 56 | #endif /* INCLUDED_PAINT_PAINT_BC_H */ 57 | 58 | -------------------------------------------------------------------------------- /include/paint/paint_config.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2015 Ron Economos. 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | #ifndef INCLUDED_PAINT_CONFIG_H 22 | #define INCLUDED_PAINT_CONFIG_H 23 | 24 | namespace gr { 25 | namespace paint { 26 | enum paint_random_t { 27 | INTERNAL = 0, 28 | EXTERNAL, 29 | }; 30 | 31 | enum paint_equalization_t { 32 | EQUALIZATION_OFF = 0, 33 | EQUALIZATION_ON, 34 | }; 35 | 36 | } // namespace paint 37 | } // namespace gr 38 | 39 | typedef gr::paint::paint_random_t paint_random_t; 40 | typedef gr::paint::paint_equalization_t paint_equalization_t; 41 | 42 | #endif /* INCLUDED_PAINT_CONFIG_H */ 43 | 44 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2011,2012,2016,2018,2019 Free Software Foundation, Inc. 2 | # 3 | # This file was generated by gr_modtool, a tool from the GNU Radio framework 4 | # This file is a part of gr-paint 5 | # 6 | # SPDX-License-Identifier: GPL-3.0-or-later 7 | # 8 | 9 | ######################################################################## 10 | # Setup library 11 | ######################################################################## 12 | include(GrPlatform) #define LIB_SUFFIX 13 | 14 | list(APPEND paint_sources 15 | paint_bc_impl.cc 16 | ) 17 | 18 | set(paint_sources "${paint_sources}" PARENT_SCOPE) 19 | if(NOT paint_sources) 20 | MESSAGE(STATUS "No C++ sources... skipping lib/") 21 | return() 22 | endif(NOT paint_sources) 23 | 24 | add_library(gnuradio-paint SHARED ${paint_sources}) 25 | target_link_libraries(gnuradio-paint gnuradio::gnuradio-runtime gnuradio::gnuradio-fft) 26 | target_include_directories(gnuradio-paint 27 | PUBLIC $ 28 | PUBLIC $ 29 | ) 30 | set_target_properties(gnuradio-paint PROPERTIES DEFINE_SYMBOL "gnuradio_paint_EXPORTS") 31 | 32 | if(APPLE) 33 | set_target_properties(gnuradio-paint PROPERTIES 34 | INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib" 35 | ) 36 | endif(APPLE) 37 | 38 | ######################################################################## 39 | # Install built library files 40 | ######################################################################## 41 | include(GrMiscUtils) 42 | GR_LIBRARY_FOO(gnuradio-paint) 43 | 44 | ######################################################################## 45 | # Print summary 46 | ######################################################################## 47 | message(STATUS "Using install prefix: ${CMAKE_INSTALL_PREFIX}") 48 | message(STATUS "Building for version: ${VERSION} / ${LIBVER}") 49 | 50 | ######################################################################## 51 | # Build and register unit test 52 | ######################################################################## 53 | include(GrTest) 54 | 55 | # If your unit tests require special include paths, add them here 56 | #include_directories() 57 | # List all files that contain Boost.UTF unit tests here 58 | list(APPEND test_paint_sources 59 | ) 60 | # Anything we need to link to for the unit tests go here 61 | list(APPEND GR_TEST_TARGET_DEPS gnuradio-paint) 62 | 63 | if(NOT test_paint_sources) 64 | MESSAGE(STATUS "No C++ unit tests... skipping") 65 | return() 66 | endif(NOT test_paint_sources) 67 | 68 | foreach(qa_file ${test_paint_sources}) 69 | GR_ADD_CPP_TEST("paint_${qa_file}" 70 | ${CMAKE_CURRENT_SOURCE_DIR}/${qa_file} 71 | ) 72 | endforeach(qa_file) 73 | -------------------------------------------------------------------------------- /lib/paint_bc_impl.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2015 Ron Economos. 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include "config.h" 23 | #endif 24 | 25 | #include 26 | #include "paint_bc_impl.h" 27 | #include 28 | #include 29 | 30 | namespace gr { 31 | namespace paint { 32 | 33 | paint_bc::sptr 34 | paint_bc::make(int width, int repeats, int equalization, int randomsrc, int inputs) 35 | { 36 | return gnuradio::get_initial_sptr 37 | (new paint_bc_impl(width, repeats, equalization, randomsrc, inputs)); 38 | } 39 | 40 | /* 41 | * The private constructor 42 | */ 43 | paint_bc_impl::paint_bc_impl(int width, int repeats, int equalization, int randomsrc, int inputs) 44 | : gr::block("paint_bc", 45 | gr::io_signature::make(inputs, inputs, sizeof(unsigned char)), 46 | gr::io_signature::make(1, 1, sizeof(gr_complex))), 47 | ofdm_fft(OFDM_FFT_SIZE, 1) 48 | { 49 | double x, sinc, fs = 2000000.0; 50 | double fstep, f = 0.0; 51 | line_repeat = repeats; 52 | image_width = width; 53 | random_source = randomsrc; 54 | equalization_enable = equalization; 55 | ofdm_fft_size = OFDM_FFT_SIZE; 56 | normalization = 0.000001; 57 | pixel_repeat = ofdm_fft_size / image_width; 58 | int nulls = ofdm_fft_size - (image_width * pixel_repeat); 59 | left_nulls = nulls / 2; 60 | right_nulls = nulls / 2; 61 | if (nulls % 2 == 1) 62 | { 63 | left_nulls++; 64 | } 65 | fstep = fs / ofdm_fft_size; 66 | for (int i = 0; i < ofdm_fft_size / 2; i++) 67 | { 68 | x = M_PI * f / fs; 69 | if (i == 0) 70 | { 71 | sinc = 1.0; 72 | } 73 | else 74 | { 75 | sinc = sin(x) / x; 76 | } 77 | inverse_sinc[i + (ofdm_fft_size / 2)] = gr_complex(1.0 / sinc, 0.0); 78 | inverse_sinc[(ofdm_fft_size / 2) - i - 1] = gr_complex(1.0 / sinc, 0.0); 79 | f = f + fstep; 80 | } 81 | set_output_multiple(ofdm_fft_size * line_repeat); 82 | } 83 | 84 | /* 85 | * Our virtual destructor. 86 | */ 87 | paint_bc_impl::~paint_bc_impl() 88 | { 89 | } 90 | 91 | void 92 | paint_bc_impl::forecast (int noutput_items, gr_vector_int &ninput_items_required) 93 | { 94 | ninput_items_required[0] = (noutput_items * (image_width / line_repeat)) / ofdm_fft_size; 95 | if (random_source == EXTERNAL) 96 | { 97 | ninput_items_required[1] = (noutput_items / ofdm_fft_size) * image_width * pixel_repeat; 98 | } 99 | } 100 | 101 | int 102 | paint_bc_impl::general_work (int noutput_items, 103 | gr_vector_int &ninput_items, 104 | gr_vector_const_void_star &input_items, 105 | gr_vector_void_star &output_items) 106 | { 107 | const unsigned char *in = (const unsigned char *) input_items[0]; 108 | const unsigned char *in_rand = nullptr; 109 | 110 | if (random_source == EXTERNAL) 111 | { 112 | in_rand = (const unsigned char *) input_items[1]; 113 | } 114 | 115 | gr_complex *out = (gr_complex *) output_items[0]; 116 | int consumed = 0; 117 | int consumed_rand = 0; 118 | int pixel_index; 119 | int angle_index; 120 | int pixels; 121 | float angle, magnitude; 122 | gr_complex zero; 123 | gr_complex *dst; 124 | 125 | zero = gr_complex(0.0, 0.0); 126 | 127 | for (int i = 0; i < noutput_items; i += (ofdm_fft_size * line_repeat)) 128 | { 129 | pixel_index = 0; 130 | for (int j = 0; j < image_width; j++) 131 | { 132 | magnitude = in[consumed++]; 133 | magnitude += 256.0; 134 | magnitude = pow(magnitude, 5.0); 135 | magnitude /= 10000000000.0; 136 | for (int k = 0; k < pixel_repeat; k++) 137 | { 138 | magnitude_line[pixel_index++] = magnitude; 139 | } 140 | } 141 | for (int lrepeat = 0; lrepeat < line_repeat; lrepeat++) 142 | { 143 | for (int j = 0; j < left_nulls; j++) 144 | { 145 | *out++ = zero; 146 | } 147 | angle_index = 0; 148 | for (int j = 0; j < image_width; j++) 149 | { 150 | for (int prepeat = 0; prepeat < pixel_repeat; prepeat++) 151 | { 152 | if (random_source == INTERNAL) 153 | { 154 | angle = rand(); 155 | angle = angle - (RAND_MAX / 2); 156 | angle_line[angle_index++] = angle * M_PI / (RAND_MAX / 2); 157 | } 158 | else 159 | { 160 | angle = (in_rand[consumed_rand++] << 1) + 1; 161 | angle_line[angle_index++] = angle * M_PI / 4.0; 162 | } 163 | } 164 | } 165 | pixels = image_width * pixel_repeat; 166 | volk_32f_cos_32f(angle_cos, angle_line, pixels); 167 | volk_32f_sin_32f(angle_sin, angle_line, pixels); 168 | volk_32f_x2_multiply_32f(angle_cos, angle_cos, magnitude_line, pixels); 169 | volk_32f_x2_multiply_32f(angle_sin, angle_sin, magnitude_line, pixels); 170 | volk_32f_x2_interleave_32fc(out, angle_cos, angle_sin, pixels); 171 | out += pixels; 172 | for (int j = 0; j < right_nulls; j++) 173 | { 174 | *out++ = zero; 175 | } 176 | out -= ofdm_fft_size; 177 | if (equalization_enable == EQUALIZATION_ON) 178 | { 179 | volk_32fc_x2_multiply_32fc(out, out, inverse_sinc, ofdm_fft_size); 180 | } 181 | dst = ofdm_fft.get_inbuf(); 182 | memcpy(&dst[ofdm_fft_size / 2], &out[0], sizeof(gr_complex) * ofdm_fft_size / 2); 183 | memcpy(&dst[0], &out[ofdm_fft_size / 2], sizeof(gr_complex) * ofdm_fft_size / 2); 184 | ofdm_fft.execute(); 185 | volk_32f_s32f_multiply_32f(reinterpret_cast(out), reinterpret_cast(ofdm_fft.get_outbuf()), normalization, ofdm_fft_size * 2); 186 | out += ofdm_fft_size; 187 | } 188 | } 189 | // Tell runtime system how many input items we consumed on 190 | // each input stream. 191 | consume (0, consumed); 192 | if (random_source == EXTERNAL) 193 | { 194 | consume (1, consumed_rand); 195 | } 196 | 197 | // Tell runtime system how many output items we produced. 198 | return noutput_items; 199 | } 200 | 201 | } /* namespace paint */ 202 | } /* namespace gr */ 203 | 204 | -------------------------------------------------------------------------------- /lib/paint_bc_impl.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2015 Ron Economos. 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | #ifndef INCLUDED_PAINT_PAINT_BC_IMPL_H 22 | #define INCLUDED_PAINT_PAINT_BC_IMPL_H 23 | 24 | #include 25 | #include 26 | 27 | #define OFDM_FFT_SIZE 4096 28 | 29 | namespace gr { 30 | namespace paint { 31 | 32 | class paint_bc_impl : public paint_bc 33 | { 34 | private: 35 | int image_width; 36 | int line_repeat; 37 | int pixel_repeat; 38 | int left_nulls; 39 | int right_nulls; 40 | int random_source; 41 | int equalization_enable; 42 | gr_complex m_point[1]; 43 | fft::fft_complex_rev ofdm_fft; 44 | int ofdm_fft_size; 45 | float normalization; 46 | float magnitude_line[OFDM_FFT_SIZE]; 47 | float angle_line[OFDM_FFT_SIZE]; 48 | float angle_cos[OFDM_FFT_SIZE]; 49 | float angle_sin[OFDM_FFT_SIZE]; 50 | gr_complex inverse_sinc[OFDM_FFT_SIZE]; 51 | 52 | public: 53 | paint_bc_impl(int width, int repeats, int equalization, int randomsrc, int inputs); 54 | ~paint_bc_impl(); 55 | 56 | // Where all the action really happens 57 | void forecast (int noutput_items, gr_vector_int &ninput_items_required); 58 | 59 | int general_work(int noutput_items, 60 | gr_vector_int &ninput_items, 61 | gr_vector_const_void_star &input_items, 62 | gr_vector_void_star &output_items); 63 | }; 64 | 65 | } // namespace paint 66 | } // namespace gr 67 | 68 | #endif /* INCLUDED_PAINT_PAINT_BC_IMPL_H */ 69 | 70 | -------------------------------------------------------------------------------- /python/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2011 Free Software Foundation, Inc. 2 | # 3 | # This file was generated by gr_modtool, a tool from the GNU Radio framework 4 | # This file is a part of gr-paint 5 | # 6 | # SPDX-License-Identifier: GPL-3.0-or-later 7 | # 8 | 9 | ######################################################################## 10 | # Include python install macros 11 | ######################################################################## 12 | include(GrPython) 13 | if(NOT PYTHONINTERP_FOUND) 14 | return() 15 | endif() 16 | 17 | add_subdirectory(bindings) 18 | 19 | ######################################################################## 20 | # Install python sources 21 | ######################################################################## 22 | GR_PYTHON_INSTALL( 23 | FILES 24 | __init__.py 25 | image_source.py DESTINATION ${GR_PYTHON_DIR}/paint 26 | ) 27 | 28 | ######################################################################## 29 | # Handle the unit tests 30 | ######################################################################## 31 | include(GrTest) 32 | 33 | set(GR_TEST_TARGET_DEPS gnuradio-paint) 34 | -------------------------------------------------------------------------------- /python/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2008,2009 Free Software Foundation, Inc. 3 | # 4 | # SPDX-License-Identifier: GPL-3.0-or-later 5 | # 6 | 7 | # The presence of this file turns this directory into a Python package 8 | 9 | ''' 10 | This is the GNU Radio PAINT module. Place your Python package 11 | description here (python/__init__.py). 12 | ''' 13 | import os 14 | 15 | # import pybind11 generated symbols into the paint namespace 16 | try: 17 | # this might fail if the module is python-only 18 | from .paint_python import * 19 | except ModuleNotFoundError: 20 | pass 21 | 22 | # import any pure python here 23 | from .image_source import image_source 24 | # 25 | -------------------------------------------------------------------------------- /python/bindings/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Free Software Foundation, Inc. 2 | # 3 | # This file is part of GNU Radio 4 | # 5 | # SPDX-License-Identifier: GPL-3.0-or-later 6 | # 7 | 8 | ######################################################################## 9 | # Check if there is C++ code at all 10 | ######################################################################## 11 | if(NOT paint_sources) 12 | MESSAGE(STATUS "No C++ sources... skipping python bindings") 13 | return() 14 | endif(NOT paint_sources) 15 | 16 | ######################################################################## 17 | # Check for pygccxml 18 | ######################################################################## 19 | GR_PYTHON_CHECK_MODULE_RAW( 20 | "pygccxml" 21 | "import pygccxml" 22 | PYGCCXML_FOUND 23 | ) 24 | 25 | include(GrPybind) 26 | 27 | ######################################################################## 28 | # Python Bindings 29 | ######################################################################## 30 | 31 | list(APPEND paint_python_files 32 | paint_bc_python.cc paint_config_python.cc python_bindings.cc) 33 | 34 | GR_PYBIND_MAKE_OOT(paint 35 | ../.. 36 | gr::paint 37 | "${paint_python_files}") 38 | 39 | install(TARGETS paint_python DESTINATION ${GR_PYTHON_DIR}/paint COMPONENT pythonapi) 40 | -------------------------------------------------------------------------------- /python/bindings/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drmpeg/gr-paint/38295f04bce73408aee4bb1272bc83016a4d2646/python/bindings/README.md -------------------------------------------------------------------------------- /python/bindings/bind_oot_file.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | import argparse 3 | import os 4 | from gnuradio.bindtool import BindingGenerator 5 | import pathlib 6 | import sys 7 | 8 | parser = argparse.ArgumentParser(description='Bind a GR Out of Tree Block') 9 | parser.add_argument('--module', type=str, 10 | help='Name of gr module containing file to bind (e.g. fft digital analog)') 11 | 12 | parser.add_argument('--output_dir', default='/tmp', 13 | help='Output directory of generated bindings') 14 | parser.add_argument('--prefix', help='Prefix of Installed GNU Radio') 15 | parser.add_argument('--src', help='Directory of gnuradio source tree', 16 | default=os.path.dirname(os.path.abspath(__file__))+'/../../..') 17 | 18 | parser.add_argument( 19 | '--filename', help="File to be parsed") 20 | 21 | parser.add_argument( 22 | '--defines', help='Set additional defines for precompiler',default=(), nargs='*') 23 | parser.add_argument( 24 | '--include', help='Additional Include Dirs, separated', default=(), nargs='*') 25 | 26 | parser.add_argument( 27 | '--status', help='Location of output file for general status (used during cmake)', default=None 28 | ) 29 | parser.add_argument( 30 | '--flag_automatic', default='0' 31 | ) 32 | parser.add_argument( 33 | '--flag_pygccxml', default='0' 34 | ) 35 | 36 | args = parser.parse_args() 37 | 38 | prefix = args.prefix 39 | output_dir = args.output_dir 40 | defines = tuple(','.join(args.defines).split(',')) 41 | includes = ','.join(args.include) 42 | name = args.module 43 | 44 | namespace = ['gr', name] 45 | prefix_include_root = name 46 | 47 | 48 | with warnings.catch_warnings(): 49 | warnings.filterwarnings("ignore", category=DeprecationWarning) 50 | 51 | bg = BindingGenerator(prefix, namespace, 52 | prefix_include_root, output_dir, define_symbols=defines, addl_includes=includes, 53 | catch_exceptions=False, write_json_output=False, status_output=args.status, 54 | flag_automatic=True if args.flag_automatic.lower() in [ 55 | '1', 'true'] else False, 56 | flag_pygccxml=True if args.flag_pygccxml.lower() in ['1', 'true'] else False) 57 | bg.gen_file_binding(args.filename) 58 | -------------------------------------------------------------------------------- /python/bindings/docstrings/README.md: -------------------------------------------------------------------------------- 1 | This directory stores templates for docstrings that are scraped from the include header files for each block -------------------------------------------------------------------------------- /python/bindings/docstrings/paint_bc_pydoc_template.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Free Software Foundation, Inc. 3 | * 4 | * This file is part of GNU Radio 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | #include "pydoc_macros.h" 10 | #define D(...) DOC(gr,paint, __VA_ARGS__ ) 11 | /* 12 | This file contains placeholders for docstrings for the Python bindings. 13 | Do not edit! These were automatically extracted during the binding process 14 | and will be overwritten during the build process 15 | */ 16 | 17 | 18 | 19 | static const char *__doc_gr_paint_paint_bc = R"doc()doc"; 20 | 21 | 22 | static const char *__doc_gr_paint_paint_bc_paint_bc = R"doc()doc"; 23 | 24 | 25 | static const char *__doc_gr_paint_paint_bc_make = R"doc()doc"; 26 | 27 | 28 | -------------------------------------------------------------------------------- /python/bindings/docstrings/paint_config_pydoc_template.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Free Software Foundation, Inc. 3 | * 4 | * This file is part of GNU Radio 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | #include "pydoc_macros.h" 10 | #define D(...) DOC(gr,paint, __VA_ARGS__ ) 11 | /* 12 | This file contains placeholders for docstrings for the Python bindings. 13 | Do not edit! These were automatically extracted during the binding process 14 | and will be overwritten during the build process 15 | */ 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /python/bindings/header_utils.py: -------------------------------------------------------------------------------- 1 | # Utilities for reading values in header files 2 | 3 | from argparse import ArgumentParser 4 | import re 5 | 6 | 7 | class PybindHeaderParser: 8 | def __init__(self, pathname): 9 | with open(pathname,'r') as f: 10 | self.file_txt = f.read() 11 | 12 | def get_flag_automatic(self): 13 | # p = re.compile(r'BINDTOOL_GEN_AUTOMATIC\(([^\s])\)') 14 | # m = p.search(self.file_txt) 15 | m = re.search(r'BINDTOOL_GEN_AUTOMATIC\(([^\s])\)', self.file_txt) 16 | if (m and m.group(1) == '1'): 17 | return True 18 | else: 19 | return False 20 | 21 | def get_flag_pygccxml(self): 22 | # p = re.compile(r'BINDTOOL_USE_PYGCCXML\(([^\s])\)') 23 | # m = p.search(self.file_txt) 24 | m = re.search(r'BINDTOOL_USE_PYGCCXML\(([^\s])\)', self.file_txt) 25 | if (m and m.group(1) == '1'): 26 | return True 27 | else: 28 | return False 29 | 30 | def get_header_filename(self): 31 | # p = re.compile(r'BINDTOOL_HEADER_FILE\(([^\s]*)\)') 32 | # m = p.search(self.file_txt) 33 | m = re.search(r'BINDTOOL_HEADER_FILE\(([^\s]*)\)', self.file_txt) 34 | if (m): 35 | return m.group(1) 36 | else: 37 | return None 38 | 39 | def get_header_file_hash(self): 40 | # p = re.compile(r'BINDTOOL_HEADER_FILE_HASH\(([^\s]*)\)') 41 | # m = p.search(self.file_txt) 42 | m = re.search(r'BINDTOOL_HEADER_FILE_HASH\(([^\s]*)\)', self.file_txt) 43 | if (m): 44 | return m.group(1) 45 | else: 46 | return None 47 | 48 | def get_flags(self): 49 | return f'{self.get_flag_automatic()};{self.get_flag_pygccxml()};{self.get_header_filename()};{self.get_header_file_hash()};' 50 | 51 | 52 | 53 | def argParse(): 54 | """Parses commandline args.""" 55 | desc='Reads the parameters from the comment block in the pybind files' 56 | parser = ArgumentParser(description=desc) 57 | 58 | parser.add_argument("function", help="Operation to perform on comment block of pybind file", choices=["flag_auto","flag_pygccxml","header_filename","header_file_hash","all"]) 59 | parser.add_argument("pathname", help="Pathname of pybind c++ file to read, e.g. blockname_python.cc") 60 | 61 | return parser.parse_args() 62 | 63 | if __name__ == "__main__": 64 | # Parse command line options and set up doxyxml. 65 | args = argParse() 66 | 67 | pbhp = PybindHeaderParser(args.pathname) 68 | 69 | if args.function == "flag_auto": 70 | print(pbhp.get_flag_automatic()) 71 | elif args.function == "flag_pygccxml": 72 | print(pbhp.get_flag_pygccxml()) 73 | elif args.function == "header_filename": 74 | print(pbhp.get_header_filename()) 75 | elif args.function == "header_file_hash": 76 | print(pbhp.get_header_file_hash()) 77 | elif args.function == "all": 78 | print(pbhp.get_flags()) -------------------------------------------------------------------------------- /python/bindings/paint_bc_python.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Free Software Foundation, Inc. 3 | * 4 | * This file is part of GNU Radio 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | /***********************************************************************************/ 11 | /* This file is automatically generated using bindtool and can be manually edited */ 12 | /* The following lines can be configured to regenerate this file during cmake */ 13 | /* If manual edits are made, the following tags should be modified accordingly. */ 14 | /* BINDTOOL_GEN_AUTOMATIC(0) */ 15 | /* BINDTOOL_USE_PYGCCXML(0) */ 16 | /* BINDTOOL_HEADER_FILE(paint_bc.h) */ 17 | /* BINDTOOL_HEADER_FILE_HASH(c4a81272683bd6a0b50cadaed93b5e45) */ 18 | /***********************************************************************************/ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | namespace py = pybind11; 25 | 26 | #include 27 | // pydoc.h is automatically generated in the build directory 28 | #include 29 | 30 | void bind_paint_bc(py::module& m) 31 | { 32 | 33 | using paint_bc = ::gr::paint::paint_bc; 34 | 35 | 36 | py::class_>(m, "paint_bc", D(paint_bc)) 38 | 39 | .def(py::init(&paint_bc::make), 40 | py::arg("width"), 41 | py::arg("repeats"), 42 | py::arg("equalization"), 43 | py::arg("randomsrc"), 44 | py::arg("inputs"), 45 | D(paint_bc,make) 46 | ) 47 | 48 | 49 | 50 | 51 | ; 52 | 53 | 54 | 55 | 56 | } 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /python/bindings/paint_config_python.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Free Software Foundation, Inc. 3 | * 4 | * This file is part of GNU Radio 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | /***********************************************************************************/ 11 | /* This file is automatically generated using bindtool and can be manually edited */ 12 | /* The following lines can be configured to regenerate this file during cmake */ 13 | /* If manual edits are made, the following tags should be modified accordingly. */ 14 | /* BINDTOOL_GEN_AUTOMATIC(0) */ 15 | /* BINDTOOL_USE_PYGCCXML(0) */ 16 | /* BINDTOOL_HEADER_FILE(paint_config.h) */ 17 | /* BINDTOOL_HEADER_FILE_HASH(6853f2df5fbfc8036dfcce239e2e982a) */ 18 | /***********************************************************************************/ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | namespace py = pybind11; 25 | 26 | #include 27 | // pydoc.h is automatically generated in the build directory 28 | #include 29 | 30 | void bind_paint_config(py::module& m) 31 | { 32 | 33 | 34 | py::enum_<::gr::paint::paint_random_t>(m,"paint_random_t") 35 | .value("INTERNAL", ::gr::paint::INTERNAL) // 0 36 | .value("EXTERNAL", ::gr::paint::EXTERNAL) // 1 37 | .export_values() 38 | ; 39 | 40 | py::implicitly_convertible(); 41 | py::enum_<::gr::paint::paint_equalization_t>(m,"paint_equalization_t") 42 | .value("EQUALIZATION_OFF", ::gr::paint::EQUALIZATION_OFF) // 0 43 | .value("EQUALIZATION_ON", ::gr::paint::EQUALIZATION_ON) // 1 44 | .export_values() 45 | ; 46 | 47 | py::implicitly_convertible(); 48 | 49 | 50 | 51 | } 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /python/bindings/python_bindings.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Free Software Foundation, Inc. 3 | * 4 | * This file is part of GNU Radio 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | */ 9 | 10 | #include 11 | 12 | #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION 13 | #include 14 | 15 | namespace py = pybind11; 16 | 17 | // Headers for binding functions 18 | /**************************************/ 19 | /* The following comment block is used for 20 | /* gr_modtool to insert function prototypes 21 | /* Please do not delete 22 | /**************************************/ 23 | // BINDING_FUNCTION_PROTOTYPES( 24 | void bind_paint_bc(py::module& m); 25 | void bind_paint_config(py::module& m); 26 | // ) END BINDING_FUNCTION_PROTOTYPES 27 | 28 | 29 | // We need this hack because import_array() returns NULL 30 | // for newer Python versions. 31 | // This function is also necessary because it ensures access to the C API 32 | // and removes a warning. 33 | void* init_numpy() 34 | { 35 | import_array(); 36 | return NULL; 37 | } 38 | 39 | PYBIND11_MODULE(paint_python, m) 40 | { 41 | // Initialize the numpy C API 42 | // (otherwise we will see segmentation faults) 43 | init_numpy(); 44 | 45 | // Allow access to base block methods 46 | py::module::import("gnuradio.gr"); 47 | 48 | /**************************************/ 49 | /* The following comment block is used for 50 | /* gr_modtool to insert binding function calls 51 | /* Please do not delete 52 | /**************************************/ 53 | // BINDING_FUNCTION_CALLS( 54 | bind_paint_bc(m); 55 | bind_paint_config(m); 56 | // ) END BINDING_FUNCTION_CALLS 57 | } 58 | -------------------------------------------------------------------------------- /python/image_source.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # vim: tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab: 3 | # -*- coding: utf-8 -*- 4 | # 5 | # Copyright 2015,2016 Chris Kuethe 6 | # 7 | # This is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3, or (at your option) 10 | # any later version. 11 | # 12 | # This software is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this software; see the file COPYING. If not, write to 19 | # the Free Software Foundation, Inc., 51 Franklin Street, 20 | # Boston, MA 02110-1301, USA. 21 | # 22 | 23 | import numpy 24 | from gnuradio import gr 25 | from PIL import Image 26 | from PIL import ImageOps 27 | import pmt 28 | 29 | class image_source(gr.sync_block): 30 | """ 31 | Given an image file readable by Python-Imaging, this block produces 32 | monochrome lines suitable for input to spectrum_paint 33 | """ 34 | 35 | image_file = None 36 | image_flip = False 37 | bt709_map = True 38 | image_invert = False 39 | autocontrast = False 40 | repeatmode = 1 41 | image_data = None 42 | eof = False 43 | 44 | def __init__(self, image_file, image_flip=False, bt709_map=True, image_invert=False, autocontrast=False, repeatmode=1): 45 | gr.sync_block.__init__(self, 46 | name="image_source", 47 | in_sig=None, 48 | out_sig=[numpy.uint8]) 49 | 50 | self.image_file = image_file 51 | self.image_flip = image_flip 52 | self.bt709_map = bt709_map 53 | self.image_invert = image_invert 54 | self.autocontrast = autocontrast 55 | self.repeatmode = repeatmode 56 | 57 | self.load_image() 58 | 59 | def load_image(self): 60 | """decode the image into a buffer""" 61 | self.image_data = Image.open(self.image_file) 62 | self.image_data = ImageOps.grayscale(self.image_data) 63 | 64 | if self.autocontrast: 65 | # may or may not improve the look of the transmitted spectrum 66 | self.image_data = ImageOps.autocontrast(self.image_data) 67 | 68 | if self.image_invert: 69 | # may or may not improve the look of the transmitted spectrum 70 | self.image_data = ImageOps.invert(self.image_data) 71 | 72 | if self.image_flip: 73 | # set to true for waterfalls that scroll from the top 74 | self.image_data = ImageOps.flip(self.image_data) 75 | 76 | (self.image_width, self.image_height) = self.image_data.size 77 | max_width = 4096.0 78 | if self.image_width > max_width: 79 | scaling = max_width / self.image_width 80 | newsize = (int(self.image_width * scaling), int(self.image_height * scaling)) 81 | (self.image_width, self.image_height) = newsize 82 | self.image_data = self.image_data.resize(newsize) 83 | self.set_output_multiple(self.image_width) 84 | 85 | self.image_data = list(self.image_data.getdata()) 86 | if self.bt709_map: 87 | # scale brightness according to ITU-R BT.709 88 | self.image_data = map( lambda x: x * 219 / 255 + 16, self.image_data) 89 | self.image_data = list(self.image_data) 90 | self.image_len = len(self.image_data) 91 | if self.repeatmode != 2: 92 | print ("paint.image_source: %d bytes, %dpx width" % (self.image_len, self.image_width)) 93 | self.line_num = 0 94 | 95 | def work(self, input_items, output_items): 96 | if self.eof: 97 | return -1 98 | out = output_items[0] 99 | self.add_item_tag(0, self.nitems_written(0), pmt.intern("image_width"), pmt.from_long(self.image_width)) 100 | self.add_item_tag(0, self.nitems_written(0), pmt.intern("line_num"), pmt.from_long(self.line_num)) 101 | out[:self.image_width] = self.image_data[self.image_width*self.line_num: self.image_width*(1+self.line_num)] 102 | 103 | self.line_num += 1 104 | if self.line_num >= self.image_height: 105 | self.line_num = 0 106 | if self.repeatmode == 0: 107 | self.eof = True 108 | if self.repeatmode == 2: 109 | self.load_image() 110 | return self.image_width 111 | -------------------------------------------------------------------------------- /tgatoluma.c: -------------------------------------------------------------------------------- 1 | /* -*- c -*- */ 2 | /* 3 | * Copyright 2015 Ron Economos. 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #define TRUE 1 25 | #define FALSE 0 26 | 27 | int main(int argc, char **argv) 28 | { 29 | FILE *fp; 30 | FILE *fpout; 31 | int length, i, j, horz, vert, index = 0; 32 | int vertical_flip = TRUE; 33 | int black_white; 34 | int image_ident; 35 | int bits_pixel; 36 | int r, g, b, alpha; 37 | double y; 38 | double cr, cg, cb; 39 | unsigned char *yp; 40 | int matrix_coefficients = 1; 41 | static unsigned char buffer_header[18]; 42 | unsigned char *buffer_ident; 43 | unsigned char *buffer_444Y; 44 | unsigned char *buffer_444input; 45 | static double coef[8][3] = { 46 | {0.2126, 0.7152, 0.0722}, /* ITU-R Rec. 709 */ 47 | {0.299, 0.587, 0.114}, /* unspecified */ 48 | {0.299, 0.587, 0.114}, /* reserved */ 49 | {0.30, 0.59, 0.11}, /* FCC */ 50 | {0.299, 0.587, 0.114}, /* ITU-R Rec. 624-4 System B, G */ 51 | {0.299, 0.587, 0.114}, /* SMPTE 170M */ 52 | {0.212, 0.701, 0.087}, /* SMPTE 240M (1987) */ 53 | {0.2627, 0.678, 0.0593}}; /* ITU-R BT.2020-1 */ 54 | 55 | if (argc != 3) { 56 | fprintf(stderr, "usage: tgatoluma \n"); 57 | exit(-1); 58 | } 59 | 60 | /*--- open binary file (for reading) ---*/ 61 | fp = fopen(argv[1], "rb"); 62 | if (fp == 0) { 63 | fprintf(stderr, "Cannot open input file <%s>\n", argv[1]); 64 | exit(-1); 65 | } 66 | 67 | /*--- open binary file (for writing) ---*/ 68 | fpout = fopen(argv[2], "wb"); 69 | if (fpout == 0) { 70 | fprintf(stderr, "Cannot open output file <%s>\n", argv[2]); 71 | exit(-1); 72 | } 73 | 74 | length = fread(&buffer_header[0], 1, 18, fp); 75 | if (length != 18) { 76 | fprintf(stderr, "Length Error reading input file <%s>\n", argv[1]); 77 | exit(-1); 78 | } 79 | 80 | image_ident = buffer_header[0]; 81 | bits_pixel = buffer_header[16]; 82 | 83 | horz = buffer_header[13] << 8; 84 | horz |= buffer_header[12]; 85 | vert = buffer_header[15] << 8; 86 | vert |= buffer_header[14]; 87 | printf("horz = %d, vert = %d\n", horz, vert); 88 | 89 | if (buffer_header[17] & 0x20) 90 | { 91 | vertical_flip = FALSE; 92 | } 93 | 94 | if (image_ident != 0) { 95 | buffer_ident = (unsigned char *)malloc(image_ident); 96 | length = fread(&buffer_ident[0], 1, image_ident, fp); 97 | if (length != image_ident) { 98 | free(buffer_ident); 99 | fprintf(stderr, "Length Error reading input file <%s>\n", argv[1]); 100 | exit(-1); 101 | } 102 | free(buffer_ident); 103 | } 104 | 105 | if (buffer_header[2] == 0x3) 106 | { 107 | black_white = TRUE; 108 | buffer_444input = (unsigned char *)malloc(horz * vert); 109 | length = fread(&buffer_444input[0], 1, (horz * vert), fp); 110 | if (length != (horz * vert)) { 111 | free(buffer_444input); 112 | fprintf(stderr, "Length Error reading input file <%s>\n", argv[1]); 113 | exit(-1); 114 | } 115 | } 116 | else 117 | { 118 | black_white = FALSE; 119 | if (bits_pixel == 32) { 120 | buffer_444input = (unsigned char *)malloc(horz * vert * 4); 121 | length = fread(&buffer_444input[0], 1, (horz * vert * 4), fp); 122 | if (length != (horz * vert * 4)) { 123 | free(buffer_444input); 124 | fprintf(stderr, "Length Error reading input file <%s>\n", argv[1]); 125 | exit(-1); 126 | } 127 | } 128 | else { 129 | buffer_444input = (unsigned char *)malloc(horz * vert * 3); 130 | length = fread(&buffer_444input[0], 1, (horz * vert * 3), fp); 131 | if (length != (horz * vert * 3)) { 132 | free(buffer_444input); 133 | fprintf(stderr, "Length Error reading input file <%s>\n", argv[1]); 134 | exit(-1); 135 | } 136 | } 137 | } 138 | 139 | buffer_444Y = (unsigned char *)malloc(horz * vert); 140 | 141 | i = matrix_coefficients; 142 | cr = coef[i-1][0]; 143 | cg = coef[i-1][1]; 144 | cb = coef[i-1][2]; 145 | 146 | if (black_white == TRUE) 147 | { 148 | if (vertical_flip == FALSE) 149 | { 150 | index = 0; 151 | } 152 | else 153 | { 154 | index = (horz * (vert - 1)); 155 | } 156 | 157 | for (i = 0; i < vert; i++) 158 | { 159 | yp = &buffer_444Y[0] + i * horz; 160 | 161 | for (j = 0 ; j < horz; j++) 162 | { 163 | /* convert to Y */ 164 | y = buffer_444input[index++]; 165 | yp[j] = (219.0 / 255.0) * y + 16.5; /* nominal range: 16..235 */ 166 | } 167 | if (vertical_flip) 168 | { 169 | index = index - (horz * 2); 170 | } 171 | } 172 | } 173 | else 174 | { 175 | if (vertical_flip == FALSE) 176 | { 177 | index = 0; 178 | } 179 | else 180 | { 181 | if (bits_pixel == 32) { 182 | index = ((horz * (vert - 1)) * 4); 183 | } 184 | else { 185 | index = ((horz * (vert - 1)) * 3); 186 | } 187 | } 188 | 189 | for (i = 0; i < vert; i++) 190 | { 191 | yp = &buffer_444Y[0] + i * horz; 192 | 193 | for (j = 0 ; j < horz; j++) 194 | { 195 | b = buffer_444input[index++]; 196 | g = buffer_444input[index++]; 197 | r = buffer_444input[index++]; 198 | if (bits_pixel == 32) { 199 | alpha = buffer_444input[index++]; 200 | if (alpha == 0) { 201 | r = g = b = 235; 202 | } 203 | } 204 | 205 | /* convert to Y */ 206 | y = cr * r + cg * g + cb * b; 207 | yp[j] = (219.0 / 255.0) * y + 16.5; /* nominal range: 16..235 */ 208 | } 209 | if (vertical_flip) 210 | { 211 | if (bits_pixel == 32) { 212 | index = index - (horz * 8); 213 | } 214 | else { 215 | index = index - (horz * 6); 216 | } 217 | } 218 | } 219 | } 220 | fwrite(&buffer_444Y[0], 1, (horz * vert), fpout); 221 | free(buffer_444Y); 222 | free(buffer_444input); 223 | fclose(fp); 224 | fclose(fpout); 225 | return 0; 226 | } 227 | 228 | --------------------------------------------------------------------------------