├── AUTHORS ├── CMakeLists.txt ├── COPYING ├── MANIFEST.md ├── README.md ├── apps └── CMakeLists.txt ├── cmake ├── Modules │ ├── FindGLEW.cmake │ ├── FindGLFW3.cmake │ ├── FindOpenCL.cmake │ └── targetConfig.cmake.in └── cmake_uninstall.cmake.in ├── docs ├── CMakeLists.txt └── 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 ├── grc ├── CMakeLists.txt ├── fosphor.tree.yml ├── fosphor_glfw_sink_c.block.yml ├── fosphor_qt_sink_c.block.yml └── overlap_cc.block.yml ├── include └── gnuradio │ └── fosphor │ ├── CMakeLists.txt │ ├── api.h │ ├── base_sink_c.h │ ├── glfw_sink_c.h │ ├── overlap_cc.h │ └── qt_sink_c.h ├── lib ├── CMakeLists.txt ├── QGLSurface.cc ├── QGLSurface.h ├── base_sink_c_impl.cc ├── base_sink_c_impl.h ├── fifo.cc ├── fifo.h ├── fosphor │ ├── AUTHORS │ ├── COPYING │ ├── DroidSansMonoDotted.ttf │ ├── DroidSansMonoDotted.txt │ ├── Makefile │ ├── axis.c │ ├── axis.h │ ├── cl.c │ ├── cl.h │ ├── cl_compat.c │ ├── cl_compat.h │ ├── cl_platform.h │ ├── cmap_bicubic.glsl │ ├── cmap_fallback.glsl │ ├── cmap_simple.glsl │ ├── display.cl │ ├── fft.cl │ ├── fosphor.c │ ├── fosphor.h │ ├── gl.c │ ├── gl.h │ ├── gl_cmap.c │ ├── gl_cmap.h │ ├── gl_cmap_gen.c │ ├── gl_cmap_gen.h │ ├── gl_font.c │ ├── gl_font.h │ ├── gl_platform.h │ ├── llist.h │ ├── main.c │ ├── mkresources.py │ ├── private.h │ ├── resource.c │ ├── resource.h │ └── resource_internal.h ├── glfw_sink_c_impl.cc ├── glfw_sink_c_impl.h ├── overlap_cc_impl.cc ├── overlap_cc_impl.h ├── qt_sink_c_impl.cc └── qt_sink_c_impl.h └── python ├── CMakeLists.txt ├── __init__.py └── bindings ├── CMakeLists.txt ├── base_sink_c_python.cc ├── glfw_sink_c_python.cc ├── overlap_cc_python.cc ├── python_bindings.cc └── qt_sink_c_python.cc /AUTHORS: -------------------------------------------------------------------------------- 1 | Sylvain Munaut 2 | Dimitri Stolnikov 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2011-2020 Free Software Foundation, Inc. 2 | # Copyright 2013-2021 Sylvain Munaut 3 | # 4 | # This file is part of gr-fosphor 5 | # 6 | # SPDX-License-Identifier: GPL-3.0-or-later 7 | 8 | ######################################################################## 9 | # Project setup 10 | ######################################################################## 11 | cmake_minimum_required(VERSION 3.8) 12 | project(gr-fosphor CXX C) 13 | include(GNUInstallDirs) 14 | enable_testing() 15 | 16 | #policy setup 17 | cmake_policy(SET CMP0011 NEW) 18 | cmake_policy(SET CMP0071 NEW) 19 | 20 | 21 | #select the release build type by default to get optimization flags 22 | if(NOT CMAKE_BUILD_TYPE) 23 | set(CMAKE_BUILD_TYPE "Release") 24 | message(STATUS "Build type not specified: defaulting to release.") 25 | endif(NOT CMAKE_BUILD_TYPE) 26 | set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "") 27 | 28 | ######################################################################## 29 | # GNURadio setup 30 | ######################################################################## 31 | list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake/Modules) 32 | 33 | # Find GNURadio 34 | find_package(Gnuradio "3.9" REQUIRED runtime fft) 35 | 36 | # Set the version information here 37 | set(VERSION_MAJOR 3) 38 | set(VERSION_API 9) 39 | set(VERSION_ABI 0) 40 | set(VERSION_PATCH git) 41 | include(GrVersion) #setup version info 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 | #false positives 52 | add_definitions(-Wno-format-overflow) 53 | endif() 54 | 55 | if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 56 | set(CMAKE_CXX_STANDARD 14) 57 | elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") 58 | set(CMAKE_CXX_STANDARD 14) 59 | elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 60 | set(CMAKE_CXX_STANDARD 14) 61 | else() 62 | message(WARNING "C++ standard could not be set because compiler is not GNU, Clang or MSVC.") 63 | endif() 64 | 65 | if(CMAKE_C_COMPILER_ID STREQUAL "GNU") 66 | set(CMAKE_C_STANDARD 11) 67 | elseif(CMAKE_C_COMPILER_ID MATCHES "Clang") 68 | set(CMAKE_C_STANDARD 11) 69 | elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC") 70 | set(CMAKE_C_STANDARD 11) 71 | else() 72 | message(WARNING "C standard could not be set because compiler is not GNU, Clang or MSVC.") 73 | endif() 74 | 75 | ######################################################################## 76 | # Install directories 77 | ######################################################################## 78 | include(GrPlatform) #define LIB_SUFFIX 79 | 80 | if(NOT CMAKE_MODULES_DIR) 81 | set(CMAKE_MODULES_DIR lib${LIB_SUFFIX}/cmake) 82 | endif(NOT CMAKE_MODULES_DIR) 83 | 84 | set(GR_INCLUDE_DIR include) 85 | set(GR_CMAKE_DIR ${CMAKE_MODULES_DIR}/gnuradio) 86 | set(GR_PKG_DATA_DIR ${GR_DATA_DIR}/${CMAKE_PROJECT_NAME}) 87 | set(GR_PKG_DOC_DIR ${GR_DOC_DIR}/${CMAKE_PROJECT_NAME}) 88 | set(GR_PKG_CONF_DIR ${GR_CONF_DIR}/${CMAKE_PROJECT_NAME}/conf.d) 89 | set(GR_PKG_LIBEXEC_DIR ${GR_LIBEXEC_DIR}/${CMAKE_PROJECT_NAME}) 90 | 91 | ######################################################################## 92 | # Find boost 93 | ######################################################################## 94 | find_package(Boost "1.65" COMPONENTS system chrono thread) 95 | 96 | if(NOT Boost_FOUND) 97 | message(FATAL_ERROR "Boost required to compile gr-fosphor") 98 | endif() 99 | 100 | ######################################################################## 101 | # Find gr-fosphor build dependencies 102 | ######################################################################## 103 | 104 | # Required 105 | set(OpenGL_GL_PREFERENCE "GLVND") 106 | find_package(OpenGL) 107 | if(NOT OPENGL_FOUND) 108 | message(FATAL_ERROR "OpenGL required to compile gr-fosphor") 109 | endif() 110 | 111 | find_package(OpenCL) 112 | if(NOT OpenCL_FOUND) 113 | message(FATAL_ERROR "OpenCL required to compile gr-fosphor") 114 | endif() 115 | 116 | find_package(Freetype) 117 | if(NOT FREETYPE_FOUND) 118 | message(FATAL_ERROR "freetype2 required to compile gr-fosphor") 119 | endif() 120 | 121 | # Optional 122 | find_package(GLFW3) 123 | 124 | set(Qt5_REQUIRED_COMPONENTS Core Gui OpenGL) 125 | find_package(Qt5 5.9.0 COMPONENTS ${Qt5_REQUIRED_COMPONENTS}) 126 | if (Qt5_FOUND) 127 | foreach(module ${Qt5_REQUIRED_COMPONENTS}) 128 | list(APPEND Qt5_INCLUDE_DIRS ${Qt5${module}_INCLUDE_DIRS}) 129 | list(APPEND Qt5_LIBRARIES ${Qt5${module}_LIBRARIES}) 130 | endforeach(module) 131 | endif (Qt5_FOUND) 132 | 133 | find_package(PNG 1.6.19) 134 | 135 | ######################################################################## 136 | # Find gnuradio build dependencies 137 | ######################################################################## 138 | 139 | find_package(Doxygen) 140 | find_package(PythonLibs 3) 141 | 142 | ######################################################################## 143 | # PyBind11 Related 144 | ######################################################################## 145 | 146 | find_package(pybind11 REQUIRED) 147 | 148 | if (pybind11_FOUND) 149 | execute_process( 150 | COMMAND "${PYTHON_EXECUTABLE}" -c 151 | "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" 152 | OUTPUT_VARIABLE PYTHON_NUMPY_INCLUDE_DIR) 153 | endif (pybind11_FOUND) 154 | 155 | ######################################################################## 156 | # Setup doxygen option 157 | ######################################################################## 158 | if(DOXYGEN_FOUND) 159 | option(ENABLE_DOXYGEN "Build docs using Doxygen" ON) 160 | else(DOXYGEN_FOUND) 161 | option(ENABLE_DOXYGEN "Build docs using Doxygen" OFF) 162 | endif(DOXYGEN_FOUND) 163 | 164 | ######################################################################## 165 | # Setup the components 166 | ######################################################################## 167 | 168 | include(GrComponent) 169 | 170 | GR_REGISTER_COMPONENT("PNG" ENABLE_PNG 171 | PNG_FOUND 172 | ) 173 | 174 | GR_REGISTER_COMPONENT("Python" ENABLE_PYTHON 175 | PYTHONLIBS_FOUND pybind11_FOUND 176 | ) 177 | 178 | GR_REGISTER_COMPONENT("GLFW" ENABLE_GLFW 179 | GLFW3_FOUND 180 | ) 181 | 182 | GR_REGISTER_COMPONENT("QT" ENABLE_QT 183 | Qt5_FOUND 184 | ) 185 | 186 | macro(list_cond_append cond list_name) 187 | if(${cond}) 188 | list(APPEND ${list_name} ${ARGN}) 189 | endif(${cond}) 190 | endmacro(list_cond_append) 191 | 192 | ######################################################################## 193 | # Create uninstall target 194 | ######################################################################## 195 | configure_file( 196 | ${CMAKE_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in 197 | ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake 198 | @ONLY) 199 | 200 | add_custom_target(uninstall 201 | ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake 202 | ) 203 | 204 | ######################################################################## 205 | # Add subdirectories 206 | ######################################################################## 207 | add_subdirectory(include/gnuradio/fosphor) 208 | add_subdirectory(lib) 209 | 210 | if(ENABLE_PYTHON) 211 | add_subdirectory(python) 212 | add_subdirectory(grc) 213 | add_subdirectory(apps) 214 | endif(ENABLE_PYTHON) 215 | 216 | add_subdirectory(docs) 217 | 218 | ######################################################################## 219 | # Print Summary 220 | ######################################################################## 221 | GR_PRINT_COMPONENT_SUMMARY() 222 | MESSAGE(STATUS "Using install prefix: ${CMAKE_INSTALL_PREFIX}") 223 | -------------------------------------------------------------------------------- /MANIFEST.md: -------------------------------------------------------------------------------- 1 | title: GR fosphor 2 | brief: GNU Radio block for RTSA-like spectrum visualization using OpenCL and OpenGL acceleration 3 | tags: 4 | - fft 5 | - gpu 6 | - opencl 7 | - opengl 8 | - visualization 9 | - osmocom 10 | author: 11 | - Sylvain Munaut 12 | copyright_owner: 13 | - Sylvain Munaut 14 | license: GPLv3 15 | gr_supported_version: 3.9 16 | repo: git://git.osmocom.org/gr-fosphor 17 | website: http://sdr.osmocom.org/trac/wiki/fosphor 18 | icon: http://people.osmocom.org/~tnt/stuff/fosphor-icon.png 19 | --- 20 | GNU Radio block for RTSA-like spectrum visualization using OpenCL and OpenGL acceleration 21 | 22 | A direct integration into `osmocom_fft` is available through the -F option. 23 | 24 | ![LTE](http://sdr.osmocom.org/trac/raw-attachment/wiki/GrOsmoSDR/fosphor.png "LTE") 25 | ![GSM](http://sdr.osmocom.org/trac/raw-attachment/wiki/GrOsmoSDR/fosphor2.png "GSM") 26 | 27 | It requires a working OpenCL driver, either CPU or GPU, to be installed. 28 | Please refer to the documentation on http://sdr.osmocom.org/trac/wiki/fosphor 29 | for installation instructions. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | gr-fosphor - gnuradio block for RTSA-like spectrum visualization 2 | ================================================================ 3 | 4 | This repository contains a GNU Radio block for RTSA-like spectrum visualization using OpenCL and OpenGL 5 | acceleration. 6 | 7 | 8 | The block has been used successfully on Linux, OSX and Windows. 9 | 10 | Homepage + Documentation 11 | ------------------------ 12 | 13 | For installation and usage guidelines please read the documentation available 14 | at 15 | 16 | Forum 17 | ----- 18 | 19 | We welcome any fosphor related discussions in the 20 | [SDR](https://discourse.osmocom.org/c/sdr/) 21 | section of the osmocom discourse (web based Forum). 22 | 23 | Mailing List 24 | ------------ 25 | 26 | Discussions related to libosmocore are happening on the 27 | osmocom-sdr@lists.osmocom.org mailing list, please see 28 | for subscription 29 | options and the list archive. 30 | 31 | Please observe the [Osmocom Mailing List 32 | Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules) 33 | when posting. 34 | 35 | Issue tracker 36 | ------------- 37 | 38 | We are using the Osmocom redmine at 39 | 40 | Contributing 41 | ------------ 42 | 43 | We maintain our source code in a self-hosted instance of gitea at 44 | . You can send pull requests there, or send 45 | patches the old-fashioned way (`git send-email`) to the above-mentioned mailing list. 46 | -------------------------------------------------------------------------------- /apps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2011-2020 Free Software Foundation, Inc. 2 | # Copyright 2013-2021 Sylvain Munaut 3 | # 4 | # This file is part of gr-fosphor 5 | # 6 | # SPDX-License-Identifier: GPL-3.0-or-later 7 | 8 | include(GrPython) 9 | 10 | GR_PYTHON_INSTALL( 11 | PROGRAMS 12 | DESTINATION bin 13 | ) 14 | -------------------------------------------------------------------------------- /cmake/Modules/FindGLEW.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # FindGLEW 3 | # -------- 4 | # 5 | # Find the OpenGL Extension Wrangler Library (GLEW) 6 | # 7 | # IMPORTED Targets 8 | # ^^^^^^^^^^^^^^^^ 9 | # 10 | # This module defines the :prop_tgt:`IMPORTED` target ``GLEW::GLEW``, 11 | # if GLEW has been found. 12 | # 13 | # Result Variables 14 | # ^^^^^^^^^^^^^^^^ 15 | # 16 | # This module defines the following variables: 17 | # 18 | # :: 19 | # 20 | # GLEW_INCLUDE_DIRS - include directories for GLEW 21 | # GLEW_LIBRARIES - libraries to link against GLEW 22 | # GLEW_FOUND - true if GLEW has been found and can be used 23 | 24 | #============================================================================= 25 | # Copyright 2012 Benjamin Eikel 26 | # 27 | # Distributed under the OSI-approved BSD License (the "License"); 28 | # see accompanying file Copyright.txt for details. 29 | # 30 | # This software is distributed WITHOUT ANY WARRANTY; without even the 31 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 32 | # See the License for more information. 33 | #============================================================================= 34 | # (To distribute this file outside of CMake, substitute the full 35 | # License text for the above reference.) 36 | 37 | find_path(GLEW_INCLUDE_DIR GL/glew.h) 38 | find_library(GLEW_LIBRARY NAMES GLEW glew64 glew32 glew glew32s PATH_SUFFIXES lib64 x86_64 x86 ) 39 | 40 | set(GLEW_INCLUDE_DIRS ${GLEW_INCLUDE_DIR}) 41 | set(GLEW_LIBRARIES ${GLEW_LIBRARY}) 42 | 43 | if(GLEW_FOUND AND NOT TARGET GLEW::GLEW) 44 | add_library(GLEW::GLEW UNKNOWN IMPORTED) 45 | set_target_properties(GLEW::GLEW PROPERTIES 46 | IMPORTED_LOCATION "${GLEW_LIBRARY}" 47 | INTERFACE_INCLUDE_DIRECTORIES "${GLEW_INCLUDE_DIRS}") 48 | endif() 49 | 50 | mark_as_advanced(GLEW_INCLUDE_DIR GLEW_LIBRARY) 51 | -------------------------------------------------------------------------------- /cmake/Modules/FindGLFW3.cmake: -------------------------------------------------------------------------------- 1 | if(NOT GLFW3_FOUND) 2 | INCLUDE(FindPkgConfig) 3 | pkg_check_modules(GLFW3_PKG glfw3) 4 | find_path(GLFW3_INCLUDE_DIRS 5 | NAMES GLFW/glfw3.h 6 | HINTS ${GLFW3_PKG_INCLUDE_DIRS} 7 | ) 8 | 9 | find_library(GLFW3_LIBRARIES 10 | NAMES ${GLFW3_PKG_LIBRARIES} glfw3 glfw 11 | HINTS ${GLFW3_PKG_LIBRARY_DIRS} 12 | ) 13 | 14 | if(GLFW3_INCLUDE_DIRS AND GLFW3_LIBRARIES) 15 | set(GLFW3_FOUND TRUE CACHE INTERNAL "GLFW3 found") 16 | message(STATUS "Found GLFW3: ${GLFW3_INCLUDE_DIRS}, ${GLFW3_LIBRARIES}") 17 | else(GLFW3_INCLUDE_DIRS AND GLFW3_LIBRARIES) 18 | set(GLFW3_FOUND FALSE CACHE INTERNAL "GLFW3 found") 19 | message(STATUS "GLFW3 not found.") 20 | endif(GLFW3_INCLUDE_DIRS AND GLFW3_LIBRARIES) 21 | 22 | mark_as_advanced(GLFW3_INCLUDE_DIRS GLFW3_LIBRARIES) 23 | 24 | endif(NOT GLFW3_FOUND) 25 | -------------------------------------------------------------------------------- /cmake/Modules/FindOpenCL.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # FindOpenCL 3 | # ---------- 4 | # 5 | # Try to find OpenCL 6 | # 7 | # Once done this will define:: 8 | # 9 | # OpenCL_FOUND - True if OpenCL was found 10 | # OpenCL_INCLUDE_DIRS - include directories for OpenCL 11 | # OpenCL_LIBRARIES - link against this library to use OpenCL 12 | # OpenCL_VERSION_STRING - Highest supported OpenCL version (eg. 1.2) 13 | # OpenCL_VERSION_MAJOR - The major version of the OpenCL implementation 14 | # OpenCL_VERSION_MINOR - The minor version of the OpenCL implementation 15 | # 16 | # The module will also define two cache variables:: 17 | # 18 | # OpenCL_INCLUDE_DIR - the OpenCL include directory 19 | # OpenCL_LIBRARY - the path to the OpenCL library 20 | # 21 | 22 | #============================================================================= 23 | # Copyright 2014 Matthaeus G. Chajdas 24 | # 25 | # Distributed under the OSI-approved BSD License (the "License"); 26 | # see accompanying file Copyright.txt for details. 27 | # 28 | # This software is distributed WITHOUT ANY WARRANTY; without even the 29 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 30 | # See the License for more information. 31 | #============================================================================= 32 | # (To distribute this file outside of CMake, substitute the full 33 | # License text for the above reference.) 34 | 35 | function(_FIND_OPENCL_VERSION) 36 | include(CheckSymbolExists) 37 | include(CMakePushCheckState) 38 | set(CMAKE_REQUIRED_QUIET ${OpenCL_FIND_QUIETLY}) 39 | 40 | CMAKE_PUSH_CHECK_STATE() 41 | foreach(VERSION "2_0" "1_2" "1_1" "1_0") 42 | set(CMAKE_REQUIRED_INCLUDES "${OpenCL_INCLUDE_DIR}") 43 | 44 | if(APPLE) 45 | CHECK_SYMBOL_EXISTS( 46 | CL_VERSION_${VERSION} 47 | "${OpenCL_INCLUDE_DIR}/OpenCL/cl.h" 48 | OPENCL_VERSION_${VERSION}) 49 | else() 50 | CHECK_SYMBOL_EXISTS( 51 | CL_VERSION_${VERSION} 52 | "${OpenCL_INCLUDE_DIR}/CL/cl.h" 53 | OPENCL_VERSION_${VERSION}) 54 | endif() 55 | 56 | if(OPENCL_VERSION_${VERSION}) 57 | string(REPLACE "_" "." VERSION "${VERSION}") 58 | set(OpenCL_VERSION_STRING ${VERSION} PARENT_SCOPE) 59 | string(REGEX MATCHALL "[0-9]+" version_components "${VERSION}") 60 | list(GET version_components 0 major_version) 61 | list(GET version_components 1 minor_version) 62 | set(OpenCL_VERSION_MAJOR ${major_version} PARENT_SCOPE) 63 | set(OpenCL_VERSION_MINOR ${minor_version} PARENT_SCOPE) 64 | break() 65 | endif() 66 | endforeach() 67 | CMAKE_POP_CHECK_STATE() 68 | endfunction() 69 | 70 | find_path(OpenCL_INCLUDE_DIR 71 | NAMES 72 | CL/cl.h OpenCL/cl.h 73 | PATHS 74 | ENV "PROGRAMFILES(X86)" 75 | ENV AMDAPPSDKROOT 76 | ENV INTELOCLSDKROOT 77 | ENV NVSDKCOMPUTE_ROOT 78 | ENV CUDA_PATH 79 | ENV ATISTREAMSDKROOT 80 | PATH_SUFFIXES 81 | include 82 | OpenCL/common/inc 83 | "AMD APP/include") 84 | 85 | _FIND_OPENCL_VERSION() 86 | 87 | if(WIN32) 88 | if(CMAKE_SIZEOF_VOID_P EQUAL 4) 89 | find_library(OpenCL_LIBRARY 90 | NAMES OpenCL 91 | PATHS 92 | ENV "PROGRAMFILES(X86)" 93 | ENV AMDAPPSDKROOT 94 | ENV INTELOCLSDKROOT 95 | ENV CUDA_PATH 96 | ENV NVSDKCOMPUTE_ROOT 97 | ENV ATISTREAMSDKROOT 98 | PATH_SUFFIXES 99 | "AMD APP/lib/x86" 100 | lib/x86 101 | lib/Win32 102 | OpenCL/common/lib/Win32) 103 | elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) 104 | find_library(OpenCL_LIBRARY 105 | NAMES OpenCL 106 | PATHS 107 | ENV "PROGRAMFILES(X86)" 108 | ENV AMDAPPSDKROOT 109 | ENV INTELOCLSDKROOT 110 | ENV CUDA_PATH 111 | ENV NVSDKCOMPUTE_ROOT 112 | ENV ATISTREAMSDKROOT 113 | PATH_SUFFIXES 114 | "AMD APP/lib/x86_64" 115 | lib/x86_64 116 | lib/x64 117 | OpenCL/common/lib/x64) 118 | endif() 119 | else() 120 | find_library(OpenCL_LIBRARY 121 | NAMES OpenCL) 122 | endif() 123 | 124 | set(OpenCL_LIBRARIES ${OpenCL_LIBRARY}) 125 | set(OpenCL_INCLUDE_DIRS ${OpenCL_INCLUDE_DIR}) 126 | 127 | include(FindPackageHandleStandardArgs) 128 | find_package_handle_standard_args( 129 | OpenCL 130 | FOUND_VAR OpenCL_FOUND 131 | REQUIRED_VARS OpenCL_LIBRARY OpenCL_INCLUDE_DIR 132 | VERSION_VAR OpenCL_VERSION_STRING) 133 | 134 | mark_as_advanced( 135 | OpenCL_INCLUDE_DIR 136 | OpenCL_LIBRARY) 137 | -------------------------------------------------------------------------------- /cmake/Modules/targetConfig.cmake.in: -------------------------------------------------------------------------------- 1 | # Copyright 2011-2020 Free Software Foundation, Inc. 2 | # Copyright 2013-2021 Sylvain Munaut 3 | # 4 | # This file is part of gr-fosphor 5 | # 6 | # SPDX-License-Identifier: GPL-3.0-or-later 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-2020 Free Software Foundation, Inc. 2 | # Copyright 2013-2021 Sylvain Munaut 3 | # 4 | # This file is part of gr-fosphor 5 | # 6 | # SPDX-License-Identifier: GPL-3.0-or-later 7 | 8 | ######################################################################## 9 | # Begin conditional configuration 10 | ######################################################################## 11 | if(ENABLE_DOXYGEN) 12 | 13 | ######################################################################## 14 | # Add subdirectories 15 | ######################################################################## 16 | add_subdirectory(doxygen) 17 | 18 | endif(ENABLE_DOXYGEN) 19 | -------------------------------------------------------------------------------- /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-test 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-test 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-test 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-test 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/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/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-test 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 TEST C++ Signal Processing Blocks 3 | * \brief All C++ blocks that can be used from the TEST 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 TEST Block 4 | 5 | This is the intro page for the Doxygen manual generated for the TEST 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 -------------------------------------------------------------------------------- /grc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2011-2020 Free Software Foundation, Inc. 2 | # Copyright 2013-2021 Sylvain Munaut 3 | # 4 | # This file is part of gr-fosphor 5 | # 6 | # SPDX-License-Identifier: GPL-3.0-or-later 7 | 8 | list_cond_append(ENABLE_GLFW fosphor_grc fosphor_glfw_sink_c.block.yml) 9 | list_cond_append(ENABLE_QT fosphor_grc fosphor_qt_sink_c.block.yml) 10 | 11 | install(FILES 12 | fosphor.tree.yml overlap_cc.block.yml ${fosphor_grc} 13 | DESTINATION share/gnuradio/grc/blocks 14 | ) 15 | -------------------------------------------------------------------------------- /grc/fosphor.tree.yml: -------------------------------------------------------------------------------- 1 | '[Core]': 2 | - Instrumentation: 3 | - QT: 4 | - fosphor_qt_sink_c 5 | - GLFW: 6 | - fosphor_glfw_sink_c 7 | - Stream Operators: 8 | - overlap_cc 9 | -------------------------------------------------------------------------------- /grc/fosphor_glfw_sink_c.block.yml: -------------------------------------------------------------------------------- 1 | id: fosphor_glfw_sink_c 2 | label: fosphor sink (GLFW) 3 | 4 | parameters: 5 | - id: wintype 6 | label: Window Type 7 | dtype: enum 8 | default: window.WIN_BLACKMAN_hARRIS 9 | options: [window.WIN_BLACKMAN_hARRIS, window.WIN_HAMMING, window.WIN_HANN, window.WIN_BLACKMAN, window.WIN_RECTANGULAR, window.WIN_KAISER, window.WIN_FLATTOP] 10 | option_labels: [Blackman-harris, Hamming, Hann, Blackman, Rectangular, Kaiser, Flat-top] 11 | hide: part 12 | - id: freq_center 13 | label: Center Frequency (Hz) 14 | dtype: real 15 | default: '0' 16 | - id: freq_span 17 | label: span (Hz) 18 | dtype: real 19 | default: samp_rate 20 | 21 | inputs: 22 | - domain: stream 23 | dtype: complex 24 | 25 | outputs: 26 | - domain: message 27 | id: freq 28 | optional: true 29 | 30 | templates: 31 | imports: |- 32 | from gnuradio import fosphor 33 | from gnuradio.fft import window 34 | make: |- 35 | fosphor.glfw_sink_c() 36 | self.${id}.set_fft_window(${wintype}) 37 | self.${id}.set_frequency_range(${freq_center}, ${freq_span}) 38 | callbacks: 39 | - set_fft_window(${wintype}) 40 | - set_frequency_range(${freq_center}, ${freq_span}) 41 | 42 | documentation: |- 43 | Key Bindings 44 | ============ 45 | z: toggle zoom mode 46 | a/d: move zoom frequency down/up 47 | s/w: adjust zoom width 48 | q/e: adjust screen split between waterfall and fft 49 | space: pause display 50 | 51 | (left)/(right) adjust dB/div 52 | (up)/(down) adjust reference level 53 | 54 | file_format: 1 55 | -------------------------------------------------------------------------------- /grc/fosphor_qt_sink_c.block.yml: -------------------------------------------------------------------------------- 1 | id: fosphor_qt_sink_c 2 | label: fosphor sink (Qt) 3 | 4 | parameters: 5 | - id: wintype 6 | label: Window Type 7 | dtype: enum 8 | default: window.WIN_BLACKMAN_hARRIS 9 | options: [window.WIN_BLACKMAN_hARRIS, window.WIN_HAMMING, window.WIN_HANN, window.WIN_BLACKMAN, window.WIN_RECTANGULAR, window.WIN_KAISER, window.WIN_FLATTOP] 10 | option_labels: [Blackman-harris, Hamming, Hann, Blackman, Rectangular, Kaiser, Flat-top] 11 | hide: part 12 | - id: freq_center 13 | label: Center Frequency (Hz) 14 | dtype: real 15 | default: '0' 16 | - id: freq_span 17 | label: span (Hz) 18 | dtype: real 19 | default: samp_rate 20 | - id: gui_hint 21 | label: GUI Hint 22 | dtype: gui_hint 23 | hide: part 24 | 25 | inputs: 26 | - domain: stream 27 | dtype: complex 28 | 29 | outputs: 30 | - domain: message 31 | id: freq 32 | optional: true 33 | 34 | templates: 35 | imports: |- 36 | from PyQt5 import Qt 37 | import sip 38 | from gnuradio import fosphor 39 | from gnuradio.fft import window 40 | make: |- 41 | <% 42 | win = 'self._%s_win' % id 43 | %>\ 44 | fosphor.qt_sink_c() 45 | self.${id}.set_fft_window(${wintype}) 46 | self.${id}.set_frequency_range(${freq_center}, ${freq_span}) 47 | ${win} = sip.wrapinstance(self.${id}.pyqwidget(), Qt.QWidget) 48 | ${gui_hint() % win} 49 | callbacks: 50 | - set_fft_window(${wintype}) 51 | - set_frequency_range(${freq_center}, ${freq_span}) 52 | 53 | documentation: |- 54 | Key Bindings 55 | ============ 56 | z: toggle zoom mode 57 | a/d: move zoom frequency down/up 58 | s/w: adjust zoom width 59 | q/e: adjust screen split between waterfall and fft 60 | space: pause display 61 | 62 | (left)/(right) adjust dB/div 63 | (up)/(down) adjust reference level 64 | 65 | file_format: 1 66 | -------------------------------------------------------------------------------- /grc/overlap_cc.block.yml: -------------------------------------------------------------------------------- 1 | id: overlap_cc 2 | label: FFT Overlap 3 | 4 | parameters: 5 | - id: wlen 6 | label: Window length 7 | dtype: int 8 | default: '1024' 9 | - id: overlap 10 | label: Overlap ratio 11 | dtype: int 12 | default: '1' 13 | 14 | inputs: 15 | - domain: stream 16 | dtype: complex 17 | 18 | outputs: 19 | - domain: stream 20 | dtype: complex 21 | 22 | templates: 23 | imports: |- 24 | from gnuradio import fosphor 25 | make: |- 26 | fosphor.overlap_cc(${wlen}, ${overlap}) 27 | callbacks: 28 | - set_overlap_ratio(${overlap}) 29 | 30 | file_format: 1 31 | -------------------------------------------------------------------------------- /include/gnuradio/fosphor/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2011-2020 Free Software Foundation, Inc. 2 | # Copyright 2013-2021 Sylvain Munaut 3 | # 4 | # This file is part of gr-fosphor 5 | # 6 | # SPDX-License-Identifier: GPL-3.0-or-later 7 | 8 | ######################################################################## 9 | # Install public header files 10 | ######################################################################## 11 | list(APPEND fosphor_headers 12 | api.h 13 | base_sink_c.h 14 | overlap_cc.h 15 | ) 16 | 17 | list_cond_append(ENABLE_GLFW fosphor_headers glfw_sink_c.h) 18 | list_cond_append(ENABLE_QT fosphor_headers qt_sink_c.h) 19 | 20 | install(FILES 21 | ${fosphor_headers} 22 | DESTINATION include/gnuradio/fosphor 23 | ) 24 | -------------------------------------------------------------------------------- /include/gnuradio/fosphor/api.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Free Software Foundation, Inc. 3 | * Copyright 2013-2021 Sylvain Munaut 4 | * 5 | * This file is part of gr-fosphor 6 | * 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #ifdef gnuradio_fosphor_EXPORTS 15 | # define GR_FOSPHOR_API __GR_ATTR_EXPORT 16 | #else 17 | # define GR_FOSPHOR_API __GR_ATTR_IMPORT 18 | #endif 19 | -------------------------------------------------------------------------------- /include/gnuradio/fosphor/base_sink_c.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2013-2021 Sylvain Munaut 4 | * 5 | * This file is part of gr-fosphor 6 | * 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | namespace gr { 18 | namespace fosphor { 19 | 20 | /*! 21 | * \brief Base fosphor sink API interface 22 | * \ingroup fosphor 23 | */ 24 | class GR_FOSPHOR_API base_sink_c : public gr::sync_block 25 | { 26 | protected: 27 | base_sink_c(const char *name = NULL); 28 | 29 | public: 30 | 31 | enum ui_action_t { 32 | DB_PER_DIV_UP, 33 | DB_PER_DIV_DOWN, 34 | REF_UP, 35 | REF_DOWN, 36 | ZOOM_TOGGLE, 37 | ZOOM_WIDTH_UP, 38 | ZOOM_WIDTH_DOWN, 39 | ZOOM_CENTER_UP, 40 | ZOOM_CENTER_DOWN, 41 | RATIO_UP, 42 | RATIO_DOWN, 43 | FREEZE_TOGGLE, 44 | }; 45 | 46 | enum mouse_action_t { 47 | CLICK, 48 | }; 49 | 50 | virtual void execute_ui_action(enum ui_action_t action) = 0; 51 | virtual void execute_mouse_action(enum mouse_action_t action, int x, int y) = 0; 52 | 53 | virtual void set_frequency_range(const double center, 54 | const double span) = 0; 55 | virtual void set_frequency_center(const double center) = 0; 56 | virtual void set_frequency_span(const double span) = 0; 57 | 58 | virtual void set_fft_window(const gr::fft::window::win_type win) = 0; 59 | }; 60 | 61 | } // namespace fosphor 62 | } // namespace gr 63 | -------------------------------------------------------------------------------- /include/gnuradio/fosphor/glfw_sink_c.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2013-2021 Sylvain Munaut 4 | * 5 | * This file is part of gr-fosphor 6 | * 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | namespace gr { 18 | namespace fosphor { 19 | 20 | /*! 21 | * \brief GLFW version of fosphor sink 22 | * \ingroup fosphor 23 | */ 24 | class GR_FOSPHOR_API glfw_sink_c : virtual public base_sink_c 25 | { 26 | public: 27 | typedef std::shared_ptr sptr; 28 | 29 | /*! 30 | * \brief Return a shared_ptr to a new instance of fosphor::glfw_sink_c. 31 | * 32 | * To avoid accidental use of raw pointers, fosphor::glfw_sink_c's 33 | * constructor is in a private implementation 34 | * class. fosphor::glfw_sink_c::make is the public interface for 35 | * creating new instances. 36 | */ 37 | static sptr make(); 38 | }; 39 | 40 | } // namespace fosphor 41 | } // namespace gr 42 | -------------------------------------------------------------------------------- /include/gnuradio/fosphor/overlap_cc.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2013-2021 Sylvain Munaut 4 | * 5 | * This file is part of gr-fosphor 6 | * 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | namespace gr { 18 | namespace fosphor { 19 | 20 | /*! 21 | * \brief Block preparing an overlapped version of the stream 22 | * \ingroup fosphor 23 | */ 24 | class GR_FOSPHOR_API overlap_cc : virtual public gr::sync_interpolator 25 | { 26 | public: 27 | typedef std::shared_ptr sptr; 28 | 29 | static sptr make(int wlen, int overlap); 30 | 31 | virtual void set_overlap_ratio(const int overlap) = 0; 32 | }; 33 | 34 | } // namespace fosphor 35 | } // namespace gr 36 | -------------------------------------------------------------------------------- /include/gnuradio/fosphor/qt_sink_c.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2013-2021 Sylvain Munaut 4 | * 5 | * This file is part of gr-fosphor 6 | * 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | class QApplication; 18 | class QWidget; 19 | 20 | namespace gr { 21 | namespace fosphor { 22 | 23 | /*! 24 | * \brief Qt version of fosphor sink 25 | * \ingroup fosphor 26 | */ 27 | class GR_FOSPHOR_API qt_sink_c : virtual public base_sink_c 28 | { 29 | public: 30 | typedef std::shared_ptr sptr; 31 | 32 | /*! 33 | * \brief Return a shared_ptr to a new instance of fosphor::qt_sink_c. 34 | * 35 | * To avoid accidental use of raw pointers, fosphor::qt_sink_c's 36 | * constructor is in a private implementation 37 | * class. fosphor::qt_sink_c::make is the public interface for 38 | * creating new instances. 39 | */ 40 | static sptr make(QWidget *parent=NULL); 41 | 42 | virtual void exec_() = 0; 43 | virtual QWidget* qwidget() = 0; 44 | 45 | #if defined(PY_VERSION) 46 | virtual PyObject* pyqwidget() = 0; 47 | #else 48 | virtual void* pyqwidget() = 0; 49 | #endif 50 | 51 | QApplication *d_qApplication; 52 | }; 53 | 54 | } // namespace fosphor 55 | } // namespace gr 56 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2011-2020 Free Software Foundation, Inc. 2 | # Copyright 2013-2021 Sylvain Munaut 3 | # 4 | # This file is part of gr-fosphor 5 | # 6 | # SPDX-License-Identifier: GPL-3.0-or-later 7 | 8 | ######################################################################## 9 | # Setup library 10 | ######################################################################## 11 | include(GrPlatform) #define LIB_SUFFIX 12 | 13 | find_package(PythonInterp 3) 14 | 15 | add_custom_command( 16 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/fosphor 17 | OUTPUT fosphor/resource_data.c 18 | DEPENDS fosphor/fft.cl fosphor/display.cl fosphor/cmap_simple.glsl fosphor/cmap_bicubic.glsl fosphor/cmap_fallback.glsl fosphor/DroidSansMonoDotted.ttf 19 | COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/fosphor/ 20 | COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/fosphor/llist.h ${CMAKE_CURRENT_BINARY_DIR}/fosphor/ 21 | COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/fosphor/resource_internal.h ${CMAKE_CURRENT_BINARY_DIR}/fosphor/ 22 | COMMAND ${PYTHON_EXECUTABLE} -B mkresources.py fft.cl display.cl cmap_simple.glsl cmap_bicubic.glsl cmap_fallback.glsl DroidSansMonoDotted.ttf > ${CMAKE_CURRENT_BINARY_DIR}/fosphor/resource_data.c 23 | ) 24 | 25 | list(APPEND fosphor_sources 26 | fosphor/axis.c 27 | fosphor/cl.c 28 | fosphor/cl_compat.c 29 | fosphor/fosphor.c 30 | fosphor/gl.c 31 | fosphor/gl_cmap.c 32 | fosphor/gl_cmap_gen.c 33 | fosphor/gl_font.c 34 | fosphor/resource.c 35 | fosphor/resource_data.c 36 | fifo.cc 37 | base_sink_c_impl.cc 38 | overlap_cc_impl.cc 39 | ) 40 | 41 | list_cond_append(ENABLE_GLFW fosphor_sources glfw_sink_c_impl.cc) 42 | list_cond_append(ENABLE_QT fosphor_sources QGLSurface.cc qt_sink_c_impl.cc) 43 | 44 | add_library(gnuradio-fosphor SHARED ${fosphor_sources}) 45 | 46 | target_include_directories(gnuradio-fosphor 47 | PUBLIC ${OPENGL_INCLUDE_DIRS} 48 | PUBLIC ${OpenCL_INCLUDE_DIRS} 49 | PUBLIC ${FREETYPE_INCLUDE_DIRS} 50 | PUBLIC ${Boost_INCLUDE_DIR} 51 | PUBLIC $ 52 | PUBLIC $ 53 | ) 54 | target_link_libraries(gnuradio-fosphor 55 | ${OPENGL_LIBRARIES} 56 | ${OpenCL_LIBRARIES} 57 | ${FREETYPE_LIBRARIES} 58 | ${Boost_LIBRARIES} 59 | gnuradio::gnuradio-runtime 60 | gnuradio::gnuradio-fft 61 | ${CMAKE_DL_LIBS} 62 | ) 63 | 64 | if(WIN32) 65 | find_package(GLEW REQUIRED) 66 | target_include_directories(gnuradio-fosphor PRIVATE ${GLEW_INCLUDE_DIRS}) 67 | target_link_libraries(gnuradio-fosphor ${GLEW_LIBRARIES}) 68 | add_definitions(-DENABLE_GLEW) 69 | endif(WIN32) 70 | 71 | if(ENABLE_PYTHON) 72 | add_definitions(-DENABLE_PYTHON) 73 | target_include_directories(gnuradio-fosphor PUBLIC ${PYTHON_INCLUDE_DIRS}) 74 | target_link_libraries(gnuradio-fosphor ${PYTHON_LIBRARY}) 75 | endif(ENABLE_PYTHON) 76 | 77 | if(ENABLE_GLFW) 78 | target_include_directories(gnuradio-fosphor PUBLIC ${GLFW3_INCLUDE_DIRS}) 79 | target_link_libraries(gnuradio-fosphor ${GLFW3_LIBRARIES}) 80 | endif(ENABLE_GLFW) 81 | 82 | if(ENABLE_QT) 83 | set_target_properties(gnuradio-fosphor PROPERTIES AUTOMOC ON) 84 | target_include_directories(gnuradio-fosphor PUBLIC ${Qt5_INCLUDE_DIRS}) 85 | target_link_libraries(gnuradio-fosphor ${Qt5_LIBRARIES}) 86 | endif(ENABLE_QT) 87 | 88 | if(ENABLE_PNG) 89 | add_definitions(-DENABLE_PNG) 90 | target_include_directories(gnuradio-fosphor PRIVATE ${PNG_INCLUDE_DIRS}) 91 | target_link_libraries(gnuradio-fosphor ${PNG_LIBRARIES}) 92 | endif(ENABLE_PNG) 93 | 94 | set_target_properties(gnuradio-fosphor PROPERTIES DEFINE_SYMBOL "gnuradio_fosphor_EXPORTS") 95 | 96 | if(APPLE) 97 | set_target_properties(gnuradio-fosphor PROPERTIES 98 | INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib" 99 | ) 100 | endif(APPLE) 101 | 102 | ######################################################################## 103 | # Install built library files 104 | ######################################################################## 105 | include(GrMiscUtils) 106 | GR_LIBRARY_FOO(gnuradio-fosphor) 107 | 108 | ######################################################################## 109 | # Print summary 110 | ######################################################################## 111 | message(STATUS "Using install prefix: ${CMAKE_INSTALL_PREFIX}") 112 | message(STATUS "Building for version: ${VERSION} / ${LIBVER}") 113 | -------------------------------------------------------------------------------- /lib/QGLSurface.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2013-2021 Sylvain Munaut 4 | * 5 | * This file is part of gr-fosphor 6 | * 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #include "QGLSurface.h" 14 | #include "qt_sink_c_impl.h" 15 | 16 | #include 17 | 18 | namespace gr { 19 | namespace fosphor { 20 | 21 | QGLSurface::QGLSurface(QWidget *parent, qt_sink_c_impl *block) 22 | : QGLWidget(parent), d_block(block) 23 | { 24 | /* Save the pointer to the main GUI thread */ 25 | this->d_gui_thread = this->thread(); 26 | 27 | /* QWidget policies */ 28 | this->setFocusPolicy(Qt::StrongFocus); 29 | this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 30 | } 31 | 32 | 33 | void 34 | QGLSurface::hideEvent(QHideEvent *he) 35 | { 36 | this->d_block->cb_visibility(this->isVisible()); 37 | } 38 | 39 | void 40 | QGLSurface::showEvent(QShowEvent *he) 41 | { 42 | this->d_block->cb_visibility(this->isVisible()); 43 | } 44 | 45 | void 46 | QGLSurface::paintEvent(QPaintEvent *pe) 47 | { 48 | /* Don't do anything */ 49 | 50 | /* 51 | * The default implementation calls makeCurrent but here we want 52 | * _other_ threads to be current, so we need a dummy empty impl 53 | * for the paintEvent 54 | */ 55 | } 56 | 57 | void 58 | QGLSurface::resizeEvent(QResizeEvent *re) 59 | { 60 | /* 61 | * The default implementation calls makeCurrent but here we want 62 | * _other_ threads to be current, so don't do that ! 63 | */ 64 | 65 | /* Call back to main block */ 66 | this->d_block->cb_reshape(re->size().width(), re->size().height()); 67 | } 68 | 69 | void 70 | QGLSurface::keyPressEvent(QKeyEvent *ke) 71 | { 72 | switch (ke->key()) { 73 | case Qt::Key_Up: 74 | this->d_block->execute_ui_action(qt_sink_c_impl::REF_DOWN); 75 | break; 76 | case Qt::Key_Down: 77 | this->d_block->execute_ui_action(qt_sink_c_impl::REF_UP); 78 | break; 79 | case Qt::Key_Left: 80 | this->d_block->execute_ui_action(qt_sink_c_impl::DB_PER_DIV_DOWN); 81 | break; 82 | case Qt::Key_Right: 83 | this->d_block->execute_ui_action(qt_sink_c_impl::DB_PER_DIV_UP); 84 | break; 85 | case Qt::Key_Z: 86 | this->d_block->execute_ui_action(qt_sink_c_impl::ZOOM_TOGGLE); 87 | break; 88 | case Qt::Key_W: 89 | this->d_block->execute_ui_action(qt_sink_c_impl::ZOOM_WIDTH_UP); 90 | break; 91 | case Qt::Key_S: 92 | this->d_block->execute_ui_action(qt_sink_c_impl::ZOOM_WIDTH_DOWN); 93 | break; 94 | case Qt::Key_D: 95 | this->d_block->execute_ui_action(qt_sink_c_impl::ZOOM_CENTER_UP); 96 | break; 97 | case Qt::Key_A: 98 | this->d_block->execute_ui_action(qt_sink_c_impl::ZOOM_CENTER_DOWN); 99 | break; 100 | case Qt::Key_Q: 101 | this->d_block->execute_ui_action(qt_sink_c_impl::RATIO_UP); 102 | break; 103 | case Qt::Key_E: 104 | this->d_block->execute_ui_action(qt_sink_c_impl::RATIO_DOWN); 105 | break; 106 | case Qt::Key_Space: 107 | this->d_block->execute_ui_action(qt_sink_c_impl::FREEZE_TOGGLE); 108 | break; 109 | } 110 | } 111 | 112 | void 113 | QGLSurface::mousePressEvent(QMouseEvent *me) 114 | { 115 | int x, y; 116 | 117 | if (me->type() != QEvent::MouseButtonDblClick) 118 | return; 119 | 120 | x = me->x(); 121 | y = this->size().height() - me->y() - 1; 122 | 123 | this->d_block->execute_mouse_action(qt_sink_c_impl::CLICK, x, y); 124 | } 125 | 126 | 127 | void 128 | QGLSurface::grabContext() 129 | { 130 | QMetaObject::invokeMethod( 131 | this, 132 | "giveContext", 133 | Qt::BlockingQueuedConnection, 134 | Q_ARG(QThread*, QThread::currentThread()) 135 | ); 136 | 137 | this->makeCurrent(); 138 | } 139 | 140 | void 141 | QGLSurface::releaseContext() 142 | { 143 | this->doneCurrent(); 144 | this->context()->moveToThread(this->d_gui_thread); 145 | } 146 | 147 | void 148 | QGLSurface::giveContext(QThread *thread) 149 | { 150 | this->doneCurrent(); 151 | this->context()->moveToThread(thread); 152 | } 153 | 154 | } /* namespace fosphor */ 155 | } /* namespace gr */ 156 | -------------------------------------------------------------------------------- /lib/QGLSurface.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2013-2021 Sylvain Munaut 4 | * 5 | * This file is part of gr-fosphor 6 | * 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | class QThread; 15 | 16 | namespace gr { 17 | namespace fosphor { 18 | 19 | class qt_sink_c_impl; 20 | 21 | class QGLSurface : public ::QGLWidget 22 | { 23 | Q_OBJECT 24 | 25 | qt_sink_c_impl *d_block; 26 | QThread *d_gui_thread; 27 | 28 | protected: 29 | void hideEvent(QHideEvent *he); 30 | void showEvent(QShowEvent *he); 31 | void paintEvent(QPaintEvent *pe); 32 | void resizeEvent(QResizeEvent *re); 33 | void keyPressEvent(QKeyEvent *ke); 34 | void mousePressEvent(QMouseEvent *me); 35 | 36 | private slots: 37 | void giveContext(QThread *thread); 38 | 39 | public: 40 | QGLSurface(QWidget *parent, qt_sink_c_impl *d_block); 41 | 42 | void grabContext(); 43 | void releaseContext(); 44 | }; 45 | 46 | } // namespace fosphor 47 | } // namespace gr 48 | -------------------------------------------------------------------------------- /lib/base_sink_c_impl.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2013-2021 Sylvain Munaut 4 | * 5 | * This file is part of gr-fosphor 6 | * 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include 15 | 16 | #include 17 | 18 | struct fosphor; 19 | struct fosphor_render; 20 | 21 | namespace gr { 22 | namespace fosphor { 23 | 24 | class fifo; 25 | 26 | /*! 27 | * \brief Base class for fosphor sink implementation 28 | * \ingroup fosphor 29 | */ 30 | class base_sink_c_impl : virtual public base_sink_c 31 | { 32 | private: 33 | /* Worker thread */ 34 | gr::thread::thread d_worker; 35 | bool d_visible; 36 | bool d_active; 37 | bool d_frozen; 38 | 39 | void worker(); 40 | static void _worker(base_sink_c_impl *obj); 41 | 42 | gr::thread::mutex d_render_mutex; 43 | 44 | /* fosphor core */ 45 | fifo *d_fifo; 46 | 47 | struct fosphor *d_fosphor; 48 | struct fosphor_render *d_render_main; 49 | struct fosphor_render *d_render_zoom; 50 | 51 | void render(); 52 | 53 | static gr::thread::mutex s_boot_mutex; 54 | 55 | /* settings refresh logic */ 56 | enum { 57 | SETTING_DIMENSIONS = (1 << 0), 58 | SETTING_POWER_RANGE = (1 << 1), 59 | SETTING_FREQUENCY_RANGE = (1 << 2), 60 | SETTING_FFT_WINDOW = (1 << 3), 61 | SETTING_RENDER_OPTIONS = (1 << 4), 62 | }; 63 | 64 | uint32_t d_settings_changed; 65 | gr::thread::mutex d_settings_mutex; 66 | 67 | void settings_mark_changed(uint32_t setting); 68 | uint32_t settings_get_and_reset_changed(void); 69 | void settings_apply(uint32_t settings); 70 | 71 | /* settings values */ 72 | int d_width; 73 | int d_height; 74 | 75 | static const int k_db_per_div[]; 76 | int d_db_ref; 77 | int d_db_per_div_idx; 78 | 79 | bool d_zoom_enabled; 80 | double d_zoom_center; 81 | double d_zoom_width; 82 | 83 | float d_ratio; 84 | 85 | struct { 86 | double center; 87 | double span; 88 | } d_frequency; 89 | 90 | gr::fft::window::win_type d_fft_window; 91 | 92 | protected: 93 | base_sink_c_impl(); 94 | 95 | /* Delegated implementation of GL context management */ 96 | virtual void glctx_init() = 0; 97 | virtual void glctx_poll() = 0; 98 | virtual void glctx_swap() = 0; 99 | virtual void glctx_fini() = 0; 100 | virtual void glctx_update() = 0; 101 | 102 | /* Callbacks from GL window */ 103 | void cb_reshape(int width, int height); 104 | void cb_visibility(bool visible); 105 | 106 | public: 107 | virtual ~base_sink_c_impl(); 108 | 109 | /* gr::fosphor::base_sink_c implementation */ 110 | void execute_ui_action(enum ui_action_t action); 111 | void execute_mouse_action(enum mouse_action_t action, int x, int y); 112 | 113 | void set_frequency_range(const double center, 114 | const double span); 115 | void set_frequency_center(const double center); 116 | void set_frequency_span(const double span); 117 | 118 | void set_fft_window(const gr::fft::window::win_type win); 119 | 120 | /* gr::sync_block implementation */ 121 | int work (int noutput_items, 122 | gr_vector_const_void_star &input_items, 123 | gr_vector_void_star &output_items); 124 | 125 | bool start(); 126 | bool stop(); 127 | }; 128 | 129 | } // namespace fosphor 130 | } // namespace gr 131 | -------------------------------------------------------------------------------- /lib/fifo.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2013-2021 Sylvain Munaut 4 | * 5 | * This file is part of gr-fosphor 6 | * 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #include 11 | 12 | #include "fifo.h" 13 | 14 | namespace gr { 15 | namespace fosphor { 16 | 17 | fifo::fifo(int length) : 18 | d_len(length), d_rp(0), d_wp(0) 19 | { 20 | this->d_buf = new gr_complex[this->d_len]; 21 | } 22 | 23 | fifo::~fifo() 24 | { 25 | delete[] this->d_buf; 26 | } 27 | 28 | int 29 | fifo::free() 30 | { 31 | return (this->d_len - 1) - this->used(); 32 | } 33 | 34 | int 35 | fifo::used() 36 | { 37 | return (this->d_wp - this->d_rp) & (this->d_len - 1); 38 | } 39 | 40 | int 41 | fifo::write_max_size() 42 | { 43 | return this->d_len - this->d_wp; 44 | } 45 | 46 | gr_complex * 47 | fifo::write_prepare(int size, bool wait) 48 | { 49 | gr::thread::scoped_lock lock(this->d_mutex); 50 | 51 | if (!wait && (this->free() < size)) 52 | return NULL; 53 | 54 | while (this->free() < size) 55 | this->d_cond_full.wait(lock); 56 | 57 | return &this->d_buf[this->d_wp]; 58 | } 59 | 60 | void 61 | fifo::write_commit(int size) 62 | { 63 | gr::thread::scoped_lock lock(this->d_mutex); 64 | 65 | this->d_wp = (this->d_wp + size) & (this->d_len - 1); 66 | 67 | this->d_cond_empty.notify_one(); 68 | } 69 | 70 | int 71 | fifo::read_max_size() 72 | { 73 | return this->d_len - this->d_rp; 74 | } 75 | 76 | gr_complex * 77 | fifo::read_peek(int size, bool wait) 78 | { 79 | gr::thread::scoped_lock lock(this->d_mutex); 80 | 81 | if (!wait && (this->used() < size)) 82 | return NULL; 83 | 84 | while (this->used() < size) 85 | this->d_cond_empty.wait(lock); 86 | 87 | return &this->d_buf[this->d_rp]; 88 | } 89 | 90 | void 91 | fifo::read_discard(int size) 92 | { 93 | gr::thread::scoped_lock lock(this->d_mutex); 94 | 95 | this->d_rp = (this->d_rp + size) & (this->d_len - 1); 96 | 97 | this->d_cond_full.notify_one(); 98 | } 99 | 100 | } /* namespace fosphor */ 101 | } /* namespace gr */ 102 | -------------------------------------------------------------------------------- /lib/fifo.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2013-2021 Sylvain Munaut 4 | * 5 | * This file is part of gr-fosphor 6 | * 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | namespace gr { 18 | namespace fosphor { 19 | 20 | class GR_FOSPHOR_API fifo 21 | { 22 | private: 23 | gr_complex *d_buf; 24 | int d_len; 25 | int d_rp; 26 | int d_wp; 27 | 28 | thread::mutex d_mutex; 29 | thread::condition_variable d_cond_empty; 30 | thread::condition_variable d_cond_full; 31 | 32 | public: 33 | fifo(int length); 34 | ~fifo(); 35 | 36 | int free(); 37 | int used(); 38 | 39 | int write_max_size(); 40 | gr_complex *write_prepare(int size, bool wait=true); 41 | void write_commit(int size); 42 | 43 | int read_max_size(); 44 | gr_complex *read_peek(int size, bool wait=true); 45 | void read_discard(int size); 46 | }; 47 | 48 | } // namespace fosphor 49 | } // namespace gr 50 | -------------------------------------------------------------------------------- /lib/fosphor/AUTHORS: -------------------------------------------------------------------------------- 1 | Sylvain Munaut 2 | -------------------------------------------------------------------------------- /lib/fosphor/DroidSansMonoDotted.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osmocom/gr-fosphor/74d54fc0b3ec9aeb7033686526c5e766f36eaf24/lib/fosphor/DroidSansMonoDotted.ttf -------------------------------------------------------------------------------- /lib/fosphor/DroidSansMonoDotted.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /lib/fosphor/Makefile: -------------------------------------------------------------------------------- 1 | UNAME=$(shell uname) 2 | CC=gcc 3 | CFLAGS=-Wall -Werror -O2 `pkg-config freetype2 glfw3 libpng --cflags` -g 4 | LDLIBS=`pkg-config freetype2 glfw3 libpng --libs` -lm 5 | ifneq ($(AMDAPPSDKROOT), ) 6 | CFLAGS+=-I$(AMDAPPSDKROOT)/include 7 | endif 8 | ifeq ($(UNAME), Linux) 9 | LDLIBS+=-lOpenCL -lGL -ldl 10 | endif 11 | ifeq ($(UNAME), Darwin) 12 | LDLIBS+=-framework OpenCL -framework OpenGL -framework Cocoa -framework IOKit 13 | endif 14 | LDFLAGS=-g 15 | 16 | RESOURCE_FILES=fft.cl display.cl cmap_simple.glsl cmap_bicubic.glsl cmap_fallback.glsl DroidSansMonoDotted.ttf 17 | 18 | all: main 19 | 20 | resource_data.c: $(RESOURCE_FILES) mkresources.py 21 | ./mkresources.py $(RESOURCE_FILES) > resource_data.c 22 | 23 | main: resource.o resource_data.o axis.o cl.o cl_compat.o fosphor.o gl.o gl_cmap.o gl_cmap_gen.o gl_font.o main.o 24 | 25 | clean: 26 | rm -f main *.o resource_data.c 27 | -------------------------------------------------------------------------------- /lib/fosphor/axis.c: -------------------------------------------------------------------------------- 1 | /* 2 | * axis.c 3 | * 4 | * Logic to deal with various axises 5 | * 6 | * Copyright (C) 2013-2021 Sylvain Munaut 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | /*! \addtogroup axis 11 | * @{ 12 | */ 13 | 14 | /*! \file axis.c 15 | * \brief Logic to deal with various axises 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "axis.h" 24 | 25 | 26 | #define FX_MODE_COUNT 0 27 | #define FX_MODE_RELATIVE 1 28 | #define FX_MODE_ABSOLUTE 2 29 | 30 | 31 | static double 32 | _si_scaling(double val, int max_num, char *prefix, unsigned int *exp) 33 | { 34 | const char prefixes[5] = { ' ', 'k', 'M', 'G', 'T' }; 35 | int exponent = log10f(fabs(val)); 36 | int d = exponent >= max_num ? ((exponent - max_num + 3) / 3) : 0; 37 | 38 | if (prefix) 39 | *prefix = prefixes[d]; 40 | 41 | if (exp) 42 | *exp = d * 3; 43 | 44 | return val / powf(10.0, d * 3); 45 | } 46 | 47 | static int 48 | _num_decimals(double val) 49 | { 50 | int c; 51 | double v; 52 | 53 | for (c=0; c<22; c++) { 54 | v = val * pow(10, c); 55 | if ( fabs(v - round(v)) == 0.0 ) 56 | return c; 57 | } 58 | 59 | return -1; 60 | } 61 | 62 | 63 | void 64 | freq_axis_build(struct freq_axis *fx, double center, double span, int n_div) 65 | { 66 | /* Basic info */ 67 | fx->center = center; 68 | fx->span = span; 69 | fx->step = span / n_div; 70 | 71 | /* Select mode of operation */ 72 | if (span == 0.0) { 73 | fx->mode = FX_MODE_COUNT; 74 | } else if (center == 0.0) { 75 | fx->mode = FX_MODE_RELATIVE; 76 | } else if (floor(log10f(fx->step)) < (floor(log10f(fx->center)) - 4)) { 77 | fx->mode = FX_MODE_RELATIVE; 78 | } else { 79 | fx->mode = FX_MODE_ABSOLUTE; 80 | } 81 | 82 | /* Select display format for abolute frequencies */ 83 | if (center != 0.0) 84 | { 85 | double min_freq, max_freq, big_freq; 86 | char prefix[2] = {0, 0}; 87 | unsigned int exp, x, y, z; 88 | 89 | max_freq = fx->center + (span / 2.0); 90 | min_freq = fx->center - (span / 2.0); 91 | 92 | big_freq = (fabs(max_freq) > fabs(min_freq)) ? 93 | fabs(max_freq) : fabs(min_freq); 94 | 95 | _si_scaling(big_freq, 4, prefix, &exp); 96 | 97 | if (prefix[0] == ' ') 98 | prefix[0] = '\0'; 99 | 100 | fx->abs_scale = 1.0 / powf(10.0, exp); 101 | 102 | x = (int)floor(log10f(big_freq)) - exp + 1; 103 | y = _num_decimals(fx->center * fx->abs_scale); 104 | z = _num_decimals(fx->step * fx->abs_scale); 105 | 106 | if (z > y) 107 | y = z; 108 | 109 | if (x + y > 6) 110 | y = 6 - x; 111 | 112 | sprintf(fx->abs_fmt, "%%.%dlf%s", y, prefix); 113 | } 114 | 115 | /* Select display format for relative mode */ 116 | if (fx->mode == FX_MODE_RELATIVE) 117 | { 118 | double max_dev = fx->step * 5; 119 | char prefix[2] = {0, 0}; 120 | unsigned int exp, x, y; 121 | 122 | _si_scaling(max_dev, 3, prefix, &exp); 123 | 124 | if (prefix[0] == ' ') 125 | prefix[0] = '\0'; 126 | 127 | fx->rel_step = fx->step / powf(10.0, exp); 128 | 129 | x = (int)floor(log10f(max_dev)) - exp + 1; 130 | y = _num_decimals(fx->rel_step); 131 | 132 | if (x + y > 4) 133 | y = 4 - x; 134 | 135 | sprintf(fx->rel_fmt, "%%+.%dlf%s", y, prefix); 136 | } 137 | } 138 | 139 | void 140 | freq_axis_render(struct freq_axis *fx, char *str, int step) 141 | { 142 | /* Special case: count mode */ 143 | if (step && (fx->mode == FX_MODE_COUNT)) { 144 | sprintf(str, "%+d", step); 145 | return; 146 | } 147 | 148 | /* Special case: no center frequency */ 149 | if (!step && (fx->center == 0.0)) { 150 | sprintf(str, "0"); 151 | return; 152 | } 153 | 154 | /* Relative case */ 155 | if (step && (fx->mode == FX_MODE_RELATIVE)) { 156 | sprintf(str, fx->rel_fmt, step * fx->rel_step); 157 | return; 158 | } 159 | 160 | /* Normal full absolute frequency */ 161 | sprintf(str, fx->abs_fmt, (fx->center + step * fx->step) * fx->abs_scale); 162 | } 163 | 164 | /*! @} */ 165 | -------------------------------------------------------------------------------- /lib/fosphor/axis.h: -------------------------------------------------------------------------------- 1 | /* 2 | * axis.h 3 | * 4 | * Logic to deal with various axises 5 | * 6 | * Copyright (C) 2013-2021 Sylvain Munaut 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #pragma once 11 | 12 | /*! \defgroup axis 13 | * @{ 14 | */ 15 | 16 | /*! \file axis.h 17 | * \brief Logic to deal with various axises 18 | */ 19 | 20 | struct freq_axis 21 | { 22 | double center; 23 | double span; 24 | double step; 25 | int mode; 26 | char abs_fmt[16]; 27 | double abs_scale; 28 | char rel_fmt[16]; 29 | double rel_step; 30 | }; 31 | 32 | void freq_axis_build(struct freq_axis *fx, double center, double span, int n_div); 33 | void freq_axis_render(struct freq_axis *fx, char *str, int step); 34 | 35 | /*! @} */ 36 | -------------------------------------------------------------------------------- /lib/fosphor/cl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * cl.h 3 | * 4 | * OpenCL base routines 5 | * 6 | * Copyright (C) 2013-2021 Sylvain Munaut 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #pragma once 11 | 12 | /*! \defgroup cl 13 | * @{ 14 | */ 15 | 16 | /*! \file cl.h 17 | * \brief OpenCL base routines 18 | */ 19 | 20 | struct fosphor; 21 | 22 | int fosphor_cl_init(struct fosphor *self); 23 | void fosphor_cl_release(struct fosphor *self); 24 | 25 | int fosphor_cl_process(struct fosphor *self, 26 | void *samples, int len); 27 | int fosphor_cl_finish(struct fosphor *self); 28 | 29 | void fosphor_cl_load_fft_window(struct fosphor *self, float *win); 30 | int fosphor_cl_get_waterfall_position(struct fosphor *self); 31 | void fosphor_cl_set_histogram_range(struct fosphor *self, 32 | float scale, float offset); 33 | 34 | /*! @} */ 35 | -------------------------------------------------------------------------------- /lib/fosphor/cl_compat.c: -------------------------------------------------------------------------------- 1 | /* 2 | * cl_compat.c 3 | * 4 | * Handle OpenCL 1.1 <> 1.2 fallback and the related uglyness 5 | * 6 | * Copyright (C) 2013-2021 Sylvain Munaut 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | /*! \addtogroup cl 11 | * @{ 12 | */ 13 | 14 | /*! \file cl_compat.c 15 | * \brief Handle OpenCL 1.1 <> 1.2 fallback and the related uglyness 16 | */ 17 | 18 | /* Include whatever is needed for dynamic symbol lookup */ 19 | #ifdef _WIN32 20 | # include 21 | #else 22 | # define _GNU_SOURCE 23 | # include 24 | #endif 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | /* Make sure we allow OpenCL 1.1 fn without warnings */ 31 | #define CL_USE_DEPRECATED_OPENCL_1_1_APIS 32 | 33 | #include "cl_compat.h" 34 | 35 | 36 | /* -------------------------------------------------------------------------- */ 37 | /* Fallback magic */ 38 | /* -------------------------------------------------------------------------- */ 39 | 40 | #define ALT_WRAP(rtype, fn, arg_list, arg_call) \ 41 | \ 42 | static CL_API_ENTRY rtype (CL_API_CALL * fn ## _icd) arg_list = NULL; \ 43 | static rtype fn ## _alt arg_list; \ 44 | \ 45 | CL_API_ENTRY rtype CL_API_CALL fn arg_list \ 46 | { \ 47 | if (g_allow_cl12 && fn ## _icd) \ 48 | return fn ## _icd arg_call; \ 49 | else \ 50 | return fn ## _alt arg_call; \ 51 | } \ 52 | \ 53 | static rtype fn ## _alt arg_list 54 | 55 | #define ALT_INIT(fn) \ 56 | *(void **)(&fn ## _icd) = _cl_icd_get_sym(#fn); \ 57 | if (!fn ## _icd) \ 58 | g_allow_cl12 = 0; 59 | 60 | 61 | /*! \brief Whether to allow direct CL 1.2 usage or not */ 62 | static int g_allow_cl12 = 1; 63 | 64 | 65 | /*! \brief Tries to find a function pointer to a given OpenCL function 66 | * \param[in] fn The name of the function to lookup 67 | */ 68 | static void * 69 | _cl_icd_get_sym(const char *fn) 70 | { 71 | #ifdef _WIN32 72 | static HMODULE h = NULL; 73 | if (!h) 74 | h = GetModuleHandle(L"OpenCL.dll"); 75 | return GetProcAddress(h, fn); 76 | #else 77 | return dlsym(RTLD_NEXT, fn); 78 | #endif 79 | } 80 | 81 | 82 | /* -------------------------------------------------------------------------- */ 83 | /* Fallback implementations */ 84 | /* -------------------------------------------------------------------------- */ 85 | /* These are only valid for fosphor and might not cover all use cases ! */ 86 | 87 | ALT_WRAP(cl_mem, 88 | clCreateFromGLTexture, 89 | (cl_context context, 90 | cl_mem_flags flags, 91 | GLenum texture_target, 92 | GLint miplevel, 93 | GLuint texture, 94 | cl_int *errcode_ret), 95 | (context, flags, texture_target, miplevel, texture, errcode_ret) 96 | ) 97 | { 98 | return clCreateFromGLTexture2D( 99 | context, 100 | flags, 101 | texture_target, 102 | miplevel, 103 | texture, 104 | errcode_ret 105 | ); 106 | } 107 | 108 | ALT_WRAP(cl_mem, 109 | clCreateImage, 110 | (cl_context context, 111 | cl_mem_flags flags, 112 | const cl_image_format *image_format, 113 | const cl_image_desc *image_desc, 114 | void *host_ptr, 115 | cl_int *errcode_ret), 116 | (context, flags, image_format, image_desc, host_ptr, errcode_ret) 117 | ) 118 | { 119 | if (image_desc->image_type != CL_MEM_OBJECT_IMAGE2D) 120 | { 121 | *errcode_ret = CL_IMAGE_FORMAT_NOT_SUPPORTED; 122 | return NULL; 123 | } 124 | 125 | return clCreateImage2D( 126 | context, 127 | flags, 128 | image_format, 129 | image_desc->image_width, 130 | image_desc->image_height, 131 | image_desc->image_row_pitch, 132 | host_ptr, 133 | errcode_ret 134 | ); 135 | } 136 | 137 | ALT_WRAP(cl_int, 138 | clEnqueueFillBuffer, 139 | (cl_command_queue command_queue, 140 | cl_mem buffer, 141 | const void *pattern, 142 | size_t pattern_size, 143 | size_t offset, 144 | size_t size, 145 | cl_uint num_events_in_wait_list, 146 | const cl_event *event_wait_list, 147 | cl_event *event), 148 | (command_queue, buffer, pattern, pattern_size, offset, size, 149 | num_events_in_wait_list, event_wait_list, event) 150 | ) 151 | { 152 | cl_int err; 153 | char *buf; 154 | int i; 155 | 156 | /* Generate the pattern 'manually' */ 157 | buf = malloc(size); 158 | if (!buf) 159 | return CL_OUT_OF_RESOURCES; 160 | 161 | for (i=0; i pattern_size ? pattern_size : (size - i); 164 | memcpy(&buf[i], pattern, s); 165 | } 166 | 167 | /* Do a blocking write */ 168 | err = clEnqueueWriteBuffer( 169 | command_queue, 170 | buffer, 171 | CL_TRUE, 172 | offset, 173 | size, 174 | buf, 175 | num_events_in_wait_list, 176 | event_wait_list, 177 | event 178 | ); 179 | 180 | /* Done */ 181 | free(buf); 182 | 183 | return err; 184 | } 185 | 186 | ALT_WRAP(cl_int, 187 | clEnqueueFillImage, 188 | (cl_command_queue command_queue, 189 | cl_mem image, 190 | const void *fill_color, 191 | const size_t *origin, 192 | const size_t *region, 193 | cl_uint num_events_in_wait_list, 194 | const cl_event *event_wait_list, 195 | cl_event *event), 196 | (command_queue, image, fill_color, origin, region, 197 | num_events_in_wait_list, event_wait_list, event) 198 | ) 199 | { 200 | cl_int err; 201 | cl_image_format fmt; 202 | float *buf, *color = (float *)fill_color; 203 | int i; 204 | 205 | /* Grab a bunch of infos about the image */ 206 | err = clGetImageInfo(image, CL_IMAGE_FORMAT, sizeof(fmt), &fmt, NULL); 207 | if (err != CL_SUCCESS) 208 | return err; 209 | 210 | /* Very limited replacement :p */ 211 | if ((fmt.image_channel_order != CL_R) || 212 | (fmt.image_channel_data_type != CL_FLOAT)) 213 | return CL_IMAGE_FORMAT_NOT_SUPPORTED; 214 | 215 | if ((origin[2] != 0) || (region[2] != 1)) 216 | return CL_IMAGE_FORMAT_NOT_SUPPORTED; 217 | 218 | /* Fill a buffer manually */ 219 | buf = malloc(region[0] * region[1] * sizeof(float)); 220 | if (!buf) 221 | return CL_OUT_OF_RESOURCES; 222 | 223 | for (i=0; i<(region[0] * region[1]); i++) 224 | buf[i] = color[0]; 225 | 226 | /* Do a blocking write */ 227 | err = clEnqueueWriteImage( 228 | command_queue, 229 | image, 230 | CL_TRUE, 231 | origin, 232 | region, 233 | 0, 234 | 0, 235 | buf, 236 | num_events_in_wait_list, 237 | event_wait_list, 238 | event 239 | ); 240 | 241 | /* Done */ 242 | free(buf); 243 | 244 | return err; 245 | } 246 | 247 | 248 | /* -------------------------------------------------------------------------- */ 249 | /* Compat API control */ 250 | /* -------------------------------------------------------------------------- */ 251 | 252 | void 253 | cl_compat_init(void) 254 | { 255 | ALT_INIT(clCreateFromGLTexture) 256 | ALT_INIT(clCreateImage) 257 | ALT_INIT(clEnqueueFillBuffer) 258 | ALT_INIT(clEnqueueFillImage) 259 | } 260 | 261 | void 262 | cl_compat_check_platform(cl_platform_id pl_id) 263 | { 264 | cl_int err; 265 | char buf[128]; 266 | 267 | if (!g_allow_cl12) 268 | return; 269 | 270 | err = clGetPlatformInfo(pl_id, CL_PLATFORM_VERSION, sizeof(buf), buf, NULL); 271 | if (err != CL_SUCCESS) { 272 | fprintf(stderr, "[!] Failed to fetch platform version. Assume it can't do OpenCL 1.2\n"); 273 | g_allow_cl12 = 0; 274 | } 275 | 276 | if (!strncmp(buf, "OpenCL 1.2 pocl", 15)) { 277 | fprintf(stderr, "[w] POCL detected, blacklisting CL 1.2 since it's buggy\n"); 278 | g_allow_cl12 = 0; 279 | } 280 | 281 | if (strncmp(buf, "OpenCL 1.2 ", 11)) { 282 | g_allow_cl12 = 0; 283 | } 284 | } 285 | 286 | /*! @} */ 287 | -------------------------------------------------------------------------------- /lib/fosphor/cl_compat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * cl_compat.h 3 | * 4 | * Handle OpenCL 1.1 <> 1.2 fallback and the related uglyness 5 | * 6 | * Copyright (C) 2013-2021 Sylvain Munaut 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #pragma once 11 | 12 | /*! \ingroup cl 13 | * @{ 14 | */ 15 | 16 | /*! \file cl_compat.h 17 | * \brief Handle OpenCL 1.1 <> 1.2 fallback and the related uglyness 18 | */ 19 | 20 | #include "cl_platform.h" 21 | #include "gl_platform.h" 22 | 23 | 24 | /* Define NVidia specific attributes */ 25 | #ifndef CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV 26 | # define CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV 0x4000 27 | # define CL_DEVICE_COMPUTE_CAPABILITY_MINOR_NV 0x4001 28 | #endif 29 | 30 | 31 | /* If OpenCL 1.2 isn't supported in the header, add our prototypes */ 32 | #ifndef CL_VERSION_1_2 33 | 34 | typedef struct _cl_image_desc { 35 | cl_mem_object_type image_type; 36 | size_t image_width; 37 | size_t image_height; 38 | size_t image_depth; 39 | size_t image_array_size; 40 | size_t image_row_pitch; 41 | size_t image_slice_pitch; 42 | cl_uint num_mip_levels; 43 | cl_uint num_samples; 44 | cl_mem buffer; 45 | } cl_image_desc; 46 | 47 | cl_mem CL_API_CALL 48 | clCreateFromGLTexture(cl_context context, 49 | cl_mem_flags flags, 50 | GLenum texture_target, 51 | GLint miplevel, 52 | GLuint texture, 53 | cl_int *errcode_ret); 54 | 55 | cl_mem CL_API_CALL 56 | clCreateImage(cl_context context, 57 | cl_mem_flags flags, 58 | const cl_image_format *image_format, 59 | const cl_image_desc *image_desc, 60 | void *host_ptr, 61 | cl_int *errcode_ret); 62 | 63 | cl_int CL_API_CALL 64 | clEnqueueFillBuffer(cl_command_queue command_queue, 65 | cl_mem buffer, 66 | const void *pattern, 67 | size_t pattern_size, 68 | size_t offset, 69 | size_t size, 70 | cl_uint num_events_in_wait_list, 71 | const cl_event *event_wait_list, 72 | cl_event *event); 73 | 74 | cl_int CL_API_CALL 75 | clEnqueueFillImage(cl_command_queue command_queue, 76 | cl_mem image, 77 | const void *fill_color, 78 | const size_t *origin, 79 | const size_t *region, 80 | cl_uint num_events_in_wait_list, 81 | const cl_event *event_wait_list, 82 | cl_event *event); 83 | 84 | #endif 85 | 86 | 87 | /* The actual API */ 88 | void cl_compat_init(void); 89 | void cl_compat_check_platform(cl_platform_id pl_id); 90 | 91 | 92 | /*! @} */ 93 | -------------------------------------------------------------------------------- /lib/fosphor/cl_platform.h: -------------------------------------------------------------------------------- 1 | /* 2 | * cl_platform.h 3 | * 4 | * Wrapper to select proper OpenCL headers for various platforms 5 | * 6 | * Copyright (C) 2013-2021 Sylvain Munaut 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | /*! \file cl_platform.h 11 | * \brief Wrapper to select proper OpenCL headers for various platforms 12 | */ 13 | 14 | #if !defined(_WIN32) && (defined(__WIN32__) || defined(WIN32) || defined(__CYGWIN__)) 15 | # define _WIN32 16 | #endif 17 | 18 | #define CL_TARGET_OPENCL_VERSION 120 19 | 20 | #if defined(__APPLE__) || defined(MACOSX) 21 | # include 22 | # include 23 | # include 24 | # include 25 | #else 26 | # define CL_USE_DEPRECATED_OPENCL_1_1_APIS 27 | # define CL_USE_DEPRECATED_OPENCL_1_2_APIS 28 | # include 29 | # include 30 | # include 31 | #endif 32 | -------------------------------------------------------------------------------- /lib/fosphor/cmap_bicubic.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | * cmap_bicubic.glsl 3 | * 4 | * Color mapping shader - BiCubic interpolation 5 | * 6 | * Copyright (C) 2013-2021 Sylvain Munaut 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | * 9 | * Note (to make it clear): for the purpose of this license, any software 10 | * making use of this shader (or derivative thereof) is considered to be 11 | * a derivative work (i.e. "a work based on the program"). 12 | */ 13 | 14 | /* Require GLSL 1.5 compatibility profile to build without warning 15 | * But we use 1.3 since some cards don't allow 1.5 compatibility but 16 | * will work with 1.3 17 | */ 18 | 19 | /* #version 150 compatibility */ 20 | #version 130 21 | 22 | 23 | /* ------------------------------------------------------------------------ */ 24 | /* Cubic interpolation functions */ 25 | /* ------------------------------------------------------------------------ */ 26 | 27 | /* Triangular */ 28 | float if_triangular(float x) 29 | { 30 | x /= 2.0; 31 | return (x < 0.0) ? (x + 1.0) : (1.0 - x); 32 | } 33 | 34 | /* Bell curve */ 35 | float if_bell(float x) 36 | { 37 | x *= 0.75; /* [-2:2] -> [-1.5:1.5] */ 38 | 39 | if ((x > -1.5) && (x < -0.5)) 40 | return 0.5 * pow(x + 1.5, 2.0); 41 | 42 | else if ((x > -0.5) && (x < 0.5)) 43 | return 3.0 / 4.0 - (x * x); 44 | 45 | else if ((x > 0.5) && (x < 1.5)) 46 | return 0.5 * pow(x - 1.5, 2.0); 47 | 48 | return 0.0; 49 | } 50 | 51 | /* B-Spline */ 52 | float if_bspline(float x) 53 | { 54 | if (x < 0.0) 55 | x = -x; 56 | 57 | if ((x >= 0.0) && (x <= 1.0)) 58 | return (2.0 / 3.0) + 0.5 * (x * x * x) - (x*x); 59 | 60 | else if( x > 1.0 && x <= 2.0 ) 61 | return (1.0 / 6.0) * pow((2.0 - x), 3.0); 62 | 63 | return 1.0; 64 | } 65 | 66 | /* Catmull-Rom Spline */ 67 | float if_catmull_rom(float x) 68 | { 69 | const float B = 0.0; 70 | const float C = 0.5; 71 | 72 | if (x < 0.0) 73 | x = -x; 74 | 75 | if (x < 1.0) 76 | return ( 77 | ( 12.0 - 9.0 * B - 6.0 * C) * ( x * x * x ) + 78 | (-18.0 + 12.0 * B + 6.0 * C) * ( x * x ) + 79 | ( 6.0 - 2.0 * B ) 80 | ) / 6.0; 81 | 82 | else if ((x >= 1.0) && (x < 2.0)) 83 | return ( 84 | ( - B - 6.0 * C ) * ( x * x * x ) + 85 | ( 6.0 * B + 30.0 * C ) * ( x *x ) + 86 | ( -12.0 * B - 48.0 * C ) * x + 87 | ( 8.0 * B + 24.0 * C ) 88 | ) / 6.0; 89 | 90 | return 0.0; 91 | } 92 | 93 | 94 | /* ------------------------------------------------------------------------ */ 95 | /* Bi-Cubic texture sampling */ 96 | /* ------------------------------------------------------------------------ */ 97 | 98 | /* Select the interpolation function */ 99 | #ifndef if_func 100 | #define if_func if_bspline 101 | #endif 102 | 103 | /* Returns the weight for the 4 pixels in the cubic interpolation */ 104 | vec4 cubic(float x) 105 | { 106 | return vec4( 107 | if_func(-1.0 - x), 108 | if_func( - x), 109 | if_func( 1.0 - x), 110 | if_func( 2.0 - x) 111 | ); 112 | } 113 | 114 | /* bicubic sampler */ 115 | vec4 bicubic(sampler2D tex_id, vec2 tex_coord) 116 | { 117 | ivec2 tex_size = textureSize(tex_id, 0); 118 | vec2 tex_scale = 1.0 / vec2(tex_size); 119 | 120 | tex_coord *= tex_size; 121 | tex_coord -= vec2(0.5,0.5); 122 | 123 | float fx = fract(tex_coord.x); 124 | float fy = fract(tex_coord.y); 125 | tex_coord.x -= fx; 126 | tex_coord.y -= fy; 127 | 128 | vec4 xcubic = cubic(fx); 129 | vec4 ycubic = cubic(fy); 130 | 131 | vec4 c = vec4( 132 | tex_coord.x - 0.5, 133 | tex_coord.x + 1.5, 134 | tex_coord.y - 0.5, 135 | tex_coord.y + 1.5 136 | ); 137 | vec4 s = vec4( 138 | xcubic.x + xcubic.y, 139 | xcubic.z + xcubic.w, 140 | ycubic.x + ycubic.y, 141 | ycubic.z + ycubic.w 142 | ); 143 | vec4 offset = c + vec4(xcubic.y, xcubic.w, ycubic.y, ycubic.w) / s; 144 | 145 | vec4 s0 = texture(tex_id, offset.xz * tex_scale); 146 | vec4 s1 = texture(tex_id, offset.yz * tex_scale); 147 | vec4 s2 = texture(tex_id, offset.xw * tex_scale); 148 | vec4 s3 = texture(tex_id, offset.yw * tex_scale); 149 | 150 | float sx = s.x / (s.x + s.y); 151 | float sy = s.z / (s.z + s.w); 152 | 153 | return mix( 154 | mix(s3, s2, sx), 155 | mix(s1, s0, sx), 156 | sy 157 | ); 158 | } 159 | 160 | 161 | /* ------------------------------------------------------------------------ */ 162 | /* Main fragment shader code */ 163 | /* ------------------------------------------------------------------------ */ 164 | 165 | /* Uniforms */ 166 | 167 | uniform sampler1D palette; /* 1D texture with the color to map to */ 168 | uniform sampler2D tex; /* 2D intensity texture */ 169 | uniform vec2 range; /* (scale, offset) vector */ 170 | 171 | 172 | /* In/Out */ 173 | 174 | out vec4 out_FragColor; 175 | 176 | 177 | /* Shader main */ 178 | 179 | void main() 180 | { 181 | float intensity = bicubic(tex, gl_TexCoord[0].st).x; 182 | float map = (intensity + range.y) * range.x; 183 | vec4 color = texture(palette, map); 184 | out_FragColor = color; 185 | } 186 | 187 | /* vim: set syntax=c: */ 188 | -------------------------------------------------------------------------------- /lib/fosphor/cmap_fallback.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | * cmap_fallback.glsl 3 | * 4 | * Color mapping shader - Fall back GLSL 1.0 compatibility version 5 | * 6 | * Copyright (C) 2013-2021 Sylvain Munaut 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | * 9 | * Note (to make it clear): for the purpose of this license, any software 10 | * making use of this shader (or derivative thereof) is considered to be 11 | * a derivative work (i.e. "a work based on the program"). 12 | */ 13 | 14 | /* ------------------------------------------------------------------------ */ 15 | /* Main fragment shader code */ 16 | /* ------------------------------------------------------------------------ */ 17 | 18 | /* Uniforms */ 19 | 20 | uniform sampler1D palette; /* 1D texture with the color to map to */ 21 | uniform sampler2D tex; /* 2D intensity texture */ 22 | uniform vec2 range; /* (scale, offset) vector */ 23 | 24 | 25 | /* Shader main */ 26 | 27 | void main() 28 | { 29 | float intensity = texture2D(tex, gl_TexCoord[0].st).x; 30 | float map = (intensity + range.y) * range.x; 31 | vec4 color = texture1D(palette, map); 32 | gl_FragColor = color; 33 | } 34 | 35 | /* vim: set syntax=c: */ 36 | -------------------------------------------------------------------------------- /lib/fosphor/cmap_simple.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | * cmap_simple.glsl 3 | * 4 | * Color mapping shader - Nearest/Bilinear version 5 | * 6 | * Copyright (C) 2013-2021 Sylvain Munaut 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | * 9 | * Note (to make it clear): for the purpose of this license, any software 10 | * making use of this shader (or derivative thereof) is considered to be 11 | * a derivative work (i.e. "a work based on the program"). 12 | */ 13 | 14 | /* Require GLSL 1.5 compatibility profile to build without warning 15 | * But we use 1.3 since some cards don't allow 1.5 compatibility but 16 | * will work with 1.3 17 | */ 18 | 19 | /* #version 150 compatibility */ 20 | #version 130 21 | 22 | 23 | /* ------------------------------------------------------------------------ */ 24 | /* Main fragment shader code */ 25 | /* ------------------------------------------------------------------------ */ 26 | 27 | /* Uniforms */ 28 | 29 | uniform sampler1D palette; /* 1D texture with the color to map to */ 30 | uniform sampler2D tex; /* 2D intensity texture */ 31 | uniform vec2 range; /* (scale, offset) vector */ 32 | 33 | 34 | /* In/Out */ 35 | 36 | out vec4 out_FragColor; 37 | 38 | 39 | /* Shader main */ 40 | 41 | void main() 42 | { 43 | float intensity = texture(tex, gl_TexCoord[0].st).x; 44 | float map = (intensity + range.y) * range.x; 45 | vec4 color = texture(palette, map); 46 | out_FragColor = color; 47 | } 48 | 49 | /* vim: set syntax=c: */ 50 | -------------------------------------------------------------------------------- /lib/fosphor/display.cl: -------------------------------------------------------------------------------- 1 | /* 2 | * display.cl 3 | * 4 | * Display OpenCL kernel. Formats the raw FFT output into GL objects we 5 | * use for display. 6 | * 7 | * Copyright (C) 2013-2014 Sylvain Munaut 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | * 22 | * NOTE (to make it clear): For the purpose of this license, any software 23 | * making use of this kernel (or derivative thereof) is considered to be 24 | * a derivative work (i.e. "a work based on the Program"). 25 | */ 26 | 27 | /* Enable or not use of NV SM11 histogram algo (set automatically) */ 28 | /* #define USE_NV_SM11_ATOMICS */ 29 | 30 | /* Enable or not the use of cl_khr_local_int32_base_atomics to 31 | * implement atomic add (set automatically) */ 32 | /* #define USE_EXT_ATOMICS */ 33 | 34 | #ifdef USE_EXT_ATOMICS 35 | #pragma OPENCL EXTENSION cl_khr_local_int32_base_atomics : enable 36 | #endif 37 | 38 | #define CLAMP 39 | 40 | //#define MAX_HOLD_LIVE 41 | //#define MAX_HOLD_HISTO 42 | //#define MAX_HOLD_NORMAL 43 | #define MAX_HOLD_DECAY 44 | 45 | 46 | #ifdef USE_NV_SM11_ATOMICS 47 | 48 | #define LOG2_WARP_SIZE 5U 49 | #define UINT_BITS 32U 50 | #define WARP_SIZE (1U << LOG2_WARP_SIZE) 51 | #define TAG_MASK ( (1U << (UINT_BITS - LOG2_WARP_SIZE)) - 1U ) 52 | 53 | /* See NVidia OpenCL Histogram256 example for how/why this works */ 54 | inline void nv_sm11_atomic_inc(volatile __local uint *p, uint tag) 55 | { 56 | uint count; 57 | do { 58 | count = *p & TAG_MASK; 59 | count = tag | (count + 1); 60 | *p = count; 61 | } while (*p != count); 62 | } 63 | 64 | #endif /* USE_NV_SM11_ATOMICS */ 65 | 66 | 67 | __attribute__((reqd_work_group_size(16, 16, 1))) 68 | __kernel void display( 69 | /* FFT Input */ 70 | __global const float2 *fft, /* [ 0] Input FFT (complex) */ 71 | const uint fft_log2_len, /* [ 1] log2(FFT length) */ 72 | const uint fft_batch, /* [ 2] # spectrums in the input */ 73 | 74 | /* Waterfall */ 75 | __write_only image2d_t wf_tex, /* [ 3] Texture handle */ 76 | const uint wf_offset, /* [ 4] Y Offset in the texture */ 77 | 78 | /* Histogram */ 79 | __read_only image2d_t histo_tex_r, /* [ 5] Texture read handle */ 80 | __write_only image2d_t histo_tex_w, /* [ 6] Texture write handle */ 81 | const float histo_t0r, /* [ 7] Rise time constant */ 82 | const float histo_t0d, /* [ 8] Decay time constant */ 83 | const float histo_scale, /* [ 9] Val->Bin: scaling */ 84 | const float histo_ofs, /* [10] Val->Bin: offset */ 85 | 86 | /* Live spectrum */ 87 | __global float2 *spectrum_vbo, /* [11] Vertex Buffer Object */ 88 | const float live_alpha) /* [12] Averaging time constant */ 89 | { 90 | int gidx; 91 | float max_pwr = - 1000.0f; 92 | 93 | /* Local memory */ 94 | __local float live_buf[16 * 16]; /* get_local_size(0) * get_local_size(1) */ 95 | __local float max_buf[16 * 16]; /* get_local_size(0) * get_local_size(1) */ 96 | __local uint histo_buf[16 * 128]; 97 | 98 | /* Local shortcuts */ 99 | const float live_one_minus_alpha = 1.0f - live_alpha; 100 | 101 | /* Transposition & Atomic emulation */ 102 | #ifdef USE_NV_SM11_ATOMICS 103 | __local float pwr_buf[16 * 16]; /* pwr transpose buffer */ 104 | 105 | uint tib = (get_local_id(0) + get_local_id(1)) & 15; 106 | uint ti0 = tib | (get_local_id(0) << 4); 107 | uint ti1 = tib | (get_local_id(1) << 4); 108 | 109 | const uint tag = get_local_id(0) << (UINT_BITS - LOG2_WARP_SIZE); 110 | #endif 111 | 112 | /* Clear buffers */ 113 | live_buf[get_local_id(1) * get_local_size(0) + get_local_id(0)] = 0.0f; 114 | 115 | __local uint *h = &histo_buf[get_local_id(1) * get_local_size(0) + get_local_id(0)]; 116 | 117 | h[ 0] = 0; 118 | h[ 256] = 0; 119 | h[ 512] = 0; 120 | h[ 768] = 0; 121 | h[1024] = 0; 122 | h[1280] = 0; 123 | h[1536] = 0; 124 | h[1792] = 0; 125 | 126 | /* Wait for all clears to be done by everyone */ 127 | barrier(CLK_LOCAL_MEM_FENCE); 128 | 129 | /* Main loop */ 130 | for (gidx=0; gidx 127) 164 | #ifdef CLAMP 165 | bin = (bin < 0) ? 0 : 127; 166 | #else 167 | continue; 168 | #endif 169 | 170 | /* Atomic Bin increment */ 171 | #if defined(USE_NV_SM11_ATOMICS) 172 | nv_sm11_atomic_inc(&histo_buf[(bin << 4) + get_local_id(1)], tag); 173 | #elif defined(USE_EXT_ATOMICS) 174 | atom_inc(&histo_buf[(bin << 4) + get_local_id(0)]); 175 | #else 176 | atomic_inc(&histo_buf[(bin << 4) + get_local_id(0)]); 177 | #endif 178 | } 179 | 180 | max_buf[get_local_id(1) * get_local_size(0) + get_local_id(0)] = max_pwr; 181 | 182 | /* Wait for everyone before the final merges */ 183 | barrier(CLK_LOCAL_MEM_FENCE); 184 | 185 | /* Live Spectrum merging */ 186 | __global float2 *live_vbo = &spectrum_vbo[0]; 187 | 188 | if (get_global_id(1) == 0) 189 | { 190 | int i,n; 191 | float sum; 192 | float2 vertex; 193 | 194 | /* Compute sum */ 195 | sum = 0.0f; 196 | for (i=0; i> 1; 201 | i = get_global_id(0) ^ n; 202 | 203 | /* Compute vertex position */ 204 | vertex = live_vbo[i]; 205 | 206 | if (!isfinite(vertex.y)) /* Safety if previous val is weird */ 207 | vertex.y = sum / get_local_size(1); 208 | 209 | vertex.x = ((float)i / (float)n) - 1.0f; 210 | vertex.y = vertex.y * native_powr(live_one_minus_alpha, (float)fft_batch) + 211 | sum * live_alpha; 212 | 213 | live_vbo[i] = vertex; 214 | } 215 | 216 | /* Histogram merging */ 217 | for (gidx=0; gidx<128; gidx+=get_local_size(1)) 218 | { 219 | const sampler_t direct_sample = CLK_NORMALIZED_COORDS_FALSE | CLK_FILTER_NEAREST | CLK_ADDRESS_CLAMP_TO_EDGE; 220 | 221 | /* Histogram coordinates */ 222 | int2 coord; 223 | coord.x = get_global_id(0); 224 | coord.y = gidx + get_local_id(1); 225 | 226 | /* Fetch previous histogram value */ 227 | float4 hv = read_imagef(histo_tex_r, direct_sample, coord); 228 | 229 | /* Fetch hit count */ 230 | uint hc = histo_buf[coord.y * get_local_size(0) + get_local_id(0)] 231 | #ifdef USE_NV_SM11_ATOMICS 232 | & TAG_MASK 233 | #endif 234 | ; 235 | 236 | /* Fast exit if possible ... */ 237 | if ((hv.x <= 0.01f) && (hc == 0)) 238 | continue; 239 | 240 | /* Apply the rise / decay */ 241 | float a = (float)hc / (float)fft_batch; 242 | float b = a * native_recip(histo_t0r); 243 | float c = b + native_recip(histo_t0d); 244 | float d = b * native_recip(c); 245 | float e = native_powr(1.0f - c, (float)fft_batch); 246 | 247 | hv.x = (hv.x - d) * e + d; 248 | 249 | /* Clamp value (we don't clear the texture so we get crap) */ 250 | hv.x = clamp(hv.x, 0.0f, 1.0f); 251 | 252 | /* Write new histogram value */ 253 | write_imagef(histo_tex_w, coord, hv); 254 | } 255 | 256 | /* Max hold */ 257 | __global float2 *max_vbo = &spectrum_vbo[1 << fft_log2_len]; 258 | 259 | if (get_global_id(1) == 0) 260 | { 261 | int i, j, n; 262 | float2 vertex; 263 | 264 | #ifdef MAX_HOLD_HISTO 265 | vertex.y = - histo_ofs; 266 | 267 | for (gidx=0; gidx<128; gidx++) 268 | { 269 | const sampler_t direct_sample = CLK_NORMALIZED_COORDS_FALSE | CLK_FILTER_NEAREST | CLK_ADDRESS_CLAMP_TO_EDGE; 270 | 271 | /* Histogram coordinates */ 272 | int2 coord; 273 | coord.x = get_global_id(0); 274 | coord.y = gidx; 275 | 276 | /* Fetch histogram value */ 277 | float4 hv = read_imagef(histo_tex_r, direct_sample, coord); 278 | 279 | /* Set vertex position */ 280 | if (hv.x > 0.1f) 281 | vertex.y = ((float)gidx / histo_scale)- histo_ofs; 282 | } 283 | #endif 284 | 285 | /* Position in spectrum */ 286 | n = get_global_size(0) >> 1; 287 | i = get_global_id(0) ^ n; 288 | 289 | max_pwr = max_vbo[i].y; 290 | if (!isfinite(max_pwr)) 291 | max_pwr = - MAXFLOAT; /* Will be replaced by max() below */ 292 | 293 | vertex.x = ((float)i / (float)n) - 1.0f; 294 | #ifdef MAX_HOLD_LIVE 295 | vertex.y = max(live_vbo[i].y, max_pwr); 296 | #endif 297 | #ifdef MAX_HOLD_NORMAL 298 | for (i=0; i 19 | #include 20 | #include 21 | #include 22 | 23 | #include "cl.h" 24 | #include "gl.h" 25 | #include "fosphor.h" 26 | #include "private.h" 27 | 28 | 29 | struct fosphor * 30 | fosphor_init(void) 31 | { 32 | struct fosphor *self; 33 | int rv; 34 | 35 | /* Allocate structure */ 36 | self = malloc(sizeof(struct fosphor)); 37 | if (!self) 38 | return NULL; 39 | 40 | memset(self, 0, sizeof(struct fosphor)); 41 | 42 | /* Init GL/CL sub-states */ 43 | rv = fosphor_gl_init(self); 44 | if (rv) 45 | goto error; 46 | 47 | rv = fosphor_cl_init(self); 48 | if (rv) 49 | goto error; 50 | 51 | /* Buffers (if needed) */ 52 | if (!(self->flags & FLG_FOSPHOR_USE_CLGL_SHARING)) 53 | { 54 | self->img_waterfall = malloc(FOSPHOR_FFT_LEN * 1024 * sizeof(float)); 55 | self->img_histogram = malloc(FOSPHOR_FFT_LEN * 128 * sizeof(float)); 56 | self->buf_spectrum = malloc(2 * 2 * FOSPHOR_FFT_LEN * sizeof(float)); 57 | 58 | if (!self->img_waterfall || 59 | !self->img_histogram || 60 | !self->buf_spectrum) 61 | goto error; 62 | } 63 | 64 | /* Initial state */ 65 | fosphor_set_fft_window_default(self); 66 | fosphor_set_power_range(self, 0, 10); 67 | 68 | return self; 69 | 70 | /* Error path */ 71 | error: 72 | fosphor_release(self); 73 | return NULL; 74 | } 75 | 76 | void 77 | fosphor_release(struct fosphor *self) 78 | { 79 | if (!self) 80 | return; 81 | 82 | free(self->img_waterfall); 83 | free(self->img_histogram); 84 | free(self->buf_spectrum); 85 | 86 | fosphor_cl_release(self); 87 | fosphor_gl_release(self); 88 | 89 | free(self); 90 | } 91 | 92 | int 93 | fosphor_process(struct fosphor *self, void *samples, int len) 94 | { 95 | return fosphor_cl_process(self, samples, len); 96 | } 97 | 98 | void 99 | fosphor_draw(struct fosphor *self, struct fosphor_render *render) 100 | { 101 | if (fosphor_cl_finish(self) > 0) 102 | fosphor_gl_refresh(self); 103 | render->_wf_pos = fosphor_cl_get_waterfall_position(self); 104 | fosphor_gl_draw(self, render); 105 | } 106 | 107 | 108 | void 109 | fosphor_set_fft_window_default(struct fosphor *self) 110 | { 111 | int i; 112 | 113 | /* Default Hamming window (periodic) */ 114 | for (i=0; ifft_win[i] = (0.54f - 0.46f * cosf((2.0f * 3.141592f * fp) / ft)) * 1.855f; 118 | } 119 | 120 | fosphor_cl_load_fft_window(self, self->fft_win); 121 | } 122 | 123 | void 124 | fosphor_set_fft_window(struct fosphor *self, float *win) 125 | { 126 | memcpy(self->fft_win, win, sizeof(float) * FOSPHOR_FFT_LEN); 127 | fosphor_cl_load_fft_window(self, self->fft_win); 128 | } 129 | 130 | 131 | void 132 | fosphor_set_power_range(struct fosphor *self, int db_ref, int db_per_div) 133 | { 134 | int db0, db1; 135 | float k; 136 | float scale, offset; 137 | 138 | db0 = db_ref - 10*db_per_div; 139 | db1 = db_ref; 140 | 141 | k = log10f((float)FOSPHOR_FFT_LEN); 142 | 143 | offset = - ( k + ((float)db0 / 20.0f) ); 144 | scale = 20.0f / (float)(db1 - db0); 145 | 146 | self->power.db_ref = db_ref; 147 | self->power.db_per_div = db_per_div; 148 | self->power.scale = scale; 149 | self->power.offset = offset; 150 | 151 | fosphor_cl_set_histogram_range(self, scale, offset); 152 | } 153 | 154 | void 155 | fosphor_set_frequency_range(struct fosphor *self, double center, double span) 156 | { 157 | self->frequency.center = center; 158 | self->frequency.span = span; 159 | } 160 | 161 | 162 | void 163 | fosphor_render_defaults(struct fosphor_render *render) 164 | { 165 | render->pos_x = 0; 166 | render->pos_y = 0; 167 | render->width = 1024; 168 | render->height = 1024; 169 | 170 | render->options = 171 | FRO_LIVE | 172 | FRO_MAX_HOLD | 173 | FRO_HISTO | 174 | FRO_WATERFALL | 175 | FRO_LABEL_FREQ | 176 | FRO_LABEL_PWR | 177 | FRO_LABEL_TIME | 178 | FRO_COLOR_SCALE; 179 | 180 | render->histo_wf_ratio = 0.5f; 181 | render->freq_n_div = 10; 182 | render->freq_center = 0.5f; 183 | render->freq_span = 1.0f; 184 | render->wf_span = 1.0f; 185 | } 186 | 187 | void 188 | fosphor_render_refresh(struct fosphor_render *render) 189 | { 190 | int disp_spectrum, disp_waterfall; 191 | int avail, div, over, rsvd, rsvd_lr[2]; 192 | float y_top, y_bot; 193 | 194 | /* Which screen zone ? */ 195 | disp_spectrum = !!(render->options & (FRO_LIVE | FRO_MAX_HOLD | FRO_HISTO)); 196 | disp_waterfall = !!(render->options & FRO_WATERFALL); 197 | 198 | /* Split the X space */ 199 | rsvd_lr[0] = 10; 200 | rsvd_lr[1] = 10; 201 | 202 | if (render->options & (FRO_LABEL_PWR | FRO_LABEL_TIME)) 203 | rsvd_lr[0] += 30; 204 | 205 | if (render->options & FRO_COLOR_SCALE) 206 | rsvd_lr[1] += 10; 207 | 208 | rsvd = rsvd_lr[0] + rsvd_lr[1]; 209 | 210 | render->freq_n_div = ((int)(render->width - rsvd) / 80) & ~1; 211 | if (render->freq_n_div > 10) 212 | render->freq_n_div = 10; 213 | if (render->freq_n_div < 2) 214 | render->freq_n_div = 2; 215 | 216 | avail = render->width - rsvd; 217 | div = avail / render->freq_n_div; 218 | over = avail - (render->freq_n_div * div); 219 | 220 | render->_x_div = (float)div; 221 | render->_x[0] = render->pos_x + (float)(rsvd_lr[0]) + (float)(over / 2); 222 | render->_x[1] = render->_x[0] + (render->freq_n_div * render->_x_div) + 1.0f; 223 | render->_x_label = render->_x[0] - 5.0f; 224 | 225 | /* Split the Y space */ 226 | y_top = render->pos_y + (float)render->height - 10.0f; 227 | y_bot = render->pos_y + 10.0f; 228 | 229 | if (disp_spectrum) 230 | { 231 | /* Spacing */ 232 | rsvd = 10; 233 | if (disp_spectrum) rsvd += 10; 234 | if (disp_waterfall) rsvd += 10; 235 | if (render->options & FRO_LABEL_FREQ) rsvd += 10; 236 | 237 | /* Divisions */ 238 | if (disp_waterfall) { 239 | avail = (int)((float)(render->height - rsvd) * render->histo_wf_ratio); 240 | div = avail / 10; 241 | over = 0; 242 | } else { 243 | avail = render->height - rsvd; 244 | div = avail / 10; 245 | over = avail - (10 * div); 246 | } 247 | 248 | render->_y_histo_div = (float)div; 249 | render->_y_histo[1] = y_top - (float)(over / 2); 250 | render->_y_histo[0] = render->_y_histo[1] - (10.0f * render->_y_histo_div) - 1.0f; 251 | 252 | y_top = render->_y_histo[0] - (float)(over / 2) - 10.0f; 253 | } else { 254 | render->_y_histo_div = 0.0f; 255 | render->_y_histo[1] = 0.0f; 256 | render->_y_histo[0] = 0.0f; 257 | } 258 | 259 | if (render->options & FRO_LABEL_FREQ) { 260 | if (render->options & FRO_HISTO) { 261 | render->_y_label = y_top; 262 | y_top -= 10.0f; 263 | } else { 264 | render->_y_label = y_bot; 265 | y_bot += 10.0f; 266 | } 267 | } else { 268 | render->_y_label = 0.0f; 269 | } 270 | 271 | if (disp_waterfall) { 272 | render->_y_wf[1] = y_top; 273 | render->_y_wf[0] = y_bot; 274 | } else { 275 | render->_y_wf[1] = 0.0f; 276 | render->_y_wf[0] = 0.0f; 277 | } 278 | } 279 | 280 | 281 | /* 282 | * Mapping notes: 283 | * - We consider the int x/y coordinates to be the center of that pixels 284 | * (so we add 0.5f to each to get the OpenGL coordinates) 285 | * 286 | * - For frequency the OpenGL surface boundaries (so the 'thin' lines between 287 | * pixels) are defined as (center - 0.5 * span) -> (center + 0.5 * span) 288 | * 289 | * - For power the center of the drawn line maps to the annotated power level 290 | * 291 | * - For samples we map the first pixel line to sample 0 and the last pixel 292 | * line to fft_size * n_spectra. (That's approxiate but pretty much the 293 | * best we can do) 294 | */ 295 | 296 | double 297 | fosphor_pos2freq(struct fosphor *self, struct fosphor_render *render, int x) 298 | { 299 | float xf = (float)x + 0.5f; 300 | float xs = render->_x[1] - render->_x[0]; 301 | float xr = (xf - render->_x[0]) / xs; 302 | 303 | double view_center = self->frequency.center + self->frequency.span * (double)(render->freq_center - 0.5f); 304 | double view_span = self->frequency.span * (double)render->freq_span; 305 | 306 | return view_center + view_span * (double)(xr - 0.5f); 307 | } 308 | 309 | float 310 | fosphor_pos2pwr(struct fosphor *self, struct fosphor_render *render, int y) 311 | { 312 | float yf = (float)y; 313 | float ys = render->_y_histo[1] - render->_y_histo[0] - 1.0f; 314 | float yr = (yf - render->_y_histo[0]) / ys; 315 | 316 | return self->power.db_ref - 10.0f * self->power.db_per_div * (1.0f - yr); 317 | } 318 | 319 | int 320 | fosphor_pos2samp(struct fosphor *self, struct fosphor_render *render, int y) 321 | { 322 | float yf = (float)y; 323 | float ys = render->_y_wf[1] - render->_y_wf[0] - 1.0f; 324 | float yr = (yf - render->_y_wf[0]) / ys; 325 | 326 | return (int)((1.0f - yr) * (float)(FOSPHOR_FFT_LEN * 1024)) * render->wf_span; 327 | } 328 | 329 | int 330 | fosphor_freq2pos(struct fosphor *self, struct fosphor_render *render, double freq) 331 | { 332 | double view_center = self->frequency.center + self->frequency.span * (double)(render->freq_center - 0.5f); 333 | double view_span = self->frequency.span * (double)render->freq_span; 334 | 335 | double fr = ((freq - view_center) / view_span); 336 | float xs = render->_x[1] - render->_x[0]; 337 | 338 | return (int)roundf(render->_x[0] + (float)(fr + 0.5) * xs - 0.5f); 339 | } 340 | 341 | int 342 | fosphor_pwr2pos(struct fosphor *self, struct fosphor_render *render, float pwr) 343 | { 344 | float pr = (self->power.db_ref - pwr) / (10.0f * self->power.db_per_div); 345 | float ys = render->_y_histo[1] - render->_y_histo[0] - 1.0f; 346 | 347 | return (int)roundf(render->_y_histo[0] + (1.0f - pr) * ys); 348 | } 349 | 350 | int 351 | fosphor_samp2pos(struct fosphor *self, struct fosphor_render *render, int time) 352 | { 353 | float tf = (float)time; 354 | float tr = tf / ((float)(FOSPHOR_FFT_LEN * 1024) * render->wf_span); 355 | float ys = render->_y_wf[1] - render->_y_wf[0] - 1.0f; 356 | 357 | return (int)roundf(render->_y_wf[0] + (1.0f - tr) * ys); 358 | } 359 | 360 | int 361 | fosphor_render_pos_inside(struct fosphor_render *render, int x, int y) 362 | { 363 | int in = 0; 364 | float fx = (float)x; 365 | float fy = (float)y; 366 | 367 | /* Check X */ 368 | if ((fx >= render->_x[0]) && (fx < render->_x[1])) 369 | in |= 1; 370 | 371 | /* Histogram */ 372 | if (render->options & (FRO_HISTO | FRO_LIVE | FRO_MAX_HOLD)) 373 | { 374 | if ((fy >= render->_y_histo[0]) && (fy < render->_y_histo[1])) 375 | in |= 2; 376 | } 377 | 378 | /* Waterfall */ 379 | if (render->options & FRO_WATERFALL) 380 | { 381 | if ((fy >= render->_y_wf[0]) && (fy < render->_y_wf[1])) 382 | in |= 4; 383 | } 384 | 385 | /* Result */ 386 | return in; 387 | } 388 | 389 | 390 | /*! @} */ 391 | -------------------------------------------------------------------------------- /lib/fosphor/fosphor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * fosphor.h 3 | * 4 | * Main fosphor entry point 5 | * 6 | * Copyright (C) 2013-2021 Sylvain Munaut 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #pragma once 11 | 12 | /*! \defgroup fosphor 13 | * @{ 14 | */ 15 | 16 | /*! \file fosphor.h 17 | * \brief Main fosphor entry point 18 | */ 19 | 20 | struct fosphor; 21 | struct fosphor_render; 22 | 23 | 24 | /* Main API */ 25 | 26 | struct fosphor *fosphor_init(void); 27 | void fosphor_release(struct fosphor *self); 28 | 29 | int fosphor_process(struct fosphor *self, void *samples, int len); 30 | void fosphor_draw(struct fosphor *self, struct fosphor_render *render); 31 | 32 | void fosphor_set_fft_window_default(struct fosphor *self); 33 | void fosphor_set_fft_window(struct fosphor *self, float *win); 34 | 35 | void fosphor_set_power_range(struct fosphor *self, int db_ref, int db_per_div); 36 | void fosphor_set_frequency_range(struct fosphor *self, 37 | double center, double span); 38 | 39 | 40 | /* Render */ 41 | 42 | #define FOSPHOR_MAX_CHANNELS 8 43 | 44 | struct fosphor_channel 45 | { 46 | int enabled; /*!< \brief Showed (1) or hidden (0) */ 47 | float center; /*!< \brief Normalized center frequency */ 48 | float width; /*!< \brief Normalized bandwidth */ 49 | }; 50 | 51 | #define FRO_LIVE (1<<0) /*!< \brief Display live spectrum */ 52 | #define FRO_MAX_HOLD (1<<1) /*!< \brief Display max-hold spectrum */ 53 | #define FRO_HISTO (1<<2) /*!< \brief Display histogram */ 54 | #define FRO_WATERFALL (1<<3) /*!< \brief Display waterfall */ 55 | #define FRO_LABEL_FREQ (1<<4) /*!< \brief Display frequency labels */ 56 | #define FRO_LABEL_PWR (1<<5) /*!< \brief Display power labels */ 57 | #define FRO_LABEL_TIME (1<<6) /*!< \brief Display time labels */ 58 | #define FRO_CHANNELS (1<<7) /*!< \brief Display channels */ 59 | #define FRO_COLOR_SCALE (1<<8) /*!< \brief Display intensity color scale */ 60 | 61 | /*! \brief fosphor render options */ 62 | struct fosphor_render 63 | { 64 | /* User fields */ 65 | int pos_x; /*!< \brief X origin (lower left corner) */ 66 | int pos_y; /*!< \brief Y origin (lower left corner) */ 67 | int width; /*!< \brief Width */ 68 | int height; /*!< \brief Height */ 69 | int options; /*!< \brief Options (See FRO_??? constants) */ 70 | float histo_wf_ratio; /*!< \brief Ratio histogram/waterfall ]0,1[ */ 71 | int freq_n_div; /*!< \brief Number of frequency divisions */ 72 | float freq_center; /*!< \brief Frequency zoom center ]0,1[ */ 73 | float freq_span; /*!< \brief Frequency zoom span ]0,1] */ 74 | float wf_span; /*!< \brief Waterfall time zoom ]0,1] */ 75 | 76 | /*! \brief Displayed channels */ 77 | struct fosphor_channel channels[FOSPHOR_MAX_CHANNELS]; 78 | 79 | /* Private fields */ 80 | int _wf_pos; /*!< \brief (private) Waterfall position */ 81 | 82 | float _x_div; /*!< \brief (private) X divisions width */ 83 | float _x[2]; /*!< \brief (private) X endpoints */ 84 | float _x_label; /*!< \brief (private) X location for labels */ 85 | 86 | float _y_histo_div; /*!< \brief (private) Y histogram divisions height */ 87 | float _y_histo[2]; /*!< \brief (private) Y histogram endpoints */ 88 | float _y_wf[2]; /*!< \brief (private) Y waterfall endpoints */ 89 | float _y_label; /*!< \brief (private) Y location for label */ 90 | }; 91 | 92 | void fosphor_render_defaults(struct fosphor_render *render); 93 | void fosphor_render_refresh(struct fosphor_render *render); 94 | 95 | 96 | /* Position Mapping */ 97 | 98 | double fosphor_pos2freq(struct fosphor *self, struct fosphor_render *render, int x); 99 | float fosphor_pos2pwr (struct fosphor *self, struct fosphor_render *render, int y); 100 | int fosphor_pos2samp(struct fosphor *self, struct fosphor_render *render, int y); 101 | int fosphor_freq2pos(struct fosphor *self, struct fosphor_render *render, double freq); 102 | int fosphor_pwr2pos (struct fosphor *self, struct fosphor_render *render, float pwr); 103 | int fosphor_samp2pos(struct fosphor *self, struct fosphor_render *render, int time); 104 | 105 | int fosphor_render_pos_inside(struct fosphor_render *render, int x, int y); 106 | 107 | 108 | /*! @} */ 109 | -------------------------------------------------------------------------------- /lib/fosphor/gl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * gl.h 3 | * 4 | * OpenGL part of fosphor 5 | * 6 | * Copyright (C) 2013-2021 Sylvain Munaut 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #pragma once 11 | 12 | /*! \defgroup gl 13 | * @{ 14 | */ 15 | 16 | /*! \file gl.h 17 | * \brief OpenGL part of fosphor 18 | */ 19 | 20 | #include "gl_platform.h" 21 | 22 | struct fosphor; 23 | struct fosphor_render; 24 | 25 | int fosphor_gl_init(struct fosphor *self); 26 | void fosphor_gl_release(struct fosphor *self); 27 | 28 | 29 | enum fosphor_gl_id { 30 | GL_ID_TEX_WATERFALL, 31 | GL_ID_TEX_HISTOGRAM, 32 | GL_ID_VBO_SPECTRUM, 33 | }; 34 | 35 | GLuint fosphor_gl_get_shared_id(struct fosphor *self, 36 | enum fosphor_gl_id id); 37 | 38 | void fosphor_gl_refresh(struct fosphor *self); 39 | void fosphor_gl_draw(struct fosphor *self, struct fosphor_render *render); 40 | 41 | /*! @} */ 42 | -------------------------------------------------------------------------------- /lib/fosphor/gl_cmap.c: -------------------------------------------------------------------------------- 1 | /* 2 | * gl_cmap.c 3 | * 4 | * OpenGL float texture -> color mapping 5 | * 6 | * Copyright (C) 2013-2021 Sylvain Munaut 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | /*! \addtogroup gl/cmap 11 | * @{ 12 | */ 13 | 14 | /*! \file gl_cmap.c 15 | * \brief OpenGL float texture to color mapping 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "gl_platform.h" 26 | 27 | #include "gl_cmap.h" 28 | #include "resource.h" 29 | 30 | 31 | struct gl_cmap_shader 32 | { 33 | /* Status */ 34 | int loaded; 35 | 36 | /* Shader handles */ 37 | GLuint prog; 38 | GLuint shader; 39 | 40 | /* Uniforms */ 41 | GLint u_tex; 42 | GLint u_palette; 43 | GLint u_range; 44 | }; 45 | 46 | enum gl_cmap_shader_type 47 | { 48 | GL_CMAP_SHADER_SIMPLE, 49 | GL_CMAP_SHADER_BICUBIC, 50 | GL_CMAP_SHADER_FALLBACK, 51 | _GL_CMAP_SHADER_NUM 52 | }; 53 | 54 | struct fosphor_gl_cmap_ctx { 55 | struct gl_cmap_shader shaders[_GL_CMAP_SHADER_NUM]; 56 | }; 57 | 58 | 59 | /* -------------------------------------------------------------------------- */ 60 | /* Helpers / Internal API */ 61 | /* -------------------------------------------------------------------------- */ 62 | 63 | static int 64 | gl_cmap_init_shader(struct gl_cmap_shader *shader, const char *name) 65 | { 66 | const char *shader_src; 67 | GLint buf_len, orv; 68 | 69 | /* Load shader sources */ 70 | shader_src = resource_get(name, NULL); 71 | if (!shader_src) 72 | return -ENOENT; 73 | 74 | /* Allocate shader */ 75 | shader->prog = glCreateProgram(); 76 | shader->shader = glCreateShader(GL_FRAGMENT_SHADER); 77 | 78 | /* Compile / Link / Attach */ 79 | glShaderSource(shader->shader, 1, (const char **)&shader_src, NULL); 80 | glCompileShader(shader->shader); 81 | 82 | /* Check success and compile log */ 83 | glGetShaderiv(shader->shader, GL_COMPILE_STATUS, &orv); 84 | glGetShaderiv(shader->shader, GL_INFO_LOG_LENGTH, &buf_len); 85 | 86 | #if 1 87 | if ((buf_len > 0) && (orv != GL_TRUE)) 88 | #else 89 | if (buf_len > 0) 90 | #endif 91 | { 92 | char *buf = malloc(buf_len+1); 93 | 94 | glGetShaderInfoLog(shader->shader, buf_len, 0, buf); 95 | buf[buf_len] = '\0'; 96 | 97 | fprintf(stderr, "[!] gl_cmap shader compile log :\n%s\n", buf); 98 | 99 | free(buf); 100 | } 101 | 102 | if (orv != GL_TRUE) { 103 | fprintf(stderr, "[!] gl_cmap shader compilation failed (%s)\n", name); 104 | return -EINVAL; 105 | } 106 | 107 | /* Attach to program */ 108 | glAttachShader(shader->prog, shader->shader); 109 | glLinkProgram(shader->prog); 110 | 111 | /* Grab the uniform locations */ 112 | shader->u_tex = glGetUniformLocation(shader->prog, "tex"); 113 | shader->u_palette = glGetUniformLocation(shader->prog, "palette"); 114 | shader->u_range = glGetUniformLocation(shader->prog, "range"); 115 | 116 | /* Success */ 117 | shader->loaded = 1; 118 | 119 | return 0; 120 | } 121 | 122 | static void 123 | gl_cmap_release_shader(struct gl_cmap_shader *shader) 124 | { 125 | if (!shader->loaded) 126 | return; 127 | 128 | glDetachShader(shader->prog, shader->shader); 129 | glDeleteShader(shader->shader); 130 | glDeleteProgram(shader->prog); 131 | 132 | memset(shader, 0x00, sizeof(struct gl_cmap_shader)); 133 | } 134 | 135 | 136 | /* -------------------------------------------------------------------------- */ 137 | /* Exposed API */ 138 | /* -------------------------------------------------------------------------- */ 139 | 140 | struct fosphor_gl_cmap_ctx * 141 | fosphor_gl_cmap_init(void) 142 | { 143 | struct fosphor_gl_cmap_ctx *cmap_ctx; 144 | int rv; 145 | int need_fallback = 0; 146 | 147 | /* Allocate structure */ 148 | cmap_ctx = malloc(sizeof(struct fosphor_gl_cmap_ctx)); 149 | if (!cmap_ctx) 150 | return NULL; 151 | 152 | memset(cmap_ctx, 0, sizeof(struct fosphor_gl_cmap_ctx)); 153 | 154 | /* Init shaders */ 155 | rv = gl_cmap_init_shader( 156 | &cmap_ctx->shaders[GL_CMAP_SHADER_SIMPLE], 157 | "cmap_simple.glsl" 158 | ); 159 | if (rv) { 160 | fprintf(stderr, "[w] Color map shader 'simple' failed to load, will use fallback\n"); 161 | need_fallback = 1; 162 | } 163 | 164 | rv = gl_cmap_init_shader( 165 | &cmap_ctx->shaders[GL_CMAP_SHADER_BICUBIC], 166 | "cmap_bicubic.glsl" 167 | ); 168 | if (rv) { 169 | fprintf(stderr, "[w] Color map shader 'bicubic' failed to load, will use fallback\n"); 170 | need_fallback = 1; 171 | } 172 | 173 | if (need_fallback) { 174 | rv = gl_cmap_init_shader( 175 | &cmap_ctx->shaders[GL_CMAP_SHADER_FALLBACK], 176 | "cmap_fallback.glsl" 177 | ); 178 | if (rv) { 179 | fprintf(stderr, "[!] Color map shader 'fallback' failed, aborting\n"); 180 | goto error; 181 | } 182 | } 183 | 184 | /* All done */ 185 | return cmap_ctx; 186 | 187 | /* Error path */ 188 | error: 189 | fosphor_gl_cmap_release(cmap_ctx); 190 | 191 | return NULL; 192 | } 193 | 194 | void 195 | fosphor_gl_cmap_release(struct fosphor_gl_cmap_ctx *cmap_ctx) 196 | { 197 | /* Safety */ 198 | if (!cmap_ctx) 199 | return; 200 | 201 | /* Disable program */ 202 | glUseProgram(0); 203 | 204 | /* Release shaders */ 205 | gl_cmap_release_shader(&cmap_ctx->shaders[GL_CMAP_SHADER_SIMPLE]); 206 | gl_cmap_release_shader(&cmap_ctx->shaders[GL_CMAP_SHADER_BICUBIC]); 207 | gl_cmap_release_shader(&cmap_ctx->shaders[GL_CMAP_SHADER_FALLBACK]); 208 | 209 | /* Release container */ 210 | free(cmap_ctx); 211 | } 212 | 213 | 214 | void 215 | fosphor_gl_cmap_enable(struct fosphor_gl_cmap_ctx *cmap_ctx, 216 | GLuint tex_id, GLuint cmap_id, 217 | float scale, float offset, 218 | enum fosphor_gl_cmap_mode mode) 219 | { 220 | struct gl_cmap_shader *shader; 221 | float range[2]; 222 | int fmode; 223 | 224 | shader = &cmap_ctx->shaders[ 225 | mode == GL_CMAP_MODE_BICUBIC ? 226 | GL_CMAP_SHADER_BICUBIC : 227 | GL_CMAP_SHADER_SIMPLE 228 | ]; 229 | 230 | if (!shader->loaded) 231 | shader = &cmap_ctx->shaders[GL_CMAP_SHADER_FALLBACK]; 232 | 233 | /* Enable program */ 234 | glUseProgram(shader->prog); 235 | 236 | /* Texture unit 0: Main texture */ 237 | glActiveTexture(GL_TEXTURE0); 238 | glBindTexture(GL_TEXTURE_2D, tex_id); 239 | glUniform1i(shader->u_tex, 0); 240 | 241 | fmode = (mode == GL_CMAP_MODE_NEAREST) ? GL_NEAREST : GL_LINEAR; 242 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, fmode); 243 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, fmode); 244 | 245 | /* Texture unit 1: Palette 1D texture */ 246 | glActiveTexture(GL_TEXTURE1); 247 | glBindTexture(GL_TEXTURE_1D, cmap_id); 248 | glUniform1i(shader->u_palette, 1); 249 | 250 | /* Range definition */ 251 | range[0] = scale; 252 | range[1] = offset; 253 | glUniform2fv(shader->u_range, 1, range); 254 | } 255 | 256 | void 257 | fosphor_gl_cmap_disable(void) 258 | { 259 | /* Default to texture unit 0 */ 260 | glActiveTexture(GL_TEXTURE0); 261 | 262 | /* Disable program */ 263 | glUseProgram(0); 264 | } 265 | 266 | void 267 | fosphor_gl_cmap_draw_scale(GLuint cmap_id, 268 | float x0, float x1, float y0, float y1) 269 | { 270 | /* Enable texture-1D */ 271 | glActiveTexture(GL_TEXTURE0); 272 | glBindTexture(GL_TEXTURE_1D, cmap_id); 273 | glEnable(GL_TEXTURE_1D); 274 | 275 | glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); 276 | 277 | /* Draw QUAD */ 278 | glBegin( GL_QUADS ); 279 | glTexCoord1f(0.0f); glVertex2f(x0, y0); 280 | glTexCoord1f(0.0f); glVertex2f(x1, y0); 281 | glTexCoord1f(1.0f); glVertex2f(x1, y1); 282 | glTexCoord1f(1.0f); glVertex2f(x0, y1); 283 | glEnd(); 284 | 285 | /* Disable texturing */ 286 | glDisable(GL_TEXTURE_1D); 287 | } 288 | 289 | 290 | int 291 | fosphor_gl_cmap_generate(GLuint *cmap_id, gl_cmap_gen_func_t gfn, void *gfn_arg, int N) 292 | { 293 | uint32_t *rgba; 294 | 295 | /* Temp array for data */ 296 | rgba = malloc(sizeof(uint32_t) * N); 297 | if (!rgba) 298 | return -1; 299 | 300 | /* Allocate texture ID */ 301 | glGenTextures(1, cmap_id); 302 | 303 | /* Select it */ 304 | glBindTexture(GL_TEXTURE_1D, *cmap_id); 305 | 306 | /* Generate texture data */ 307 | gfn(rgba, N, gfn_arg); 308 | 309 | /* Upload data */ 310 | glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, N, 0, 311 | GL_RGBA, GL_UNSIGNED_BYTE, rgba); 312 | 313 | /* Configure texture behavior */ 314 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 315 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 316 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 317 | 318 | /* Release temp array */ 319 | free(rgba); 320 | 321 | return 0; 322 | } 323 | 324 | /*! @} */ 325 | -------------------------------------------------------------------------------- /lib/fosphor/gl_cmap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * gl_cmap.h 3 | * 4 | * OpenGL float texture -> color mapping 5 | * 6 | * Copyright (C) 2013-2021 Sylvain Munaut 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #pragma once 11 | 12 | /*! \defgroup gl/cmap 13 | * @{ 14 | */ 15 | 16 | /*! \file gl_cmap.h 17 | * \brief OpenGL float texture to color mapping 18 | */ 19 | 20 | #include 21 | 22 | #include "gl_platform.h" 23 | 24 | 25 | struct fosphor_gl_cmap_ctx; 26 | 27 | enum fosphor_gl_cmap_mode { 28 | GL_CMAP_MODE_NEAREST, 29 | GL_CMAP_MODE_BILINEAR, 30 | GL_CMAP_MODE_BICUBIC, 31 | }; 32 | 33 | 34 | struct fosphor_gl_cmap_ctx *fosphor_gl_cmap_init(void); 35 | void fosphor_gl_cmap_release(struct fosphor_gl_cmap_ctx *cmap_ctx); 36 | 37 | void fosphor_gl_cmap_enable(struct fosphor_gl_cmap_ctx *cmap_ctx, 38 | GLuint tex_id, GLuint cmap_id, 39 | float scale, float offset, 40 | enum fosphor_gl_cmap_mode mode); 41 | void fosphor_gl_cmap_disable(void); 42 | 43 | void fosphor_gl_cmap_draw_scale(GLuint cmap_id, 44 | float x0, float x1, float y0, float y1); 45 | 46 | typedef int (*gl_cmap_gen_func_t)(uint32_t *rgba, int N, void *arg); 47 | int fosphor_gl_cmap_generate(GLuint *cmap_id, gl_cmap_gen_func_t gfn, void *gfn_arg, int N); 48 | 49 | 50 | /*! @} */ 51 | -------------------------------------------------------------------------------- /lib/fosphor/gl_cmap_gen.c: -------------------------------------------------------------------------------- 1 | /* 2 | * gl_cmap_gen.c 3 | * 4 | * OpenGL color map generators 5 | * 6 | * Copyright (C) 2013-2021 Sylvain Munaut 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | /*! \addtogroup gl/cmap 11 | * @{ 12 | */ 13 | 14 | /*! \file gl_cmap_gen.c 15 | * \brief OpenGL color map generators 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "gl_cmap_gen.h" 25 | #include "resource.h" 26 | 27 | 28 | static void 29 | _hsv2rgb(float *rgb, float *hsv) 30 | { 31 | int i; 32 | float h, s, v; 33 | float r, g, b; 34 | float f, p, q, t; 35 | 36 | /* Unpack input */ 37 | h = hsv[0]; 38 | s = hsv[1]; 39 | v = hsv[2]; 40 | 41 | if( s <= 0.0f ) { 42 | /* achromatic (grey) */ 43 | r = g = b = v; 44 | goto done; 45 | } 46 | 47 | h *= 5.0f; /* sector 0 to 5 */ 48 | i = floor(h); 49 | f = h - i; /* fractional part of h */ 50 | p = v * ( 1 - s ); 51 | q = v * ( 1 - s * f ); 52 | t = v * ( 1 - s * ( 1 - f ) ); 53 | 54 | switch (i % 6) { 55 | case 0: 56 | r = v; 57 | g = t; 58 | b = p; 59 | break; 60 | case 1: 61 | r = q; 62 | g = v; 63 | b = p; 64 | break; 65 | case 2: 66 | r = p; 67 | g = v; 68 | b = t; 69 | break; 70 | case 3: 71 | r = p; 72 | g = q; 73 | b = v; 74 | break; 75 | case 4: 76 | r = t; 77 | g = p; 78 | b = v; 79 | break; 80 | case 5: 81 | default: 82 | r = v; 83 | g = p; 84 | b = q; 85 | break; 86 | } 87 | 88 | done: 89 | /* Pack results */ 90 | rgb[0] = r; 91 | rgb[1] = g; 92 | rgb[2] = b; 93 | } 94 | 95 | static void 96 | _set_rgba_from_rgb(uint32_t *rgba, float r, float g, float b) 97 | { 98 | unsigned char rc,gc,bc,ac; 99 | 100 | rc = (unsigned char) roundf(r * 255.0f); 101 | gc = (unsigned char) roundf(g * 255.0f); 102 | bc = (unsigned char) roundf(b * 255.0f); 103 | ac = 255; 104 | 105 | *rgba = (ac << 24) | (bc << 16) | (gc << 8) | rc; 106 | } 107 | 108 | static void 109 | _set_rgba_from_hsv(uint32_t *rgba, float h, float s, float v) 110 | { 111 | float hsv[3], rgb[3]; 112 | 113 | hsv[0] = h; 114 | hsv[1] = s; 115 | hsv[2] = v; 116 | 117 | _hsv2rgb(rgb, hsv); 118 | 119 | _set_rgba_from_rgb(rgba, rgb[0], rgb[1], rgb[2]); 120 | } 121 | 122 | 123 | static inline uint32_t 124 | _rgba_interpolate(uint32_t *rgba, int sz, int p, int N) 125 | { 126 | int pos_i = (p * (sz-1)) / (N-1); 127 | int pos_f = (p * (sz-1)) - (pos_i * (N-1)); 128 | uint32_t vl, vh, vf = 0; 129 | int i; 130 | 131 | if (pos_f == 0) 132 | return rgba[pos_i]; 133 | 134 | vl = rgba[pos_i]; 135 | vh = rgba[pos_i+1]; 136 | 137 | for (i=0; i<4; i++) 138 | { 139 | uint32_t nv = 140 | ((vl >> (8 * i)) & 0xff) * ((N-1) - pos_f) + 141 | ((vh >> (8 * i)) & 0xff) * pos_f; 142 | 143 | vf |= ((nv / (N-1)) & 0xff) << (8 * i); 144 | } 145 | 146 | return vf; 147 | } 148 | 149 | 150 | int 151 | fosphor_gl_cmap_histogram(uint32_t *rgba, int N, void *arg) 152 | { 153 | int i; 154 | int m = N >> 4; 155 | 156 | for (i=0; i 204 | int 205 | fosphor_gl_cmap_png(uint32_t *rgba, int N, void *arg) 206 | { 207 | const char *rsrc_name = arg; 208 | png_image img; 209 | const void *png_data = NULL; 210 | void *png_rgba = NULL; 211 | int png_len, i, rv; 212 | 213 | /* Grab the file */ 214 | png_data = resource_get(rsrc_name, &png_len); 215 | if (!png_data) 216 | return -ENOENT; 217 | 218 | /* Read PNG */ 219 | memset(&img, 0x00, sizeof(img)); 220 | img.version = PNG_IMAGE_VERSION; 221 | 222 | rv = png_image_begin_read_from_memory(&img, png_data, png_len); 223 | if (!rv) { 224 | rv = -EINVAL; 225 | goto error; 226 | } 227 | 228 | img.format = PNG_FORMAT_RGBA; 229 | 230 | png_rgba = malloc(sizeof(uint32_t) * img.width * img.height); 231 | if (!png_rgba) { 232 | rv = -ENOMEM; 233 | goto error; 234 | } 235 | 236 | rv = png_image_finish_read(&img, 237 | NULL, /* background */ 238 | png_rgba, /* buffer */ 239 | sizeof(uint32_t) * img.width, /* row_stride */ 240 | NULL /* colormap */ 241 | ); 242 | if (!rv) { 243 | rv = -EINVAL; 244 | goto error; 245 | } 246 | 247 | /* Interpolate the PNG to the requested linear scale */ 248 | for (i=0; i 21 | 22 | int fosphor_gl_cmap_histogram(uint32_t *rgba, int N, void *arg); 23 | int fosphor_gl_cmap_waterfall(uint32_t *rgba, int N, void *arg); 24 | int fosphor_gl_cmap_prog(uint32_t *rgba, int N, void *arg); 25 | int fosphor_gl_cmap_png(uint32_t *rgba, int N, void *rsrc_name); 26 | 27 | /*! @} */ 28 | -------------------------------------------------------------------------------- /lib/fosphor/gl_font.c: -------------------------------------------------------------------------------- 1 | /* 2 | * gl_font.c 3 | * 4 | * Basic OpenGL font rendering 5 | * 6 | * Copyright (C) 2013-2021 Sylvain Munaut 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | /*! \addtogroup gl/font 11 | * @{ 12 | */ 13 | 14 | /*! \file gl_font.c 15 | * \brief Basic OpenGL font rendering 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include FT_FREETYPE_H 25 | #include FT_GLYPH_H 26 | #include FT_BITMAP_H 27 | #include FT_LCD_FILTER_H 28 | 29 | #include "gl_platform.h" 30 | 31 | #include "gl_font.h" 32 | 33 | 34 | #define GLF_MIN_CHR 32 /*!< Minimum ascii char we display */ 35 | #define GLF_MAX_CHR 127 /*!< Maximum ascii char we display */ 36 | #define GLF_N_CHR (GLF_MAX_CHR - GLF_MIN_CHR + 1) 37 | 38 | struct gl_glyph 39 | { 40 | FT_Glyph glyph; 41 | int advance_x; 42 | }; 43 | 44 | struct gl_font 45 | { 46 | /* Book keeping */ 47 | int loaded; 48 | 49 | int height; 50 | int flags; 51 | 52 | /* Free type stuff */ 53 | FT_Library library; /*!< */ 54 | FT_Face face; /*!< */ 55 | 56 | struct gl_glyph glyphs[GLF_N_CHR]; 57 | 58 | /* GL stuff */ 59 | GLuint vbo; 60 | 61 | /* Texture */ 62 | struct { 63 | int width; 64 | int height; 65 | GLuint id; 66 | } tex; 67 | 68 | /* Glyphs */ 69 | struct { 70 | int width; 71 | int height; 72 | int ofs_x; 73 | int ofs_y; 74 | } glyph_bb; 75 | }; 76 | 77 | 78 | struct gl_font * 79 | glf_alloc(int height, int flags) 80 | { 81 | struct gl_font *glf; 82 | FT_Error ftr; 83 | 84 | glf = calloc(1, sizeof(struct gl_font)); 85 | if (!glf) 86 | return NULL; 87 | 88 | ftr = FT_Init_FreeType(&glf->library); 89 | if (ftr) 90 | goto err; 91 | 92 | if (flags & GLF_FLG_LCD) 93 | FT_Library_SetLcdFilter(glf->library, FT_LCD_FILTER_DEFAULT); 94 | 95 | glf->height = height; 96 | glf->flags = flags; 97 | 98 | glGenBuffers(1, &glf->vbo); 99 | 100 | return glf; 101 | 102 | err: 103 | glf_free(glf); 104 | 105 | return NULL; 106 | } 107 | 108 | void 109 | glf_free(struct gl_font *glf) 110 | { 111 | if (!glf) 112 | return; 113 | 114 | if (glf->loaded) { 115 | int i; 116 | 117 | for (i=0; iglyphs[i].glyph) 119 | FT_Done_Glyph(glf->glyphs[i].glyph); 120 | 121 | glDeleteTextures(1, &glf->tex.id); 122 | } 123 | 124 | glDeleteBuffers(1, &glf->vbo); 125 | 126 | if (glf->face) 127 | FT_Done_Face(glf->face); 128 | 129 | if (glf->library) 130 | FT_Done_FreeType(glf->library); 131 | 132 | free(glf); 133 | } 134 | 135 | static int 136 | np2(int x) 137 | { 138 | int r = 1; 139 | while (r && r < x) 140 | r <<= 1; 141 | return r; 142 | } 143 | 144 | static int 145 | _glf_init_glyph(struct gl_font *glf, int ch, uint8_t *data) 146 | { 147 | FT_Glyph glyph = NULL; 148 | FT_BitmapGlyph bitmap_glyph; 149 | FT_Bitmap bitmap; 150 | FT_Error ftr; 151 | int tx, ty, px, py; 152 | int x, y, m, rv; 153 | 154 | ftr = FT_Load_Glyph(glf->face, FT_Get_Char_Index(glf->face, ch), FT_LOAD_DEFAULT); 155 | if (ftr) { 156 | rv = -ENOMEM; 157 | goto err; 158 | } 159 | 160 | ftr = FT_Get_Glyph(glf->face->glyph, &glyph); 161 | if (ftr) { 162 | rv = -ENOMEM; 163 | goto err; 164 | } 165 | 166 | FT_Glyph_To_Bitmap( 167 | &glyph, 168 | glf->flags & GLF_FLG_LCD ? 169 | FT_RENDER_MODE_LCD : FT_RENDER_MODE_NORMAL, 170 | 0, 1 171 | ); 172 | 173 | bitmap_glyph = (FT_BitmapGlyph)glyph; 174 | bitmap = bitmap_glyph->bitmap; 175 | 176 | m = glf->flags & GLF_FLG_LCD ? 3 : 1; 177 | 178 | px = ((ch - GLF_MIN_CHR) & 15) * glf->glyph_bb.width; 179 | py = ((ch - GLF_MIN_CHR) >> 4) * glf->glyph_bb.height; 180 | 181 | for (y=0; yglyph_bb.ofs_x + bitmap_glyph->left)) + x; 184 | ty = py + glf->glyph_bb.height - (glf->glyph_bb.ofs_y + bitmap_glyph->top) + y; 185 | 186 | data[m * glf->tex.width * ty + tx] = 187 | bitmap.buffer[bitmap.pitch*y + x]; 188 | } 189 | } 190 | 191 | glf->glyphs[ch - GLF_MIN_CHR].glyph = glyph; 192 | glf->glyphs[ch - GLF_MIN_CHR].advance_x = glf->face->glyph->advance.x >> 6; 193 | 194 | return 0; 195 | 196 | err: 197 | if (glyph) 198 | FT_Done_Glyph(glyph); 199 | 200 | return rv; 201 | } 202 | 203 | static int 204 | _glf_init_face(struct gl_font *glf) 205 | { 206 | int min_x, max_x, min_y, max_y; 207 | uint8_t *data; 208 | int i; 209 | 210 | /* Set request size */ 211 | FT_Set_Char_Size(glf->face, glf->height << 6, glf->height << 6, 96, 96); 212 | 213 | /* Find char BB and select texture size */ 214 | min_x = FT_MulFix(glf->face->bbox.xMin, glf->face->size->metrics.x_scale) >> 6; 215 | max_x = FT_MulFix(glf->face->bbox.xMax, glf->face->size->metrics.x_scale) >> 6; 216 | min_y = FT_MulFix(glf->face->bbox.yMin, glf->face->size->metrics.x_scale) >> 6; 217 | max_y = FT_MulFix(glf->face->bbox.yMax, glf->face->size->metrics.x_scale) >> 6; 218 | 219 | glf->glyph_bb.width = max_x - min_x + 1; 220 | glf->glyph_bb.height = max_y - min_y + 1; 221 | glf->glyph_bb.ofs_x = - min_x; 222 | glf->glyph_bb.ofs_y = - min_y; 223 | 224 | glf->tex.width = np2(glf->glyph_bb.width * 16); 225 | glf->tex.height = np2(glf->glyph_bb.height * (GLF_N_CHR + 15) >> 4); 226 | 227 | /* Raw data array */ 228 | data = calloc(glf->tex.width * glf->tex.height, glf->flags & GLF_FLG_LCD ? 3 : 1); 229 | if (!data) 230 | return -ENOMEM; 231 | 232 | /* Init all glyphs */ 233 | for (i=GLF_MIN_CHR; i<=GLF_MAX_CHR; i++) 234 | _glf_init_glyph(glf, i, data); 235 | 236 | /* Create GL texture */ 237 | glGenTextures(1, &glf->tex.id); 238 | 239 | glBindTexture(GL_TEXTURE_2D, glf->tex.id); 240 | glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); 241 | glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); 242 | 243 | if (glf->flags & GLF_FLG_LCD) { 244 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, glf->tex.width, glf->tex.height, 0, 245 | GL_RGB, GL_UNSIGNED_BYTE, data); 246 | } else { 247 | glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, glf->tex.width, glf->tex.height, 0, 248 | GL_ALPHA, GL_UNSIGNED_BYTE, data); 249 | } 250 | 251 | /* Done */ 252 | free(data); 253 | 254 | glf->loaded = 1; 255 | 256 | return 0; 257 | } 258 | 259 | int 260 | glf_load_face_file(struct gl_font *glf, const char *filename) 261 | { 262 | FT_Error ftr; 263 | 264 | if (glf->loaded) 265 | return -EBUSY; 266 | 267 | ftr = FT_New_Face(glf->library, filename, 0, &glf->face); 268 | if (ftr) 269 | return -EINVAL; 270 | 271 | return _glf_init_face(glf); 272 | } 273 | 274 | int 275 | glf_load_face_mem(struct gl_font *glf, const void *data, size_t len) 276 | { 277 | FT_Error ftr; 278 | 279 | if (glf->loaded) 280 | return -EBUSY; 281 | 282 | ftr = FT_New_Memory_Face(glf->library, data, len, 0, &glf->face); 283 | if (ftr) 284 | return -EINVAL; 285 | 286 | return _glf_init_face(glf); 287 | } 288 | 289 | static void 290 | _glf_add_char(const struct gl_font *glf, float *data, char c, float x) 291 | { 292 | float u0, v0, u1, v1; 293 | float cw, ch, crw, crh; 294 | 295 | c -= GLF_MIN_CHR; 296 | 297 | cw = (float)glf->glyph_bb.width; 298 | ch = (float)glf->glyph_bb.height; 299 | crw = cw / (float)glf->tex.width; 300 | crh = ch / (float)glf->tex.height; 301 | 302 | u0 = (c & 15) * crw; 303 | v0 = (c >> 4) * crh; 304 | u1 = u0 + crw; 305 | v1 = v0 + crh; 306 | 307 | #define VTX(x,y,u,v) do { \ 308 | data[0] = data[1] = data[2] = 1.0f; data[3] = 0.0f; \ 309 | data[4] = x; data[5] = y; data[6] = u, data[7] = v; \ 310 | data += 8; \ 311 | } while (0) 312 | 313 | VTX(x, 0.0f, u0, v1); 314 | VTX(x + cw, 0.0f, u1, v1); 315 | VTX(x + cw, ch, u1, v0); 316 | VTX(x, ch, u0, v0); 317 | 318 | #undef VTX 319 | } 320 | 321 | float 322 | glf_width_str(const struct gl_font *glf, const char *str) 323 | { 324 | float xb = 0.0f; 325 | int i; 326 | 327 | for (i=0; str[i]; i++) { 328 | xb += (float)glf->glyphs[str[i] - GLF_MIN_CHR].advance_x; 329 | } 330 | 331 | return xb; 332 | } 333 | 334 | void 335 | glf_draw_str(const struct gl_font *glf, 336 | float x, enum glf_align x_align, 337 | float y, enum glf_align y_align, 338 | const char *str) 339 | { 340 | float *data; 341 | float xb, xofs, yofs; 342 | int i; 343 | 344 | /* Temporary buffer for vertex data */ 345 | data = malloc(8 * sizeof(float) * 4 * strlen(str)); 346 | 347 | /* Add chars to the buffer */ 348 | xb = 0.0f; 349 | 350 | for (i=0; str[i]; i++) { 351 | _glf_add_char(glf, &data[32*i], str[i], xb); 352 | xb += (float)glf->glyphs[str[i] - GLF_MIN_CHR].advance_x; 353 | } 354 | 355 | /* Align */ 356 | if (x_align == GLF_CENTER) { 357 | xofs = x - roundf(xb / 2.0f); 358 | } else if (x_align == GLF_RIGHT) { 359 | xofs = x - xb; 360 | } else { 361 | xofs = x; 362 | } 363 | 364 | xofs -= (float) glf->glyph_bb.ofs_x; 365 | 366 | if (y_align == GLF_TOP) { 367 | yofs = y - (float)glf->glyph_bb.height; 368 | } else if (y_align == GLF_CENTER) { 369 | yofs = y - roundf((float)glf->glyph_bb.height / 2.0f); 370 | } else { 371 | yofs = y; 372 | } 373 | 374 | yofs += (float) glf->glyph_bb.ofs_y; 375 | 376 | for (i=0; i<4*strlen(str); i++) { 377 | data[8*i + 4] += xofs; 378 | data[8*i + 5] += yofs; 379 | } 380 | 381 | /* Draw */ 382 | #if 1 383 | glBindBuffer(GL_ARRAY_BUFFER, 0); 384 | 385 | glColorPointer (4, GL_FLOAT, 8 * sizeof(float), data + 0); 386 | glVertexPointer (2, GL_FLOAT, 8 * sizeof(float), data + 4); 387 | glTexCoordPointer(2, GL_FLOAT, 8 * sizeof(float), data + 6); 388 | 389 | glEnableClientState(GL_COLOR_ARRAY); 390 | glEnableClientState(GL_VERTEX_ARRAY); 391 | glEnableClientState(GL_TEXTURE_COORD_ARRAY); 392 | 393 | glDrawArrays(GL_QUADS, 0, 4*strlen(str)); 394 | 395 | glDisableClientState(GL_TEXTURE_COORD_ARRAY); 396 | glDisableClientState(GL_VERTEX_ARRAY); 397 | glDisableClientState(GL_COLOR_ARRAY); 398 | #else 399 | glBegin( GL_QUADS ); 400 | for (i=0; i<4*strlen(str); i++) { 401 | glColor4f(data[8*i + 0], data[8*i + 1], data[8*i + 2], data[8*i + 3]); 402 | glTexCoord2f(data[8*i + 6], data[8*i + 7]); 403 | glVertex2f(data[8*i + 4], data[8*i + 5]); 404 | } 405 | glEnd(); 406 | #endif 407 | 408 | /* Done */ 409 | free(data); 410 | } 411 | 412 | void 413 | glf_printf(const struct gl_font *glf, 414 | float x, enum glf_align x_align, 415 | float y, enum glf_align y_align, 416 | const char *fmt, ...) 417 | { 418 | va_list va1, va2; 419 | char *buf; 420 | char static_buf[128]; 421 | int l; 422 | 423 | /* Grab 2 copies of the arguments */ 424 | va_start(va1, fmt); 425 | va_copy(va2, va1); 426 | 427 | /* Print to buffer (try a stack, fallback to heap if needed) */ 428 | l = vsnprintf(static_buf, sizeof(static_buf), fmt, va1); 429 | 430 | if (l >= sizeof(static_buf)) { 431 | buf = malloc(l + 1); 432 | if (!buf) 433 | goto error; 434 | 435 | vsnprintf(buf, l, fmt, va2); 436 | } else { 437 | buf = static_buf; 438 | } 439 | 440 | /* Draw it */ 441 | glf_draw_str(glf, x, x_align, y, y_align, buf); 442 | 443 | /* Release everything */ 444 | error: 445 | va_end(va2); 446 | va_end(va1); 447 | 448 | if (buf && buf != static_buf) 449 | free(buf); 450 | } 451 | 452 | void 453 | glf_begin(const struct gl_font *glf, float fg_color[3]) 454 | { 455 | glEnable(GL_TEXTURE_2D); 456 | glEnable(GL_BLEND); 457 | 458 | glBindTexture(GL_TEXTURE_2D, glf->tex.id); 459 | 460 | glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); 461 | 462 | if (glf->flags & GLF_FLG_LCD) { 463 | glColor3f(1.0f, 1.0f, 1.0f); 464 | glBlendColor(fg_color[0], fg_color[1], fg_color[2], 0.0f); 465 | glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR); 466 | } else { 467 | glColor3f(fg_color[0], fg_color[1], fg_color[2]); 468 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 469 | } 470 | } 471 | 472 | void 473 | glf_end(void) 474 | { 475 | glDisable(GL_BLEND); 476 | glDisable(GL_TEXTURE_2D); 477 | } 478 | 479 | /*! @} */ 480 | -------------------------------------------------------------------------------- /lib/fosphor/gl_font.h: -------------------------------------------------------------------------------- 1 | /* 2 | * gl_font.h 3 | * 4 | * Basic OpenGL font rendering 5 | * 6 | * Copyright (C) 2013-2021 Sylvain Munaut 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #pragma once 11 | 12 | /*! \defgroup gl/font 13 | * @{ 14 | */ 15 | 16 | /*! \file gl_font.h 17 | * \brief Basic OpenGL font rendering 18 | */ 19 | 20 | #ifdef _MSC_VER 21 | # define ATTR_FORMAT(a,b,c) 22 | #else 23 | # define ATTR_FORMAT(a,b,c) __attribute__((format(a,b,c))) 24 | #endif 25 | 26 | 27 | struct gl_font; 28 | 29 | #define GLF_FLG_LCD (1 << 0) 30 | 31 | enum glf_align 32 | { 33 | GLF_LEFT, 34 | GLF_RIGHT, 35 | GLF_TOP, 36 | GLF_BOTTOM, 37 | GLF_CENTER, 38 | }; 39 | 40 | struct gl_font *glf_alloc(int height, int flags); 41 | void glf_free(struct gl_font *glf); 42 | 43 | int glf_load_face_file(struct gl_font *glf, const char *filename); 44 | int glf_load_face_mem(struct gl_font *glf, const void *data, size_t len); 45 | 46 | float glf_width_str(const struct gl_font *glf, const char *str); 47 | 48 | void glf_draw_str(const struct gl_font *glf, 49 | float x, enum glf_align x_align, 50 | float y, enum glf_align y_align, 51 | const char *str); 52 | 53 | void glf_printf(const struct gl_font *glf, 54 | float x, enum glf_align x_align, 55 | float y, enum glf_align y_align, 56 | const char *fmt, ...) ATTR_FORMAT(printf, 6, 7); 57 | 58 | void glf_begin(const struct gl_font *glf, float fg_color[3]); 59 | void glf_end(void); 60 | 61 | 62 | /*! @} */ 63 | -------------------------------------------------------------------------------- /lib/fosphor/gl_platform.h: -------------------------------------------------------------------------------- 1 | /* 2 | * gl_platform.h 3 | * 4 | * Wrapper to select proper OpenGL headers for various platforms 5 | * 6 | * Copyright (C) 2013-2021 Sylvain Munaut 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | /*! \file gl_platform.h 11 | * \brief Wrapper to select proper OpenGL headers for various platforms 12 | */ 13 | 14 | #if !defined(_WIN32) && (defined(__WIN32__) || defined(WIN32) || defined(__CYGWIN__)) 15 | # define _WIN32 16 | #endif 17 | 18 | #if defined(__APPLE__) || defined(MACOSX) 19 | # define GL_GLEXT_PROTOTYPES 20 | # include 21 | #elif defined(_WIN32) 22 | # include 23 | #else 24 | # define GL_GLEXT_PROTOTYPES 25 | # include 26 | #endif 27 | -------------------------------------------------------------------------------- /lib/fosphor/llist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * llist.h 3 | * 4 | * Simple double-linked list. Interface similar to the linux kernel 5 | * one, but heavily simplified and rewritten to compile on other compilers 6 | * than GCC. 7 | * 8 | * Copyright (C) 2013-2021 Sylvain Munaut 9 | * SPDX-License-Identifier: GPL-3.0-or-later 10 | */ 11 | 12 | #pragma once 13 | 14 | /*! \defgroup llist 15 | * @{ 16 | */ 17 | 18 | /*! \file llist.h 19 | * \brief Simple double-linked list 20 | */ 21 | 22 | #include 23 | 24 | 25 | struct llist_head { 26 | struct llist_head *next, *prev; 27 | }; 28 | 29 | #define LLIST_HEAD_INIT(name) { &(name), &(name) } 30 | 31 | #define LLIST_HEAD(name) \ 32 | struct llist_head name = LLIST_HEAD_INIT(name) 33 | 34 | /** 35 | * \brief Add a new entry after the specified head 36 | * \param[in] new new entry to be added 37 | * \param[in] head llist head to add it after 38 | */ 39 | static inline void llist_add(struct llist_head *_new, struct llist_head *head) 40 | { 41 | head->next->prev = _new; 42 | _new->next = head->next; 43 | _new->prev = head; 44 | head->next = _new; 45 | } 46 | 47 | /** 48 | * \brief Deletes entry from llist 49 | * \param[in] entry the element to delete from the llist 50 | */ 51 | static inline void llist_del(struct llist_head *entry) 52 | { 53 | entry->next->prev = entry->prev; 54 | entry->prev->next = entry->next; 55 | entry->next = (struct llist_head *)0; 56 | entry->prev = (struct llist_head *)0; 57 | } 58 | 59 | /** 60 | * \brief Get the struct for this entry 61 | * \param[in] ptr the &struct llist_head pointer 62 | * \param[in] type the type of the struct this is embedded in 63 | * \param[in] member the name of the llist_struct within the struct 64 | * \returns The struct for this entry 65 | */ 66 | #define llist_entry(ptr, type, member) \ 67 | ((type *)( (char *)(ptr) - offsetof(type, member) )) 68 | 69 | /** 70 | * \brief Iterate over llist of given type 71 | * \param[in] type the type of the loop counter 72 | * \param[out] pos the type * to use as a loop counter 73 | * \param[in] head the head for your llist 74 | * \param[in] member the name of the llist_struct within the struct 75 | */ 76 | #define llist_for_each_entry(type, pos, head, member) \ 77 | for (pos = llist_entry((head)->next, type, member); \ 78 | &pos->member != (head); \ 79 | pos = llist_entry(pos->member.next, type, member)) 80 | 81 | /*! @} */ 82 | -------------------------------------------------------------------------------- /lib/fosphor/main.c: -------------------------------------------------------------------------------- 1 | #if !defined(_WIN32) && (defined(__WIN32__) || defined(WIN32) || defined(__CYGWIN__)) 2 | # define _WIN32 3 | #endif 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef _WIN32 15 | #include 16 | #endif 17 | #include 18 | 19 | #include "fosphor.h" 20 | #include "private.h" 21 | 22 | struct app_state 23 | { 24 | struct fosphor *fosphor; 25 | struct fosphor_render render_main; 26 | struct fosphor_render render_zoom; 27 | 28 | FILE *src_fh; 29 | void *src_buf; 30 | 31 | int w, h; 32 | 33 | int db_ref, db_per_div_idx; 34 | float ratio; 35 | double zoom_width, zoom_center; 36 | int zoom_enable; 37 | }; 38 | 39 | static struct app_state _g_as, *g_as = &_g_as; 40 | 41 | static const int k_db_per_div[] = { 1, 2, 5, 10, 20 }; 42 | 43 | 44 | /* ------------------------------------------------------------------------ */ 45 | /* Timing utils */ 46 | /* ------------------------------------------------------------------------ */ 47 | 48 | #if defined(_WIN32) && !defined(__MINGW32__) 49 | 50 | #include 51 | #include 52 | 53 | #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) 54 | # define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 55 | #else 56 | # define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL 57 | #endif 58 | 59 | struct timezone 60 | { 61 | int tz_minuteswest; /* minutes W of Greenwich */ 62 | int tz_dsttime; /* type of dst correction */ 63 | }; 64 | 65 | static int 66 | gettimeofday(struct timeval *tv, struct timezone *tz) 67 | { 68 | FILETIME ft; 69 | unsigned __int64 tmpres = 0; 70 | static int tzflag; 71 | 72 | if (NULL != tv) 73 | { 74 | GetSystemTimeAsFileTime(&ft); 75 | 76 | tmpres |= ft.dwHighDateTime; 77 | tmpres <<= 32; 78 | tmpres |= ft.dwLowDateTime; 79 | 80 | /*converting file time to unix epoch*/ 81 | tmpres -= DELTA_EPOCH_IN_MICROSECS; 82 | tmpres /= 10; /*convert into microseconds*/ 83 | tv->tv_sec = (long)(tmpres / 1000000UL); 84 | tv->tv_usec = (long)(tmpres % 1000000UL); 85 | } 86 | 87 | if (NULL != tz) 88 | { 89 | if (!tzflag) 90 | { 91 | _tzset(); 92 | tzflag++; 93 | } 94 | tz->tz_minuteswest = _timezone / 60; 95 | tz->tz_dsttime = _daylight; 96 | } 97 | 98 | return 0; 99 | } 100 | 101 | #else /* _WIN32 */ 102 | # include 103 | #endif /* _WIN32 */ 104 | 105 | 106 | uint64_t 107 | time_now(void) 108 | { 109 | struct timeval tv; 110 | gettimeofday(&tv, NULL); 111 | return (tv.tv_sec * 1000000UL) + tv.tv_usec; 112 | } 113 | 114 | static uint64_t tic; 115 | 116 | void 117 | time_tic(void) 118 | { 119 | tic = time_now(); 120 | } 121 | 122 | uint64_t 123 | time_toc(const char *str) 124 | { 125 | uint64_t d = time_now() - tic; 126 | printf("%s: %d us\n", str, (int)d); 127 | return d; 128 | } 129 | 130 | 131 | /* ------------------------------------------------------------------------ */ 132 | /* GLFW */ 133 | /* ------------------------------------------------------------------------ */ 134 | 135 | #define BATCH_LEN 128 136 | #define BATCH_COUNT 4 137 | 138 | static void 139 | glfw_render(GLFWwindow *wnd) 140 | { 141 | static int fc = 0; 142 | int c, r, o; 143 | 144 | /* Timing */ 145 | if (!fc) 146 | time_tic(); 147 | if (fc == 99) { 148 | uint64_t t; 149 | float bw; 150 | 151 | t = time_toc("100 Frames time"); 152 | 153 | bw = (1e6f * FOSPHOR_FFT_LEN * BATCH_LEN * BATCH_COUNT) / ((float)t / 100.0f); 154 | fprintf(stderr, "BW estimated: %f Msps\n", bw / 1e6); 155 | } 156 | 157 | fc = (fc+1) % 100; 158 | 159 | /* Clear everything */ 160 | glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); 161 | glClear(GL_COLOR_BUFFER_BIT); 162 | 163 | /* Process some samples */ 164 | for (c=0; csrc_buf + o, 1, r, g_as->src_fh); 172 | if (rc <= 0) { 173 | if (fseek(g_as->src_fh, 0, SEEK_SET)) 174 | abort(); 175 | continue; 176 | } 177 | 178 | r -= rc; 179 | o += rc; 180 | } 181 | 182 | fosphor_process(g_as->fosphor, g_as->src_buf, FOSPHOR_FFT_LEN * BATCH_LEN); 183 | } 184 | 185 | /* Draw fosphor */ 186 | fosphor_draw(g_as->fosphor, &g_as->render_main); 187 | 188 | if (g_as->zoom_enable) 189 | fosphor_draw(g_as->fosphor, &g_as->render_zoom); 190 | 191 | /* Done, swap buffer */ 192 | glfwSwapBuffers(wnd); 193 | } 194 | 195 | static void 196 | _update_fosphor(void) 197 | { 198 | /* Configure the screen zones */ 199 | if (g_as->zoom_enable) 200 | { 201 | int a = (int)(g_as->w * 0.65f); 202 | 203 | g_as->render_main.width = a; 204 | g_as->render_main.height = g_as->h; 205 | g_as->render_main.options &= ~FRO_COLOR_SCALE; 206 | 207 | g_as->render_zoom.pos_x = a - 10; 208 | g_as->render_zoom.width = g_as->w - a + 10; 209 | g_as->render_zoom.height = g_as->h; 210 | } 211 | else 212 | { 213 | g_as->render_main.width = g_as->w; 214 | g_as->render_main.height = g_as->h; 215 | g_as->render_main.options |= FRO_COLOR_SCALE; 216 | } 217 | 218 | g_as->render_main.histo_wf_ratio = g_as->ratio; 219 | g_as->render_zoom.histo_wf_ratio = g_as->ratio; 220 | 221 | /* Only render channels when there is a zoom */ 222 | if (g_as->zoom_enable) 223 | g_as->render_main.options |= FRO_CHANNELS; 224 | else 225 | g_as->render_main.options &= ~FRO_CHANNELS; 226 | 227 | /* Set the zoom */ 228 | g_as->render_main.channels[0].enabled = g_as->zoom_enable; 229 | g_as->render_main.channels[0].center = (float)g_as->zoom_center; 230 | g_as->render_main.channels[0].width = (float)g_as->zoom_width; 231 | 232 | g_as->render_zoom.freq_center = g_as->zoom_center; 233 | g_as->render_zoom.freq_span = g_as->zoom_width; 234 | 235 | /* Update render options */ 236 | fosphor_render_refresh(&g_as->render_main); 237 | fosphor_render_refresh(&g_as->render_zoom); 238 | 239 | /* Set other fosphor params */ 240 | if (g_as->fosphor) { 241 | fosphor_set_power_range(g_as->fosphor, g_as->db_ref, k_db_per_div[g_as->db_per_div_idx]); 242 | } 243 | } 244 | 245 | static void 246 | glfw_cb_reshape(GLFWwindow *wnd, int w, int h) 247 | { 248 | if (w < 0 || h < 0) 249 | glfwGetFramebufferSize(wnd, &w, &h); 250 | 251 | glMatrixMode(GL_MODELVIEW); 252 | glLoadIdentity(); 253 | 254 | glMatrixMode(GL_PROJECTION); 255 | glLoadIdentity(); 256 | glOrtho(0.0, (double)w, 0.0, (double)h, -1.0, 1.0); 257 | 258 | glViewport(0, 0, w, h); 259 | 260 | g_as->w = w; 261 | g_as->h = h; 262 | 263 | _update_fosphor(); 264 | } 265 | 266 | static void 267 | glfw_cb_key(GLFWwindow *wnd, int key, int scancode, int action, int mods) 268 | { 269 | if (action != GLFW_PRESS && action != GLFW_REPEAT) 270 | return; 271 | 272 | switch (key) 273 | { 274 | case GLFW_KEY_ESCAPE: 275 | exit(0); 276 | break; 277 | 278 | case GLFW_KEY_UP: 279 | g_as->db_ref -= k_db_per_div[g_as->db_per_div_idx]; 280 | break; 281 | 282 | case GLFW_KEY_DOWN: 283 | g_as->db_ref += k_db_per_div[g_as->db_per_div_idx]; 284 | break; 285 | 286 | case GLFW_KEY_LEFT: 287 | if (g_as->db_per_div_idx > 0) 288 | g_as->db_per_div_idx--; 289 | break; 290 | 291 | case GLFW_KEY_RIGHT: 292 | if (g_as->db_per_div_idx < 4) 293 | g_as->db_per_div_idx++; 294 | break; 295 | 296 | case GLFW_KEY_W: 297 | g_as->zoom_width *= 2.0; 298 | break; 299 | 300 | case GLFW_KEY_S: 301 | g_as->zoom_width /= 2.0; 302 | break; 303 | 304 | case GLFW_KEY_A: 305 | g_as->zoom_center -= g_as->zoom_width / 8.0; 306 | break; 307 | 308 | case GLFW_KEY_D: 309 | g_as->zoom_center += g_as->zoom_width / 8.0; 310 | break; 311 | 312 | case GLFW_KEY_Z: 313 | g_as->zoom_enable ^= 1; 314 | break; 315 | 316 | case GLFW_KEY_Q: 317 | if (g_as->ratio < 0.8f) 318 | g_as->ratio += 0.1f; 319 | break; 320 | 321 | case GLFW_KEY_E: 322 | if (g_as->ratio > 0.2f) 323 | g_as->ratio -= 0.1f; 324 | break; 325 | } 326 | 327 | _update_fosphor(); 328 | } 329 | 330 | static GLFWwindow * 331 | glfw_init(void) 332 | { 333 | GLFWwindow *wnd; 334 | 335 | /* Main glfw init */ 336 | glfwInit(); 337 | 338 | /* Window init */ 339 | wnd = glfwCreateWindow(1024, 1024, "Fosphor test", NULL, NULL); 340 | if (!wnd) 341 | return NULL; 342 | 343 | glfwMakeContextCurrent(wnd); 344 | 345 | #ifdef _WIN32 346 | /* Init GLEW (on win32) */ 347 | glewInit(); 348 | #endif 349 | 350 | /* Disable VSync to test speed */ 351 | glfwSwapInterval(0); 352 | 353 | /* Callbacks */ 354 | glfwSetFramebufferSizeCallback(wnd, glfw_cb_reshape); 355 | glfwSetKeyCallback(wnd, glfw_cb_key); 356 | 357 | /* Force inital window size config */ 358 | glfw_cb_reshape(wnd, -1, -1); 359 | 360 | return wnd; 361 | } 362 | 363 | static void 364 | glfw_cleanup(GLFWwindow *wnd) 365 | { 366 | glfwDestroyWindow(wnd); 367 | glfwTerminate(); 368 | } 369 | 370 | 371 | /* ------------------------------------------------------------------------ */ 372 | /* Main */ 373 | /* ------------------------------------------------------------------------ */ 374 | 375 | int main(int argc, char *argv[]) 376 | { 377 | GLFWwindow *wnd = NULL; 378 | int rv; 379 | 380 | /* Open source file */ 381 | if (argc == 2) { 382 | g_as->src_fh = fopen(argv[1], "rb"); 383 | if (!g_as->src_fh) { 384 | fprintf(stderr, "[!] Failed to open input file\n"); 385 | return -EIO; 386 | } 387 | } else if (argc == 1) { 388 | g_as->src_fh = stdin; 389 | } else { 390 | fprintf(stderr, "Usage: %s filename.cfile\n", argv[0]); 391 | return -EINVAL;; 392 | } 393 | 394 | g_as->src_buf = malloc(2 * sizeof(float) * FOSPHOR_FFT_LEN * FOSPHOR_FFT_MAX_BATCH); 395 | if (!g_as->src_buf) { 396 | rv = -ENOMEM; 397 | goto error; 398 | } 399 | 400 | /* Init our state */ 401 | g_as->db_per_div_idx = 3; 402 | g_as->db_ref = 0; 403 | g_as->ratio = 0.5f; 404 | g_as->zoom_center = 0.5; 405 | g_as->zoom_width = 0.2; 406 | 407 | /* Default fosphor render options */ 408 | fosphor_render_defaults(&g_as->render_main); 409 | fosphor_render_defaults(&g_as->render_zoom); 410 | g_as->render_zoom.options &= ~(FRO_LABEL_PWR | FRO_LABEL_TIME); 411 | 412 | g_as->render_main.histo_wf_ratio = 0.35f; 413 | g_as->render_zoom.histo_wf_ratio = 0.35f; 414 | 415 | /* Init GLFW */ 416 | wnd = glfw_init(); 417 | if (!wnd) { 418 | fprintf(stderr, "[!] Failed to initialize GLFW window\n"); 419 | rv = -EIO; 420 | goto error; 421 | } 422 | 423 | /* Init fosphor */ 424 | g_as->fosphor = fosphor_init(); 425 | if (!g_as->fosphor) { 426 | fprintf(stderr, "[!] Failed to initialize fosphor\n"); 427 | rv = -EIO; 428 | goto error; 429 | } 430 | 431 | fosphor_set_power_range(g_as->fosphor, g_as->db_ref, k_db_per_div[g_as->db_per_div_idx]); 432 | 433 | /* Run ! */ 434 | while (!glfwWindowShouldClose(wnd)) 435 | { 436 | glfw_render(wnd); 437 | glfwPollEvents(); 438 | } 439 | 440 | rv = 0; 441 | 442 | /* Cleanup */ 443 | error: 444 | if (g_as->fosphor) 445 | fosphor_release(g_as->fosphor); 446 | 447 | if (wnd) 448 | glfw_cleanup(wnd); 449 | 450 | free(g_as->src_buf); 451 | fclose(g_as->src_fh); 452 | 453 | return rv; 454 | } 455 | -------------------------------------------------------------------------------- /lib/fosphor/mkresources.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Packs a given file list into a .c file to be included in the executable 5 | directly. 6 | 7 | 8 | Copyright (C) 2013-2014 Sylvain Munaut 9 | 10 | This program is free software: you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation, either version 3 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License 21 | along with this program. If not, see . 22 | """ 23 | 24 | import binascii 25 | import sys 26 | 27 | 28 | class ResourcePacker(object): 29 | 30 | def header(self): 31 | return [ 32 | "/* AUTO GENERATED - DO NOT MODIFY BY HAND */", 33 | "#include \"resource_internal.h\"", 34 | ] 35 | 36 | def rp_header(self): 37 | return [ 38 | "struct resource_pack __resources[] = {", 39 | ] 40 | 41 | def _file_wrap(self, name, len_, data_lines): 42 | a = [ 43 | "\t{", 44 | "\t\t.name = \"%s\"," % name, 45 | "\t\t.len = %d," % len_, 46 | ] 47 | 48 | if len(data_lines) > 1: 49 | b = [ "\t\t.data =" ] 50 | for l in data_lines: 51 | b.append("\t\t\t" + l) 52 | b[-1] += "," 53 | else: 54 | b = [ "\t\t.data = %s," % data_lines[0] ] 55 | 56 | c = [ 57 | "\t},", 58 | ] 59 | 60 | return a+b+c 61 | 62 | def _wrap_str(self, s): 63 | m = { 64 | ord(b'\n') : '\\n', 65 | ord(b'\t') : '\\t', 66 | ord(b'\0') : '\\0', 67 | ord(b'\\') : '\\\\', 68 | ord(b'"') : '\\"', 69 | } 70 | 71 | sa = [] 72 | for c in s: 73 | if c in m: 74 | sa.append(m[c]) 75 | elif (c < 32) | (c >= 128): 76 | sa.append('\\x%02x' % c) 77 | else: 78 | sa.append(chr(c)) 79 | 80 | return ''.join(sa) 81 | 82 | def file_text(self, name, content): 83 | dl = ['\"' + self._wrap_str(l) + '\"' for l in content.splitlines(True)] 84 | return self._file_wrap(name, len(content), dl) 85 | 86 | def file_binary(self, name, content): 87 | return self._file_wrap(name, len(content), [ self.file_binary_slug(name) ]) 88 | 89 | def rp_footer(self): 90 | return [ 91 | "\t/* Guard */", 92 | "\t{ .name = (void*)0 }", 93 | "};", 94 | ] 95 | 96 | def file_binary_slug(self, name): 97 | return 'data_' + binascii.hexlify(name.encode('utf-8')).decode('utf-8') 98 | 99 | def file_binary_data(self, name, content): 100 | dl = [ 'static const char %s[] = {' % self.file_binary_slug(name) ] 101 | for i in range(0, len(content), 8): 102 | dl.append('\t' + ' '.join(['0x%02x,' % c for c in content[i:i+8]])) 103 | dl.append('};'); 104 | return dl 105 | 106 | def process(self, filenames): 107 | b = [] 108 | b.extend(self.header()) 109 | 110 | data = [] 111 | rp = [] 112 | rp.extend(self.rp_header()) 113 | for f in filenames: 114 | fh = open(f, 'rb') 115 | c = fh.read() 116 | if b'\x00' in c or len(c) > 65535: 117 | rp.extend(self.file_binary(f, c)) 118 | data.extend(self.file_binary_data(f, c)) 119 | else: 120 | rp.extend(self.file_text(f, c)) 121 | fh.close() 122 | rp.extend(self.rp_footer()) 123 | 124 | b.extend(data) 125 | b.extend(rp) 126 | 127 | return b; 128 | 129 | 130 | def main(self, *files): 131 | p = ResourcePacker() 132 | r = p.process(files) 133 | print('\n'.join(r)) 134 | 135 | 136 | if __name__ == '__main__': 137 | main(*sys.argv) 138 | -------------------------------------------------------------------------------- /lib/fosphor/private.h: -------------------------------------------------------------------------------- 1 | /* 2 | * private.h 3 | * 4 | * Private fosphor definitions 5 | * 6 | * Copyright (C) 2013-2021 Sylvain Munaut 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #pragma once 11 | 12 | /*! \defgroup private 13 | * @{ 14 | */ 15 | 16 | /*! \file private.h 17 | * \brief Private fosphor definitions 18 | */ 19 | 20 | 21 | #define FOSPHOR_FFT_LEN_LOG 10 22 | #define FOSPHOR_FFT_LEN (1< 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "resource.h" 26 | #include "resource_internal.h" 27 | 28 | #ifndef PATH_MAX 29 | # define PATH_MAX 1024 30 | #endif 31 | 32 | 33 | /*! \brief Packed resources */ 34 | extern struct resource_pack __resources[]; 35 | 36 | /*! \brief Loaded resources */ 37 | static LLIST_HEAD(g_cache); 38 | 39 | 40 | /* ------------------------------------------------------------------------ */ 41 | /* Loading */ 42 | /* ------------------------------------------------------------------------ */ 43 | 44 | 45 | static FILE * 46 | _rc_try_open(struct resource_cache *rc) 47 | { 48 | FILE *fh; 49 | char p[PATH_MAX]; 50 | char *env; 51 | 52 | /* Try locally */ 53 | fh = fopen(rc->name, "rb"); 54 | if (fh) 55 | return fh; 56 | 57 | /* Try environment path */ 58 | env = getenv("RESOURCE_PATH"); 59 | if (!env) 60 | return NULL; 61 | 62 | if ((strlen(env) + strlen(rc->name) + 2) > PATH_MAX) 63 | return NULL; 64 | 65 | sprintf(p, "%s/%s", env, rc->name); 66 | 67 | /* Retry full path */ 68 | fh = fopen(p, "rb"); 69 | 70 | return fh; 71 | } 72 | 73 | static int 74 | _rc_load(struct resource_cache *rc) 75 | { 76 | FILE *fh; 77 | 78 | /* Try to find file */ 79 | fh = _rc_try_open(rc); 80 | 81 | if (fh) { 82 | char *data; 83 | long len; 84 | 85 | /* Get len */ 86 | fseek(fh, 0, SEEK_END); 87 | len = ftell(fh); 88 | fseek(fh, 0, SEEK_SET); 89 | 90 | /* Get buffer with size for added \0 */ 91 | data = malloc(len+1); 92 | if (!data) 93 | goto read_done; 94 | 95 | /* Fill it */ 96 | if (fread(data, len, 1, fh) != 1) { 97 | free(data); 98 | goto read_done; 99 | } 100 | 101 | data[len] = '\x00'; 102 | 103 | /* Store result */ 104 | rc->data = data; 105 | rc->len = len; 106 | rc->flags |= RES_FLAG_MALLOCED; 107 | 108 | read_done: 109 | /* Done */ 110 | fclose(fh); 111 | } 112 | 113 | /* Search packed resource */ 114 | if (!rc->data) 115 | { 116 | struct resource_pack *rp = __resources; 117 | 118 | while (rp->name) { 119 | if (!strcmp(rp->name, rc->name)) { 120 | rc->data = rp->data; 121 | rc->len = rp->len; 122 | break; 123 | } 124 | rp++; 125 | } 126 | } 127 | 128 | return rc->data ? 0 : -ENOENT; 129 | } 130 | 131 | static void 132 | _rc_unload(struct resource_cache *rc) 133 | { 134 | if (rc->flags & RES_FLAG_MALLOCED) 135 | free((void*)rc->data); 136 | } 137 | 138 | 139 | /* ------------------------------------------------------------------------ */ 140 | /* Internal cache */ 141 | /* ------------------------------------------------------------------------ */ 142 | 143 | static struct resource_cache * 144 | _rc_alloc(const char *name) 145 | { 146 | size_t l; 147 | struct resource_cache *rc; 148 | char *n; 149 | 150 | l = sizeof(struct resource_cache) + strlen(name) + 1; 151 | rc = malloc(l); 152 | if (!rc) 153 | return NULL; 154 | 155 | memset(rc, 0x00, l); 156 | 157 | n = &rc->extra[0]; 158 | strcpy(n, name); 159 | rc->name = n; 160 | 161 | return rc; 162 | } 163 | 164 | static struct resource_cache * 165 | _rc_find_by_name(const char *name) 166 | { 167 | struct resource_cache *rc; 168 | 169 | llist_for_each_entry(struct resource_cache, rc, &g_cache, head) { 170 | if (!strcmp(rc->name, name)) 171 | return rc; 172 | } 173 | 174 | return NULL; 175 | } 176 | 177 | static struct resource_cache * 178 | _rc_find_by_data(const void *data) 179 | { 180 | struct resource_cache *rc; 181 | 182 | llist_for_each_entry(struct resource_cache, rc, &g_cache, head) { 183 | if (rc->data == data) 184 | return rc; 185 | } 186 | 187 | return NULL; 188 | } 189 | 190 | static struct resource_cache * 191 | _rc_get(struct resource_cache *rc) 192 | { 193 | rc->refcnt++; 194 | return rc; 195 | } 196 | 197 | static struct resource_cache * 198 | _rc_get_new(const char *name) 199 | { 200 | struct resource_cache *rc; 201 | 202 | rc = _rc_alloc(name); 203 | if (!rc) 204 | return NULL; 205 | 206 | if (_rc_load(rc)) { 207 | free(rc); 208 | return NULL; 209 | } 210 | 211 | llist_add(&rc->head, &g_cache); 212 | 213 | return _rc_get(rc); 214 | } 215 | 216 | static struct resource_cache * 217 | _rc_put(struct resource_cache *rc) 218 | { 219 | assert(rc->refcnt > 0); 220 | 221 | rc->refcnt--; 222 | 223 | if (rc->refcnt == 0) { 224 | llist_del(&rc->head); 225 | _rc_unload(rc); 226 | free(rc); 227 | rc = NULL; 228 | } 229 | 230 | return rc; 231 | } 232 | 233 | 234 | /* ------------------------------------------------------------------------ */ 235 | /* Public API */ 236 | /* ------------------------------------------------------------------------ */ 237 | 238 | const void * 239 | resource_get(const char *name, int *len) 240 | { 241 | struct resource_cache *rc; 242 | 243 | /* Search the cache */ 244 | rc = _rc_find_by_name(name); 245 | if (rc) 246 | goto done; 247 | 248 | /* No such luck ? Need a new entry */ 249 | rc = _rc_get_new(name); 250 | if (!rc) 251 | return NULL; 252 | 253 | done: 254 | if (rc && len) 255 | *len = rc->len; 256 | 257 | return rc ? rc->data : NULL; 258 | } 259 | 260 | void 261 | resource_put(const void *r) 262 | { 263 | struct resource_cache *rc; 264 | 265 | /* Search the cache */ 266 | rc = _rc_find_by_data(r); 267 | if (!rc) 268 | return; 269 | 270 | /* We're done with it */ 271 | _rc_put(rc); 272 | } 273 | 274 | /*! @} */ 275 | -------------------------------------------------------------------------------- /lib/fosphor/resource.h: -------------------------------------------------------------------------------- 1 | /* 2 | * resource.h 3 | * 4 | * Resource management header 5 | * 6 | * Copyright (C) 2013-2021 Sylvain Munaut 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #pragma once 11 | 12 | /*! \defgroup resource 13 | * @{ 14 | */ 15 | 16 | /*! \file resource.h 17 | * \brief Resource management header 18 | */ 19 | 20 | const void *resource_get(const char *name, int *len); 21 | void resource_put(const void *r); 22 | 23 | /*! @} */ 24 | -------------------------------------------------------------------------------- /lib/fosphor/resource_internal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * resource_internal.h 3 | * 4 | * Internal structures for the resource management 5 | * 6 | * Copyright (C) 2013-2021 Sylvain Munaut 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #pragma once 11 | 12 | /*! \addtogroup resource 13 | * @{ 14 | */ 15 | 16 | /*! \file resource_internal.h 17 | * \brief Internal header for the resource management 18 | */ 19 | 20 | #include "llist.h" 21 | 22 | /*! \brief Internal structure describing a packed resource */ 23 | struct resource_pack 24 | { 25 | const char *name; /*!< \brief Name of the resource */ 26 | const void *data; /*!< \brief Data (with added final \0) */ 27 | unsigned int len; /*!< \brief Original length (in bytes) of data */ 28 | }; 29 | 30 | struct resource_cache 31 | { 32 | struct llist_head head; /*< \brief Linked list head for cache */ 33 | 34 | const char *name; /*< \brief Name of the resource */ 35 | const void *data; /*< \brief Data pointer given to user */ 36 | unsigned int len; /*< \brief riginal length (in bytes) of data */ 37 | 38 | int refcnt; /*< \brief Reference counter */ 39 | int flags; /*< \brief Flags */ 40 | 41 | #define RES_FLAG_MALLOCED (1 << 0) 42 | 43 | char extra[0]; /*< \brief Extra data for whatever ... */ 44 | }; 45 | 46 | /*! @} */ 47 | -------------------------------------------------------------------------------- /lib/glfw_sink_c_impl.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2013-2021 Sylvain Munaut 4 | * Copyright 2013 Dimitri Stolnikov 5 | * 6 | * This file is part of gr-fosphor 7 | * 8 | * SPDX-License-Identifier: GPL-3.0-or-later 9 | */ 10 | 11 | #ifdef HAVE_CONFIG_H 12 | #include "config.h" 13 | #endif 14 | 15 | #include 16 | 17 | #include "glfw_sink_c_impl.h" 18 | 19 | 20 | namespace gr { 21 | namespace fosphor { 22 | 23 | glfw_sink_c::sptr 24 | glfw_sink_c::make() 25 | { 26 | return gnuradio::get_initial_sptr(new glfw_sink_c_impl()); 27 | } 28 | 29 | glfw_sink_c_impl::glfw_sink_c_impl() 30 | : base_sink_c("glfw_sink_c") 31 | { 32 | /* Nothing to do but super call */ 33 | } 34 | 35 | 36 | void 37 | glfw_sink_c_impl::glfw_cb_reshape(int w, int h) 38 | { 39 | if (w < 0 || h < 0) 40 | glfwGetFramebufferSize(this->d_window, &w, &h); 41 | 42 | this->cb_reshape(w, h); 43 | this->cb_visibility(true); 44 | } 45 | 46 | void 47 | glfw_sink_c_impl::glfw_cb_key(int key, int scancode, int action, int mods) 48 | { 49 | if (action != GLFW_PRESS) 50 | return; 51 | 52 | switch (key) 53 | { 54 | case GLFW_KEY_ESCAPE: 55 | exit(0); 56 | break; 57 | 58 | case GLFW_KEY_UP: 59 | this->execute_ui_action(REF_DOWN); 60 | break; 61 | 62 | case GLFW_KEY_DOWN: 63 | this->execute_ui_action(REF_UP); 64 | break; 65 | 66 | case GLFW_KEY_LEFT: 67 | this->execute_ui_action(DB_PER_DIV_DOWN); 68 | break; 69 | 70 | case GLFW_KEY_RIGHT: 71 | this->execute_ui_action(DB_PER_DIV_UP); 72 | break; 73 | 74 | case GLFW_KEY_Z: 75 | this->execute_ui_action(ZOOM_TOGGLE); 76 | break; 77 | 78 | case GLFW_KEY_W: 79 | this->execute_ui_action(ZOOM_WIDTH_UP); 80 | break; 81 | 82 | case GLFW_KEY_S: 83 | this->execute_ui_action(ZOOM_WIDTH_DOWN); 84 | break; 85 | 86 | case GLFW_KEY_D: 87 | this->execute_ui_action(ZOOM_CENTER_UP); 88 | break; 89 | 90 | case GLFW_KEY_A: 91 | this->execute_ui_action(ZOOM_CENTER_DOWN); 92 | break; 93 | 94 | case GLFW_KEY_Q: 95 | this->execute_ui_action(RATIO_UP); 96 | break; 97 | 98 | case GLFW_KEY_E: 99 | this->execute_ui_action(RATIO_DOWN); 100 | break; 101 | 102 | case GLFW_KEY_SPACE: 103 | this->execute_ui_action(FREEZE_TOGGLE); 104 | break; 105 | } 106 | } 107 | 108 | void 109 | glfw_sink_c_impl::glfw_cb_mouse(int btn, int action, int mods) 110 | { 111 | int x, y, w, h; 112 | double xd, yd; 113 | 114 | if (action != GLFW_PRESS) 115 | return; 116 | 117 | /* Get cursor position */ 118 | glfwGetFramebufferSize(this->d_window, &w, &h); 119 | glfwGetCursorPos(this->d_window, &xd, &yd); 120 | 121 | x = floor(xd); 122 | y = h - floor(yd) - 1; 123 | 124 | /* Report upstream */ 125 | this->execute_mouse_action(glfw_sink_c_impl::CLICK, x, y); 126 | } 127 | 128 | void 129 | glfw_sink_c_impl::_glfw_cb_reshape(GLFWwindow *wnd, int w, int h) 130 | { 131 | glfw_sink_c_impl *sink = (glfw_sink_c_impl *) glfwGetWindowUserPointer(wnd); 132 | sink->glfw_cb_reshape(w, h); 133 | } 134 | 135 | void 136 | glfw_sink_c_impl::_glfw_cb_key(GLFWwindow *wnd, int key, int scancode, int action, int mods) 137 | { 138 | glfw_sink_c_impl *sink = (glfw_sink_c_impl *) glfwGetWindowUserPointer(wnd); 139 | sink->glfw_cb_key(key, scancode, action, mods); 140 | } 141 | 142 | void 143 | glfw_sink_c_impl::_glfw_cb_mouse(GLFWwindow *wnd, int btn, int action, int mods) 144 | { 145 | glfw_sink_c_impl *sink = (glfw_sink_c_impl *) glfwGetWindowUserPointer(wnd); 146 | sink->glfw_cb_mouse(btn, action, mods); 147 | } 148 | 149 | 150 | void 151 | glfw_sink_c_impl::glctx_init() 152 | { 153 | GLFWwindow *wnd; 154 | 155 | /* Init GLFW */ 156 | glfwInit(); 157 | 158 | /* Create window */ 159 | wnd = glfwCreateWindow(1024, 1024, "fosphor", NULL, NULL); 160 | if (!wnd) 161 | return; 162 | 163 | this->d_window = wnd; 164 | 165 | glfwMakeContextCurrent(wnd); 166 | glfwSetWindowUserPointer(wnd, this); 167 | 168 | /* Setup callbacks */ 169 | glfwSetFramebufferSizeCallback(wnd, _glfw_cb_reshape); 170 | glfwSetKeyCallback(wnd, _glfw_cb_key); 171 | glfwSetMouseButtonCallback(wnd, _glfw_cb_mouse); 172 | 173 | /* Force first reshape */ 174 | this->glfw_cb_reshape(-1, -1); 175 | } 176 | 177 | void 178 | glfw_sink_c_impl::glctx_swap() 179 | { 180 | glfwSwapBuffers(this->d_window); 181 | } 182 | 183 | void 184 | glfw_sink_c_impl::glctx_poll() 185 | { 186 | glfwPollEvents(); 187 | } 188 | 189 | void 190 | glfw_sink_c_impl::glctx_fini() 191 | { 192 | glfwDestroyWindow(this->d_window); 193 | glfwTerminate(); 194 | } 195 | 196 | void 197 | glfw_sink_c_impl::glctx_update() 198 | { 199 | /* Nothing to do for GLFW */ 200 | } 201 | 202 | 203 | } /* namespace fosphor */ 204 | } /* namespace gr */ 205 | -------------------------------------------------------------------------------- /lib/glfw_sink_c_impl.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2013-2021 Sylvain Munaut 4 | * 5 | * This file is part of gr-fosphor 6 | * 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include "base_sink_c_impl.h" 15 | 16 | struct GLFWwindow; 17 | 18 | namespace gr { 19 | namespace fosphor { 20 | 21 | /*! 22 | * \brief GLFW version of fosphor sink (implementation) 23 | * \ingroup fosphor 24 | */ 25 | class glfw_sink_c_impl : public glfw_sink_c, public base_sink_c_impl 26 | { 27 | private: 28 | /* GLFW stuff */ 29 | GLFWwindow *d_window; 30 | 31 | void glfw_render(void); 32 | void glfw_cb_reshape(int w, int h); 33 | void glfw_cb_key(int key, int scancode, int action, int mods); 34 | void glfw_cb_mouse(int btn, int action, int mods); 35 | 36 | static void _glfw_cb_reshape(GLFWwindow *wnd, int w, int h); 37 | static void _glfw_cb_key(GLFWwindow *wnd, int key, int scancode, int action, int mods); 38 | static void _glfw_cb_mouse(GLFWwindow *wnd, int btn, int action, int mods); 39 | 40 | protected: 41 | /* Delegated implementation of GL context management */ 42 | void glctx_init(); 43 | void glctx_swap(); 44 | void glctx_poll(); 45 | void glctx_fini(); 46 | void glctx_update(); 47 | 48 | public: 49 | glfw_sink_c_impl(); 50 | }; 51 | 52 | } // namespace fosphor 53 | } // namespace gr 54 | -------------------------------------------------------------------------------- /lib/overlap_cc_impl.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2013-2021 Sylvain Munaut 4 | * 5 | * This file is part of gr-fosphor 6 | * 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #ifdef HAVE_CONFIG_H 11 | #include "config.h" 12 | #endif 13 | 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include "overlap_cc_impl.h" 20 | 21 | 22 | namespace gr { 23 | namespace fosphor { 24 | 25 | overlap_cc::sptr 26 | overlap_cc::make(int wlen, int overlap) 27 | { 28 | return gnuradio::get_initial_sptr( 29 | new overlap_cc_impl(wlen, overlap) 30 | ); 31 | } 32 | 33 | overlap_cc_impl::overlap_cc_impl(int wlen, int overlap) 34 | : gr::sync_interpolator("overlap_cc", 35 | gr::io_signature::make(1, 1, sizeof(gr_complex)), 36 | gr::io_signature::make(1, 1, sizeof(gr_complex)), 37 | overlap), 38 | d_wlen(wlen) 39 | { 40 | this->set_overlap_ratio(overlap); 41 | } 42 | 43 | overlap_cc_impl::~overlap_cc_impl() 44 | { 45 | } 46 | 47 | void 48 | overlap_cc_impl::set_overlap_ratio(const int overlap) 49 | { 50 | /* Save the new ratio */ 51 | this->d_overlap = overlap; 52 | 53 | /* Adapt the interpolation factor accordingly */ 54 | this->set_interpolation(overlap); 55 | 56 | /* Always keep an history of a full window to properly 57 | * support overlap ratio changes */ 58 | this->set_history(this->d_wlen); 59 | 60 | /* Always output entire windows */ 61 | this->set_output_multiple(this->d_wlen); 62 | } 63 | 64 | int 65 | overlap_cc_impl::work( 66 | int noutput_items, 67 | gr_vector_const_void_star &input_items, 68 | gr_vector_void_star &output_items) 69 | { 70 | const gr_complex *in = (const gr_complex *) input_items[0]; 71 | gr_complex *out = (gr_complex *) output_items[0]; 72 | int ii, oi; 73 | 74 | for (ii=0,oi=0; oid_wlen/this->d_overlap,oi+=this->d_wlen) { 75 | memcpy(&out[oi], &in[ii], this->d_wlen * sizeof(gr_complex)); 76 | } 77 | 78 | return noutput_items; 79 | } 80 | 81 | } /* namespace fosphor */ 82 | } /* namespace gr */ 83 | -------------------------------------------------------------------------------- /lib/overlap_cc_impl.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2013-2021 Sylvain Munaut 4 | * 5 | * This file is part of gr-fosphor 6 | * 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | struct fosphor; 15 | struct fosphor_render; 16 | 17 | namespace gr { 18 | namespace fosphor { 19 | 20 | /*! 21 | * \brief Block preparing an overlapped version of the stream 22 | * \ingroup fosphor 23 | */ 24 | class overlap_cc_impl : public overlap_cc 25 | { 26 | private: 27 | int d_wlen; 28 | int d_overlap; 29 | 30 | public: 31 | overlap_cc_impl(int wlen, int overlap); 32 | virtual ~overlap_cc_impl(); 33 | 34 | void set_overlap_ratio(const int overlap); 35 | 36 | /* gr::sync_interpolator implementation */ 37 | int work (int noutput_items, 38 | gr_vector_const_void_star &input_items, 39 | gr_vector_void_star &output_items); 40 | }; 41 | 42 | } // namespace fosphor 43 | } // namespace gr 44 | -------------------------------------------------------------------------------- /lib/qt_sink_c_impl.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2013-2021 Sylvain Munaut 4 | * 5 | * This file is part of gr-fosphor 6 | * 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #ifdef ENABLE_PYTHON 11 | # include 12 | #endif 13 | 14 | #ifdef HAVE_CONFIG_H 15 | #include "config.h" 16 | #endif 17 | 18 | #include "qt_sink_c_impl.h" 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "QGLSurface.h" 25 | 26 | 27 | namespace gr { 28 | namespace fosphor { 29 | 30 | qt_sink_c::sptr 31 | qt_sink_c::make(QWidget *parent) 32 | { 33 | return gnuradio::get_initial_sptr(new qt_sink_c_impl(parent)); 34 | } 35 | 36 | qt_sink_c_impl::qt_sink_c_impl(QWidget *parent) 37 | : base_sink_c("qt_sink_c") 38 | { 39 | /* QT stuff */ 40 | if(qApp != NULL) { 41 | d_qApplication = qApp; 42 | } 43 | else { 44 | int argc=0; 45 | char **argv = NULL; 46 | d_qApplication = new QApplication(argc, argv); 47 | } 48 | 49 | this->d_gui = new QGLSurface(parent, this); 50 | } 51 | 52 | 53 | void 54 | qt_sink_c_impl::glctx_init() 55 | { 56 | this->d_gui->grabContext(); 57 | this->d_gui->setFocus(); 58 | } 59 | 60 | void 61 | qt_sink_c_impl::glctx_swap() 62 | { 63 | this->d_gui->swapBuffers(); 64 | } 65 | 66 | void 67 | qt_sink_c_impl::glctx_poll() 68 | { 69 | /* Nothing to do */ 70 | } 71 | 72 | void 73 | qt_sink_c_impl::glctx_fini() 74 | { 75 | this->d_gui->releaseContext(); 76 | } 77 | 78 | void 79 | qt_sink_c_impl::glctx_update() 80 | { 81 | this->d_gui->makeCurrent(); 82 | } 83 | 84 | 85 | void 86 | qt_sink_c_impl::exec_() 87 | { 88 | d_qApplication->exec(); 89 | } 90 | 91 | QWidget* 92 | qt_sink_c_impl::qwidget() 93 | { 94 | return dynamic_cast(this->d_gui); 95 | } 96 | 97 | 98 | #ifdef ENABLE_PYTHON 99 | PyObject* 100 | qt_sink_c_impl::pyqwidget() 101 | { 102 | PyObject *w = PyLong_FromVoidPtr((void*)dynamic_cast(this->d_gui)); 103 | PyObject *retarg = Py_BuildValue("N", w); 104 | return retarg; 105 | } 106 | #else 107 | void* 108 | qt_sink_c_impl::pyqwidget() 109 | { 110 | return NULL; 111 | } 112 | #endif 113 | 114 | } /* namespace fosphor */ 115 | } /* namespace gr */ 116 | -------------------------------------------------------------------------------- /lib/qt_sink_c_impl.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2013-2021 Sylvain Munaut 4 | * 5 | * This file is part of gr-fosphor 6 | * 7 | * SPDX-License-Identifier: GPL-3.0-or-later 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include "base_sink_c_impl.h" 15 | 16 | namespace gr { 17 | namespace fosphor { 18 | 19 | class QGLSurface; 20 | 21 | /*! 22 | * \brief Qt version of fosphor sink (implementation) 23 | * \ingroup fosphor 24 | */ 25 | class qt_sink_c_impl : public qt_sink_c, public base_sink_c_impl 26 | { 27 | friend class QGLSurface; 28 | 29 | private: 30 | QGLSurface *d_gui; 31 | 32 | protected: 33 | /* Delegated implementation of GL context management */ 34 | void glctx_init(); 35 | void glctx_swap(); 36 | void glctx_poll(); 37 | void glctx_fini(); 38 | void glctx_update(); 39 | 40 | public: 41 | qt_sink_c_impl(QWidget *parent=NULL); 42 | 43 | void exec_(); 44 | QWidget* qwidget(); 45 | 46 | #if defined(PY_VERSION) 47 | PyObject* pyqwidget(); 48 | #else 49 | void* pyqwidget(); 50 | #endif 51 | }; 52 | 53 | } // namespace fosphor 54 | } // namespace gr 55 | -------------------------------------------------------------------------------- /python/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2011-2020 Free Software Foundation, Inc. 2 | # Copyright 2013-2021 Sylvain Munaut 3 | # 4 | # This file is part of gr-fosphor 5 | # 6 | # SPDX-License-Identifier: GPL-3.0-or-later 7 | 8 | ######################################################################## 9 | # Include python install macros 10 | ######################################################################## 11 | include(GrPython) 12 | if(NOT PYTHONINTERP_FOUND) 13 | return() 14 | endif() 15 | 16 | add_subdirectory(bindings) 17 | 18 | ######################################################################## 19 | # Install python sources 20 | ######################################################################## 21 | list(APPEND fosphor_python 22 | __init__.py 23 | ) 24 | 25 | GR_PYTHON_INSTALL( 26 | FILES 27 | ${fosphor_python} 28 | DESTINATION ${GR_PYTHON_DIR}/gnuradio/fosphor 29 | ) 30 | 31 | ######################################################################## 32 | # Handle the unit tests 33 | ######################################################################## 34 | include(GrTest) 35 | 36 | set(GR_TEST_TARGET_DEPS gnuradio-fosphor) 37 | -------------------------------------------------------------------------------- /python/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2008-2020 Free Software Foundation, Inc. 2 | # Copyright 2013-2021 Sylvain Munaut 3 | # 4 | # This file is part of gr-fosphor 5 | # 6 | # SPDX-License-Identifier: GPL-3.0-or-later 7 | 8 | # import pybind11 generated symbols into the iqbalance namespace 9 | from .fosphor_python import * 10 | -------------------------------------------------------------------------------- /python/bindings/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2011-2020 Free Software Foundation, Inc. 2 | # Copyright 2013-2021 Sylvain Munaut 3 | # 4 | # This file is part of gr-fosphor 5 | # 6 | # SPDX-License-Identifier: GPL-3.0-or-later 7 | 8 | ######################################################################## 9 | # Check for pygccxml 10 | ######################################################################## 11 | GR_PYTHON_CHECK_MODULE_RAW( 12 | "pygccxml" 13 | "import pygccxml" 14 | PYGCCXML_FOUND 15 | ) 16 | 17 | # Official module is broken, makes too many assumptions that are not true 18 | #include(GrPybind) 19 | 20 | include(GrPython) 21 | 22 | macro(GR_PYBIND_MAKE name updir filter files) 23 | 24 | configure_file(${CMAKE_SOURCE_DIR}/docs/doxygen/pydoc_macros.h ${CMAKE_CURRENT_BINARY_DIR} COPYONLY) 25 | 26 | pybind11_add_module(${name}_python ${files}) 27 | 28 | SET(MODULE_NAME ${name}) 29 | if (${name} STREQUAL gr) 30 | SET(MODULE_NAME "runtime") 31 | endif() 32 | 33 | target_include_directories(${name}_python PUBLIC 34 | ${CMAKE_CURRENT_BINARY_DIR} 35 | ${PYTHON_NUMPY_INCLUDE_DIR} 36 | ${CMAKE_CURRENT_SOURCE_DIR}/${updir}/lib 37 | ${CMAKE_CURRENT_SOURCE_DIR}/${updir}/include 38 | ${PYBIND11_INCLUDE_DIR} 39 | ) 40 | target_link_libraries(${name}_python PUBLIC ${Boost_LIBRARIES} ${PYTHON_LIBRARIES} gnuradio-${MODULE_NAME}) 41 | if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR 42 | CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 43 | target_compile_options(${name}_python PRIVATE -Wno-unused-variable) # disable warnings for docstring templates 44 | endif(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR 45 | CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 46 | 47 | endmacro(GR_PYBIND_MAKE) 48 | 49 | 50 | 51 | ######################################################################## 52 | # Python Bindings 53 | ######################################################################## 54 | 55 | list(APPEND fosphor_python_files 56 | base_sink_c_python.cc 57 | glfw_sink_c_python.cc 58 | qt_sink_c_python.cc 59 | overlap_cc_python.cc 60 | python_bindings.cc) 61 | 62 | GR_PYBIND_MAKE(fosphor 63 | ../.. 64 | gr::fosphor 65 | "${fosphor_python_files}") 66 | 67 | install(TARGETS fosphor_python DESTINATION ${GR_PYTHON_DIR}/gnuradio/fosphor COMPONENT pythonapi) 68 | -------------------------------------------------------------------------------- /python/bindings/base_sink_c_python.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2021 Sylvain Munaut 3 | * 4 | * This file is part of gr-fosphor 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | namespace py = pybind11; 14 | 15 | #include 16 | 17 | #define D(...) "" 18 | 19 | void bind_base_sink_c(py::module& m) 20 | { 21 | using base_sink_c = gr::fosphor::base_sink_c; 22 | 23 | py::class_> sink_class (m, "base_sink_c", D(base_sink_c)); 28 | 29 | 30 | py::enum_(sink_class, "ui_action") 31 | .value("DB_PER_DIV_UP", base_sink_c::DB_PER_DIV_UP) 32 | .value("DB_PER_DIV_DOWN", base_sink_c::DB_PER_DIV_DOWN) 33 | .value("REF_UP", base_sink_c::REF_UP) 34 | .value("REF_DOWN", base_sink_c::REF_DOWN) 35 | .value("ZOOM_TOGGLE", base_sink_c::ZOOM_TOGGLE) 36 | .value("ZOOM_WIDTH_UP", base_sink_c::ZOOM_WIDTH_UP) 37 | .value("ZOOM_WIDTH_DOWN", base_sink_c::ZOOM_WIDTH_DOWN) 38 | .value("ZOOM_CENTER_UP", base_sink_c::ZOOM_CENTER_UP) 39 | .value("ZOOM_CENTER_DOWN", base_sink_c::ZOOM_CENTER_DOWN) 40 | .value("RATIO_UP", base_sink_c::RATIO_UP) 41 | .value("RATIO_DOWN", base_sink_c::RATIO_DOWN) 42 | .value("FREEZE_TOGGLE", base_sink_c::FREEZE_TOGGLE) 43 | .export_values(); 44 | 45 | py::enum_(sink_class, "mouse_action") 46 | .value("CLICK", base_sink_c::CLICK) 47 | .export_values(); 48 | 49 | py::implicitly_convertible(); 50 | py::implicitly_convertible(); 51 | 52 | sink_class 53 | .def("execute_ui_action", 54 | &base_sink_c::execute_ui_action, 55 | py::arg("action"), 56 | D(base_sink_c,execute_ui_action) 57 | ) 58 | 59 | .def("execute_mouse_action", 60 | &base_sink_c::execute_mouse_action, 61 | py::arg("action"), 62 | py::arg("x"), 63 | py::arg("y"), 64 | D(base_sink_c,execute_mouse_action) 65 | ) 66 | 67 | .def("set_frequency_range", 68 | &base_sink_c::set_frequency_range, 69 | py::arg("center"), 70 | py::arg("span"), 71 | D(base_sink_c,set_frequency_range) 72 | ) 73 | 74 | .def("set_frequency_center", 75 | &base_sink_c::set_frequency_center, 76 | py::arg("center"), 77 | D(base_sink_c,set_frequency_center) 78 | ) 79 | 80 | .def("set_frequency_span", 81 | &base_sink_c::set_frequency_span, 82 | py::arg("span"), 83 | D(base_sink_c,set_frequency_span) 84 | ) 85 | 86 | .def("set_fft_window", 87 | &base_sink_c::set_fft_window, 88 | py::arg("win"), 89 | D(base_sink_c,set_fft_window) 90 | ) 91 | 92 | ; 93 | } 94 | -------------------------------------------------------------------------------- /python/bindings/glfw_sink_c_python.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2021 Sylvain Munaut 3 | * 4 | * This file is part of gr-fosphor 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | namespace py = pybind11; 14 | 15 | #include 16 | 17 | #define D(...) "" 18 | 19 | void bind_glfw_sink_c(py::module& m) 20 | { 21 | using glfw_sink_c = gr::fosphor::glfw_sink_c; 22 | 23 | py::class_>(m, "glfw_sink_c", D(glfw_sink_c)) 29 | 30 | .def(py::init(&glfw_sink_c::make), 31 | D(glfw_sink_c,make) 32 | ) 33 | 34 | ; 35 | } 36 | -------------------------------------------------------------------------------- /python/bindings/overlap_cc_python.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2021 Sylvain Munaut 3 | * 4 | * This file is part of gr-fosphor 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | namespace py = pybind11; 14 | 15 | #include 16 | 17 | #define D(...) "" 18 | 19 | void bind_overlap_cc(py::module& m) 20 | { 21 | using overlap_cc = gr::fosphor::overlap_cc; 22 | 23 | py::class_>(m, "overlap_cc", D(overlap_cc)) 28 | 29 | .def(py::init(&overlap_cc::make), 30 | py::arg("wlen"), 31 | py::arg("overlap"), 32 | D(overlap_cc,make) 33 | ) 34 | 35 | .def("set_overlap_ration", 36 | &overlap_cc::set_overlap_ratio, 37 | py::arg("overlap"), 38 | D(overlap_cc, set_overlap_ratio) 39 | ) 40 | 41 | ; 42 | } 43 | -------------------------------------------------------------------------------- /python/bindings/python_bindings.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021 Free Software Foundation, Inc. 3 | * Copyright 2013-2021 Sylvain Munaut 4 | * 5 | * This file is part of gr-fosphor 6 | * 7 | * SPDX-License-Identifier: GPL-3.0-or-later 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 | void bind_base_sink_c(py::module& m); 18 | void bind_glfw_sink_c(py::module& m); 19 | void bind_qt_sink_c(py::module& m); 20 | void bind_overlap_cc(py::module& m); 21 | 22 | // We need this hack because import_array() returns NULL 23 | // for newer Python versions. 24 | // This function is also necessary because it ensures access to the C API 25 | // and removes a warning. 26 | void* init_numpy() 27 | { 28 | import_array(); 29 | return NULL; 30 | } 31 | 32 | PYBIND11_MODULE(fosphor_python, m) 33 | { 34 | // Initialize the numpy C API 35 | // (otherwise we will see segmentation faults) 36 | init_numpy(); 37 | 38 | // Allow access to base block methods 39 | py::module::import("gnuradio.gr"); 40 | 41 | bind_base_sink_c(m); 42 | bind_glfw_sink_c(m); 43 | bind_qt_sink_c(m); 44 | bind_overlap_cc(m); 45 | } 46 | -------------------------------------------------------------------------------- /python/bindings/qt_sink_c_python.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2021 Sylvain Munaut 3 | * 4 | * This file is part of gr-fosphor 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | namespace py = pybind11; 14 | 15 | #include 16 | 17 | #include 18 | 19 | #define D(...) "" 20 | 21 | void bind_qt_sink_c(py::module& m) 22 | { 23 | using qt_sink_c = gr::fosphor::qt_sink_c; 24 | 25 | py::class_>(m, "qt_sink_c", D(qt_sink_c)) 31 | 32 | .def(py::init(&qt_sink_c::make), 33 | py::arg("parent") = nullptr, 34 | D(qt_sink_c,make) 35 | ) 36 | 37 | .def("exec_", 38 | &qt_sink_c::exec_, 39 | D(qt_sink_c, exec_) 40 | ) 41 | 42 | .def("qwidget", 43 | &qt_sink_c::qwidget, 44 | D(qt_sink_c, qwidget) 45 | ) 46 | 47 | //.def("pyqwidget", 48 | // &qt_sink_c::pyqwidget, 49 | // D(qt_sink_c, pyqwidget) 50 | //) 51 | // For the sip conversion to python to work, the widget object 52 | // needs to be explicitly converted to long long. 53 | .def("pyqwidget", 54 | [](std::shared_ptr p) { 55 | return PyLong_AsLongLong(p->pyqwidget()); 56 | }, 57 | D(qt_sink_c, pyqwidget) 58 | ) 59 | 60 | ; 61 | } 62 | --------------------------------------------------------------------------------