diff --git a/.gitignore b/.gitignore index 798fb0a..ea4df6d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,35 +1,20 @@ *.a -*.cxx *.pyc +*.so dist CMakeFiles CMakeCache.txt cmake_install.cmake -lasp/*.cpp Makefile build -*.html __pycache__ cython_debug -lasp/config.pxi -lasp/wrappers.c -lasp/resources_rc.py -*.so -test/test_bf -test/test_fft -test/test_math -test/test_workers -test/test_uldaq doc -LASP.egg-info -lasp_octave_fir.* -lasp/ui_* -resources_rc.py .ropeproject .spyproject -test/test_uldaq -lasp/device/lasp_daq.cxx -lasp/c/lasp_config.h compile_commands.json .cache -lasp_config.h +_skbuild +src/lasp.egg-info +test/.ipynb_checkpoints +src/lasp/lasp_config.h diff --git a/.gitmodules b/.gitmodules index 14931f2..d7155c2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,12 +1,15 @@ [submodule "STL-Threadsafe"] - path = STL-Threadsafe + path = third_party/STL-Threadsafe url = https://github.com/miachm/STL-Threadsafe [submodule "gsl-lite"] - path = gsl-lite + path = third_party/gsl-lite url = https://github.com/gsl-lite/gsl-lite [submodule "DebugTrace-cpp"] - path = DebugTrace-cpp + path = third_party/DebugTrace-cpp url = https://github.com/MasatoKokubo/DebugTrace-cpp [submodule "armadillo-code"] - path = armadillo-code + path = third_party/armadillo-code url = https://gitlab.com/conradsnicta/armadillo-code +[submodule "third_party/tomlplusplus"] + path = third_party/tomlplusplus + url = https://github.com/marzer/tomlplusplus diff --git a/CMakeLists.txt b/CMakeLists.txt index ee90237..dff8eb1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,157 +1,65 @@ cmake_minimum_required (VERSION 3.16) +project(LASP LANGUAGES CXX) + # To allow linking to static libs from other directories cmake_policy(SET CMP0079 NEW) -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/lasp/cmake") + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake") include("BuildType") +include("QueryPythonForPybind11") + + +# Generate stubs for the Python module +option(WITH_PY_STUBS + "Generate Python stub files (.pyi) for the Python module." On) + +# Find the pybind11 package +find_pybind11_python_first() # This is used for code completion in vim set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -project(LASP LANGUAGES C CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED) -set(CMAKE_C_STANDARD 11) option(LASP_DOUBLE_PRECISION "Compile as double precision floating point" ON) option(LASP_HAS_RTAUDIO "Compile with RtAudio Daq backend" ON) option(LASP_HAS_ULDAQ "Compile with UlDaq backend" ON) -set(LASP_TRACERNAME "defaulttracer" CACHE STRING "Name of tracer variable containing level") set(LASP_FFT_BACKEND "FFTW" CACHE STRING "FFT Library backend") set_property(CACHE LASP_FFT_BACKEND PROPERTY STRINGS "FFTW" "FFTPack" "None") -find_package(Python3 COMPONENTS Interpreter Development REQUIRED) -include_directories(${Python3_INCLUDE_DIRS}) -find_package(pybind11 CONFIG REQUIRED) +set(PY_FULL_VERSION ${PROJECT_VERSION}${PY_VERSION_SUFFIX}) # Required for PYBIND11 -set(POSITION_INDEPENDENT_CODE True) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) if(CMAKE_BUILD_TYPE STREQUAL Debug) + add_definitions(-DDEBUG=1) set(LASP_DEBUG True) else() set(LASP_DEBUG False) endif() -if(LASP_PARALLEL) - add_definitions(-D_REENTRANT) - set(LASP_THREADING_LIBRARIES pthread) -else() - set(LASP_THREADING_LIBRARIES "") -endif() - # link openblas set(BLA_VENDOR OpenBLAS) find_package(BLAS REQUIRED) -# Armadillo -include_directories(SYSTEM armadillo-code/include) - - # Reasonable maximum to the nfft size, at 48kHz this is 700s of data... add_definitions(-DLASP_MAX_NFFT=33554432) # 2**25 # ####################################### End of user-adjustable variables section -if(LASP_FFT_BACKEND STREQUAL "FFTW") - find_library(FFTW_LIBRARY NAMES fftw3 fftw) - set(FFT_LIBRARIES "${FFTW_LIBRARY}") -elseif(LASP_FFT_BACKEND STREQUAL "FFTPack") - include_directories(fftpack) - set(FFT_LIBRARIES fftpack) - add_subdirectory(fftpack) -endif() +include(OSSpecific) -# General make flags -set(CMAKE_POSITION_INDEPENDENT_CODE ON) - -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-type-limits \ --Werror=implicit-function-declaration -Wno-unused-parameter \ --Werror=return-type -Wfatal-errors") - -if(CMAKE_SYSTEM_NAME STREQUAL "Windows") - set(win32 true) - set(home $ENV{USERPROFILE}) - # set(miniconda_dir ${home}\\Miniconda3) - - message("Building for Windows") - include_directories( - ..\\rtaudio - C:\\mingw\\mingw64\\include\\OpenBLAS - link_directories(${home}\\miniconda3\\Library\\include) - ) - set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH} $miniconda_dir\\Lib\\cmake") - # include( - add_definitions(-DMS_WIN64) - link_directories(C:\\mingw\\mingw64\\lib) - link_directories(C:\\mingw\\mingw64\\bin) - link_directories(..\\rtaudio) - link_directories(${home}\\Miniconda3) - add_definitions(-DHAS_RTAUDIO_WIN_WASAPI_API) -else() # Linux compile - set(win32 false) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=incompatible-pointer-types") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wfatal-errors") - include_directories(/usr/local/include/rtaudio) - include_directories(/usr/include/rtaudio) - link_directories(/usr/local/lib) - # This should become optional later on, and be added to the windows list as - # well. - -endif() -# The last argument here takes care of calling SIGABRT when an integer overflow -# occures. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-type-limits") -set(CMAKE_C_FLAGS_RELEASE "-O2 -mfpmath=sse -march=x86-64 -mtune=native \ +set(CMAKE_C_FLAGS_RELEASE "-O3 -mfpmath=sse -march=x86-64 -mtune=native \ -fdata-sections -ffunction-sections -fomit-frame-pointer -finline-functions") # ############################# End compilation flags - -# Add FFTpack dir if used as FFT backend -if(LASP_FFTPACK_BACKEND) - add_subdirectory(fftpack) - include_directories( - fftpack - ) -endif() - -include_directories(STL-Threadsafe/include) -include_directories(gsl-lite/include) -include_directories(DebugTrace-cpp/include) - -add_subdirectory(lasp) -add_subdirectory(gsl-lite) -add_subdirectory(test) - -# set(SETUP_PY_IN "${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in") -set(SETUP_PY "${CMAKE_CURRENT_BINARY_DIR}/setup.py") - -set(DEPS "${CMAKE_CURRENT_SOURCE_DIR}/*.py" - "${CMAKE_CURRENT_SOURCE_DIR}/lasp/*.py" - # "wrappers" - # "lasp_device_lib") - ) - -# set(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/build/timestamp") - -# # configure_file(${SETUP_PY_IN} ${SETUP_PY}) -# add_custom_command(OUTPUT ${OUTPUT} -# COMMAND ${PYTHON_EXECUTABLE} ${SETUP_PY} build -# COMMAND ${CMAKE_COMMAND} -E touch ${OUTPUT} -# DEPENDS ${DEPS}) - -# add_custom_target(target ALL DEPENDS ${OUTPUT}) - -# if(DEFINED INSTALL_DEBUG) -# set(EXTRA_SETUP_ARG --user -e) -# else() -# set(EXTRA_SETUP_ARG "") -# endif() - - -# install(CODE "execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip install ${EXTRA_SETUP_ARG} .)") +add_subdirectory(third_party) +add_subdirectory(src/lasp) diff --git a/Doxyfile b/Doxyfile index d06c7aa..1a3d61f 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.8.14 +# Doxyfile 1.9.3 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -17,10 +17,10 @@ # Project related configuration options #--------------------------------------------------------------------------- -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See # https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. @@ -38,20 +38,20 @@ PROJECT_NAME = LASP # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = +PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = +PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. -PROJECT_LOGO = +PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is @@ -93,6 +93,14 @@ ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. @@ -162,7 +170,7 @@ FULL_PATH_NAMES = YES # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = +STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which @@ -171,7 +179,7 @@ STRIP_FROM_PATH = # specify the list of include paths that are normally passed to the compiler # using the -I flag. -STRIP_FROM_INC_PATH = +STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't @@ -189,6 +197,16 @@ SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus @@ -238,14 +256,18 @@ TAB_SIZE = 4 # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines (in the resulting output). You can put ^^ in the value part of an # alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) -ALIASES = +ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. -TCL_SUBST = +TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For @@ -253,7 +275,7 @@ TCL_SUBST = # members will be omitted, etc. # The default value is: NO. -OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored @@ -275,28 +297,37 @@ OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: -# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: -# Fortran. In the later case the parser tries to guess whether the code is fixed -# or free formatted code, this is the default for Fortran type files), VHDL. For -# instance to make doxygen treat .inc files as Fortran files (default is PHP), -# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is +# Fortran), use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. -EXTENSION_MAPPING = +EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. +# documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. @@ -308,7 +339,7 @@ MARKDOWN_SUPPORT = YES # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. -# Minimum value: 0, maximum value: 99, default value: 0. +# Minimum value: 0, maximum value: 99, default value: 5. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 0 @@ -444,6 +475,12 @@ EXTRACT_ALL = YES EXTRACT_PRIVATE = NO +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. @@ -498,8 +535,8 @@ HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO, these declarations will be -# included in the documentation. +# declarations. If set to NO, these declarations will be included in the +# documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO @@ -522,7 +559,7 @@ INTERNAL_DOCS = NO # names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. +# (including Cygwin) ands Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = NO @@ -532,7 +569,7 @@ CASE_SENSE_NAMES = NO # scope will be hidden. # The default value is: NO. -HIDE_SCOPE_NAMES = YES +HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to @@ -649,7 +686,7 @@ GENERATE_DEPRECATEDLIST= YES # sections, marked by \if ... \endif and \cond # ... \endcond blocks. -ENABLED_SECTIONS = +ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the @@ -691,7 +728,7 @@ SHOW_NAMESPACES = YES # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. -FILE_VERSION_FILTER = +FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated @@ -704,7 +741,7 @@ FILE_VERSION_FILTER = # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. -LAYOUT_FILE = +LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib @@ -714,7 +751,7 @@ LAYOUT_FILE = # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. -CITE_BIB_FILES = +CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages @@ -754,7 +791,8 @@ WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. WARN_NO_PARAMDOC = NO @@ -779,7 +817,7 @@ WARN_FORMAT = "$file:$line: $text" # messages should be written. If left blank the output is written to standard # error (stderr). -WARN_LOGFILE = +WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files @@ -791,7 +829,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = lasp +INPUT = src # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -813,8 +851,10 @@ INPUT_ENCODING = UTF-8 # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, -# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. +# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), +# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen +# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f, *.for, *.tcl, *.vhd, +# *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.c \ *.cc \ @@ -874,7 +914,7 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = +EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded @@ -890,7 +930,7 @@ EXCLUDE_SYMLINKS = NO # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = +EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the @@ -901,13 +941,13 @@ EXCLUDE_PATTERNS = # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* -EXCLUDE_SYMBOLS = +EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). -EXAMPLE_PATH = +EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and @@ -927,7 +967,7 @@ EXAMPLE_RECURSIVE = NO # that contain images that are to be included in the documentation (see the # \image command). -IMAGE_PATH = +IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program @@ -948,7 +988,7 @@ IMAGE_PATH = # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. -INPUT_FILTER = +INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the @@ -961,7 +1001,7 @@ INPUT_FILTER = # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. -FILTER_PATTERNS = +FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for @@ -976,14 +1016,14 @@ FILTER_SOURCE_FILES = NO # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. -FILTER_SOURCE_PATTERNS = +FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. -USE_MDFILE_AS_MAINPAGE = +USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing @@ -1012,7 +1052,7 @@ INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. +# entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO @@ -1049,7 +1089,7 @@ SOURCE_TOOLTIPS = YES # # To use it do the following: # - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # @@ -1071,6 +1111,35 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files +# were built. This is equivalent to specifying the "-p" option to a clang tool, +# such as clang-check. These options will then be passed to the parser. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -1095,7 +1164,7 @@ COLS_IN_ALPHA_INDEX = 5 # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. -IGNORE_PREFIX = +IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output @@ -1139,7 +1208,7 @@ HTML_FILE_EXTENSION = .html # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_HEADER = +HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard @@ -1149,7 +1218,7 @@ HTML_HEADER = # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_FOOTER = +HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of @@ -1161,7 +1230,7 @@ HTML_FOOTER = # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_STYLESHEET = +HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets @@ -1174,7 +1243,7 @@ HTML_STYLESHEET = # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_EXTRA_STYLESHEET = +HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note @@ -1184,7 +1253,7 @@ HTML_EXTRA_STYLESHEET = # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_EXTRA_FILES = +HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to @@ -1227,9 +1296,9 @@ HTML_TIMESTAMP = NO # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that -# are dynamically created via Javascript. If disabled, the navigation index will +# are dynamically created via JavaScript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML -# page. Disable this option to support browsers that do not have Javascript, +# page. Disable this option to support browsers that do not have JavaScript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1259,13 +1328,13 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: https://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# environment (see: https://developer.apple.com/xcode/), introduced with OSX +# 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See https://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1304,7 +1373,7 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output @@ -1324,7 +1393,7 @@ GENERATE_HTMLHELP = NO # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. -CHM_FILE = +CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, @@ -1332,7 +1401,7 @@ CHM_FILE = # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. -HHC_LOCATION = +HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the master .chm file (NO). @@ -1345,7 +1414,7 @@ GENERATE_CHI = NO # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. -CHM_INDEX_ENCODING = +CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it @@ -1376,11 +1445,11 @@ GENERATE_QHP = NO # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. -QCH_FILE = +QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: http://doc.qt.io/qt-4.8/qthelpproject.html#namespace). +# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1388,7 +1457,8 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://doc.qt.io/qt-4.8/qthelpproject.html#virtual-folders). +# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- +# folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1396,31 +1466,33 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters). +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- +# filters). # This tag requires that the tag GENERATE_QHP is set to YES. -QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters). +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- +# filters). # This tag requires that the tag GENERATE_QHP is set to YES. -QHP_CUST_FILTER_ATTRS = +QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://doc.qt.io/qt-4.8/qthelpproject.html#filter-attributes). +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. -QHP_SECT_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. -QHG_LOCATION = +QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To @@ -1514,8 +1586,14 @@ FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# https://www.mathjax.org) which uses client side Javascript for the rendering +# https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path @@ -1543,7 +1621,7 @@ MATHJAX_FORMAT = HTML-CSS # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from https://www.mathjax.org before deployment. -# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/. +# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/ @@ -1553,7 +1631,7 @@ MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/ # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_EXTENSIONS = +MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site @@ -1561,7 +1639,7 @@ MATHJAX_EXTENSIONS = # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_CODEFILE = +MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and @@ -1585,7 +1663,7 @@ MATHJAX_CODEFILE = SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a web server instead of a web client using Javascript. There +# implemented using a web server instead of a web client using JavaScript. There # are two flavors of web server based searching depending on the EXTERNAL_SEARCH # setting. When disabled, doxygen will generate a PHP script for searching and # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing @@ -1621,7 +1699,7 @@ EXTERNAL_SEARCH = NO # Searching" for details. # This tag requires that the tag SEARCHENGINE is set to YES. -SEARCHENGINE_URL = +SEARCHENGINE_URL = # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed # search data is written to a file for indexing by an external tool. With the @@ -1637,7 +1715,7 @@ SEARCHDATA_FILE = searchdata.xml # projects and redirect the results back to the right project. # This tag requires that the tag SEARCHENGINE is set to YES. -EXTERNAL_SEARCH_ID = +EXTERNAL_SEARCH_ID = # The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen # projects other than the one defined by this configuration file, but that are @@ -1647,7 +1725,7 @@ EXTERNAL_SEARCH_ID = # EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... # This tag requires that the tag SEARCHENGINE is set to YES. -EXTRA_SEARCH_MAPPINGS = +EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # Configuration options related to the LaTeX output @@ -1669,21 +1747,35 @@ LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. # -# Note that when enabling USE_PDFLATEX this option is only used for generating -# bitmaps for formulas in the HTML output, but not in the Makefile that is -# written to the output directory. -# The default file is: latex. +# Note that when not enabling USE_PDFLATEX the default is latex when enabling +# USE_PDFLATEX the default is pdflatex and when in the later case latex is +# chosen this is overwritten by pdflatex. For specific output languages the +# default can have been set differently, this depends on the implementation of +# the output language. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate # index for LaTeX. +# Note: This tag is used in the Makefile / make.bat. +# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file +# (.tex). # The default file is: makeindex. # This tag requires that the tag GENERATE_LATEX is set to YES. MAKEINDEX_CMD_NAME = makeindex +# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to +# generate index for LaTeX. In case there is no backslash (\) as first character +# it will be automatically added in the LaTeX code. +# Note: This tag is used in the generated output file (.tex). +# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. +# The default value is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_MAKEINDEX_CMD = makeindex + # If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some # trees in general. @@ -1711,7 +1803,7 @@ PAPER_TYPE = a4 # If left blank no extra packages will be included. # This tag requires that the tag GENERATE_LATEX is set to YES. -EXTRA_PACKAGES = +EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for the # generated LaTeX document. The header should contain everything until the first @@ -1727,7 +1819,7 @@ EXTRA_PACKAGES = # to HTML_HEADER. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_HEADER = +LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the # generated LaTeX document. The footer should contain everything after the last @@ -1738,7 +1830,7 @@ LATEX_HEADER = # Note: Only use a user-defined footer if you know what you are doing! # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_FOOTER = +LATEX_FOOTER = # The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined # LaTeX style sheets that are included after the standard style sheets created @@ -1749,7 +1841,7 @@ LATEX_FOOTER = # list). # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_EXTRA_STYLESHEET = +LATEX_EXTRA_STYLESHEET = # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the LATEX_OUTPUT output @@ -1757,7 +1849,7 @@ LATEX_EXTRA_STYLESHEET = # markers available. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_EXTRA_FILES = +LATEX_EXTRA_FILES = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is # prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will @@ -1818,6 +1910,14 @@ LATEX_BIB_STYLE = plain LATEX_TIMESTAMP = NO +# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) +# path from which the emoji images will be read. If a relative path is entered, +# it will be relative to the LATEX_OUTPUT directory. If left blank the +# LATEX_OUTPUT directory will be used. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EMOJI_DIRECTORY = + #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- @@ -1857,22 +1957,22 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's config -# file, i.e. a series of assignments. You only have to provide replacements, -# missing definitions are set to their default value. +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# configuration file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. # # See also section "Doxygen usage" for information on how to generate the # default style sheet that doxygen normally uses. # This tag requires that the tag GENERATE_RTF is set to YES. -RTF_STYLESHEET_FILE = +RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is -# similar to doxygen's config file. A template extensions file can be generated -# using doxygen -e rtf extensionFile. +# similar to doxygen's configuration file. A template extensions file can be +# generated using doxygen -e rtf extensionFile. # This tag requires that the tag GENERATE_RTF is set to YES. -RTF_EXTENSIONS_FILE = +RTF_EXTENSIONS_FILE = # If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code # with syntax highlighting in the RTF output. @@ -1917,7 +2017,7 @@ MAN_EXTENSION = .3 # MAN_EXTENSION with the initial . removed. # This tag requires that the tag GENERATE_MAN is set to YES. -MAN_SUBDIR = +MAN_SUBDIR = # If the MAN_LINKS tag is set to YES and doxygen generates man output, then it # will generate one additional man file for each entity documented in the real @@ -1955,6 +2055,13 @@ XML_OUTPUT = xml XML_PROGRAMLISTING = YES +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# namespace members in file scope as well, matching the HTML output. +# The default value is: NO. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_NS_MEMB_FILE_SCOPE = NO + #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- @@ -2030,7 +2137,7 @@ PERLMOD_PRETTY = YES # overwrite each other's variables. # This tag requires that the tag GENERATE_PERLMOD is set to YES. -PERLMOD_MAKEVAR_PREFIX = +PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor @@ -2071,7 +2178,7 @@ SEARCH_INCLUDES = YES # preprocessor. # This tag requires that the tag SEARCH_INCLUDES is set to YES. -INCLUDE_PATH = +INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the @@ -2079,7 +2186,7 @@ INCLUDE_PATH = # used. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -INCLUDE_FILE_PATTERNS = +INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that are # defined before the preprocessor is started (similar to the -D option of e.g. @@ -2089,7 +2196,7 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = +PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The @@ -2098,7 +2205,7 @@ PREDEFINED = # definition found in the source code. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -EXPAND_AS_DEFINED = +EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will # remove all references to function-like macros that are alone on a line, have @@ -2127,13 +2234,13 @@ SKIP_FUNCTION_MACROS = YES # the path). If a tag file is not located in the directory in which doxygen is # run, you must also specify the path to the tagfile here. -TAGFILES = +TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create a # tag file that is based on the input files it reads. See section "Linking to # external documentation" for more information about the usage of tag files. -GENERATE_TAGFILE = +GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES, all external class will be listed in # the class index. If set to NO, only the inherited external classes will be @@ -2156,12 +2263,6 @@ EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of 'which perl'). -# The default file (with absolute path) is: /usr/bin/perl. - -PERL_PATH = /usr/bin/perl - #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- @@ -2175,21 +2276,12 @@ PERL_PATH = /usr/bin/perl CLASS_DIAGRAMS = YES -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see: -# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. # If left empty dia is assumed to be found in the default search path. -DIA_PATH = +DIA_PATH = # If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. @@ -2202,9 +2294,9 @@ HIDE_UNDOC_RELATIONS = YES # http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is # set to NO -# The default value is: NO. +# The default value is: YES. -HAVE_DOT = NO +HAVE_DOT = YES # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed # to run in parallel. When set to 0 doxygen will base this on the number of @@ -2238,7 +2330,7 @@ DOT_FONTSIZE = 10 # the path where dot can find it using this tag. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTPATH = +DOT_FONTPATH = # If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for # each documented class showing the direct and indirect inheritance relations. @@ -2358,7 +2450,9 @@ DIRECTORY_GRAPH = YES # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). -# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, +# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd, +# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo, +# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo, # png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and # png:gdiplus:gdiplus. # The default value is: png. @@ -2382,26 +2476,26 @@ INTERACTIVE_SVG = NO # found. If left blank, it is assumed the dot tool can be found in the path. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_PATH = +DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the \dotfile # command). # This tag requires that the tag HAVE_DOT is set to YES. -DOTFILE_DIRS = +DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the \mscfile # command). -MSCFILE_DIRS = +MSCFILE_DIRS = # The DIAFILE_DIRS tag can be used to specify one or more directories that # contain dia files that are included in the documentation (see the \diafile # command). -DIAFILE_DIRS = +DIAFILE_DIRS = # When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the # path where java can find the plantuml.jar file. If left blank, it is assumed @@ -2409,17 +2503,17 @@ DIAFILE_DIRS = # generate a warning when it encounters a \startuml command in this case and # will not generate output for the diagram. -PLANTUML_JAR_PATH = +PLANTUML_JAR_PATH = # When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a # configuration file for plantuml. -PLANTUML_CFG_FILE = +PLANTUML_CFG_FILE = # When using plantuml, the specified paths are searched for files specified by # the !include statement in a plantuml block. -PLANTUML_INCLUDE_PATH = +PLANTUML_INCLUDE_PATH = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes diff --git a/README.md b/README.md index 5d28a0e..bfa3edf 100644 --- a/README.md +++ b/README.md @@ -31,31 +31,48 @@ Future features (wish-list) For now, the source code is well-documented but it requires some additional documentation (the math behind it). This will be published -in a sister repository in a later stage. +in a sister repository (https://code.ascee.nl/ascee/lasp-doc). If you have any question(s), please feel free to contact us: info@ascee.nl. # Building from source -## Dependencies +Two commands that install all requirements (for Ubuntu / Linux Mint) -Optional dependencies, which can be turned ON/OFF using CMake: +- `pip install scipy numpy build scikit-build appdirs` +- `sudo apt install libusb-dev -- Build tools, including [http://cmake.org](CMake). -- FFTW (For really fast FFT's) +## Runtime dependencies (Linux) + +- FFTW (For really fast FFT's). If compiled with Ffftpack, this library is not + required. - libUlDAQ, for the Measurement Computing DT9837A USB DAQ box - GNU Autotools, for compiling libUlDAQ - RtAudio, for Audio DAQ backends -- Cython, for wrapping the C-code to Python. +- libusb +- BLAS (OpenBLAS, other). +- RtAudio (optional) +- UlDaq (optional) + + + +## Build dependencies + +Optional dependencies, which can be turned ON/OFF using CMake: + +- Build tools: compiler [http://cmake.org](CMake), the Python packages: + - Scipy + - Numpy + - py-build-cmake + - appdirs + These can all be installed using: + - The following Python packages need also be available: - `Scipy` (which includes Numpy). Install with `sudo apt install python3-scipy`, or `pacman -S scipy`. - `appdirs`, which can be grabbed from [https://pypi.org](Pypi) -## Installation of dependencies - - ## Compilation of LASP diff --git a/lasp/cmake/BuildType.cmake b/cmake/BuildType.cmake similarity index 100% rename from lasp/cmake/BuildType.cmake rename to cmake/BuildType.cmake diff --git a/lasp/cmake/FindNumpy.cmake b/cmake/FindNumpy.cmake similarity index 100% rename from lasp/cmake/FindNumpy.cmake rename to cmake/FindNumpy.cmake diff --git a/cmake/OSSpecific.cmake b/cmake/OSSpecific.cmake new file mode 100644 index 0000000..170c023 --- /dev/null +++ b/cmake/OSSpecific.cmake @@ -0,0 +1,29 @@ +if(WIN32) + set(home $ENV{USERPROFILE}) + + # set(miniconda_dir ${home}\\Miniconda3) + message("Building for Windows") + include_directories( + ..\\rtaudio + C:\\mingw\\mingw64\\include\\OpenBLAS + link_directories(${home}\\miniconda3\\Library\\include) + ) + set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH} $miniconda_dir\\Lib\\cmake") + # include( + add_definitions(-DMS_WIN64) + link_directories(C:\\mingw\\mingw64\\lib) + link_directories(C:\\mingw\\mingw64\\bin) + link_directories(..\\rtaudio) + link_directories(${home}\\Miniconda3) + add_definitions(-DHAS_RTAUDIO_WIN_WASAPI_API) +else() # Linux compile + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wfatal-errors") + include_directories(/usr/local/include/rtaudio) + include_directories(/usr/include/rtaudio) + link_directories(/usr/local/lib) + # This should become optional later on, and be added to the windows list as + # well. + +endif() +# The last argument here takes care of calling SIGABRT when an integer overflow +# occures. diff --git a/cmake/QueryPythonForPybind11.cmake b/cmake/QueryPythonForPybind11.cmake new file mode 100644 index 0000000..8be6181 --- /dev/null +++ b/cmake/QueryPythonForPybind11.cmake @@ -0,0 +1,26 @@ +# First tries to find Python 3, then tries to import the pybind11 module to +# query the CMake config location, and finally imports pybind11 using +# find_package(pybind11 REQUIRED CONFIG). +function(find_pybind11_python_first) + + # Query Python to see if it knows where the headers are + find_package(Python3 REQUIRED COMPONENTS Interpreter Development) + if (NOT pybind11_ROOT OR NOT EXISTS ${pybind11_ROOT}) + execute_process(COMMAND ${Python3_EXECUTABLE} + -m pybind11 --cmakedir + OUTPUT_VARIABLE PY_BUILD_PYBIND11_CMAKE + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE PY_BUILD_CMAKE_PYBIND11_RESULT) + # If it was successful + if (PY_BUILD_CMAKE_PYBIND11_RESULT EQUAL 0) + message(STATUS "Found pybind11: ${PY_BUILD_PYBIND11_CMAKE}") + set(pybind11_ROOT ${PY_BUILD_PYBIND11_CMAKE} + CACHE PATH "Path to the pybind11 CMake configuration." FORCE) + else() + unset(pybind11_ROOT CACHE) + endif() + endif() + + find_package(pybind11 REQUIRED CONFIG) + +endfunction() diff --git a/lasp/cmake/ReplicatePythonSourceTree.cmake b/cmake/ReplicatePythonSourceTree.cmake similarity index 100% rename from lasp/cmake/ReplicatePythonSourceTree.cmake rename to cmake/ReplicatePythonSourceTree.cmake diff --git a/lasp/CMakeLists.txt b/lasp/CMakeLists.txt deleted file mode 100644 index db5719f..0000000 --- a/lasp/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -add_subdirectory(c) -add_subdirectory(device) -add_subdirectory(dsp) diff --git a/lasp/device/__init__.py b/lasp/device/__init__.py deleted file mode 100644 index b55f7a5..0000000 --- a/lasp/device/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .lasp_device import * - diff --git a/lasp/device/lasp_daqconfig.cpp b/lasp/device/lasp_daqconfig.cpp deleted file mode 100644 index afcd4c7..0000000 --- a/lasp/device/lasp_daqconfig.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include "lasp_daqconfig.h" -#include "lasp_deviceinfo.h" -#include -#include - -#define MAX_DEV_COUNT_PER_API 20 - -using std::vector; - - -vector DaqApi::getAvailableApis() { - - vector apis; - apis.resize(6); -#if LASP_HAS_ULDAQ == 1 - apis.at(uldaqapi.apicode) = uldaqapi; -#endif -#if LASP_HAS_RTAUDIO == 1 - apis.at(rtaudioAlsaApi.apicode) = rtaudioAlsaApi; - apis.at(rtaudioPulseaudioApi.apicode) = rtaudioPulseaudioApi; - apis.at(rtaudioWasapiApi.apicode) = rtaudioWasapiApi; - apis.at(rtaudioDsApi.apicode) = rtaudioDsApi; - apis.at(rtaudioAsioApi.apicode) = rtaudioAsioApi; -#endif - return apis; -} - -DaqConfiguration::DaqConfiguration(const DeviceInfo &device) { - - api = device.api; - device_name = device.device_name; - - eninchannels.resize(device.ninchannels, false); - enoutchannels.resize(device.noutchannels, false); - - inchannel_sensitivities.resize(device.ninchannels, 1.0); - inchannel_metadata.resize(device.ninchannels, ""); - for (us i = 0; i < eninchannels.size(); i++) { - std::stringstream chname; - chname << "Unnamed input channel " << i; - inchannel_names.push_back(chname.str()); - } - - outchannel_metadata.resize(device.noutchannels, ""); - outchannel_sensitivities.resize(device.noutchannels, 1.0); - for (us i = 0; i < enoutchannels.size(); i++) { - std::stringstream chname; - chname << "Unnamed output channel " << i; - outchannel_names.push_back(chname.str()); - } - sampleRateIndex = device.prefSampleRateIndex; - dataTypeIndex = device.prefDataTypeIndex; - framesPerBlockIndex = device.prefFramesPerBlockIndex; - - monitorOutput = false; - - inputIEPEEnabled.resize(device.ninchannels, false); - inputACCouplingMode.resize(device.ninchannels, false); - inputRangeIndices.resize(device.ninchannels, device.prefInputRangeIndex); - - assert(match(device)); -} - -bool DaqConfiguration::match(const DeviceInfo &dev) const { - return (dev.device_name == device_name && dev.api == api); -} - -int DaqConfiguration::getHighestInChannel() const { - for (int i = eninchannels.size() - 1; i > -1; i--) { - if (eninchannels.at(i)) - return i; - } - return -1; -} - -int DaqConfiguration::getHighestOutChannel() const { - for (us i = enoutchannels.size() - 1; i >= 0; i--) { - if (enoutchannels.at(i)) - return i; - } - return -1; -} -int DaqConfiguration::getLowestInChannel() const { - for (us i = 0; i < eninchannels.size(); i++) { - if (eninchannels.at(i)) - return i; - } - return -1; -} -int DaqConfiguration::getLowestOutChannel() const { - for (us i = 0; i < enoutchannels.size(); i++) { - if (enoutchannels.at(i)) - return i; - } - return -1; -} - diff --git a/lasp/device/lasp_deviceinfo.cpp b/lasp/device/lasp_deviceinfo.cpp deleted file mode 100644 index c3a8be1..0000000 --- a/lasp/device/lasp_deviceinfo.cpp +++ /dev/null @@ -1,139 +0,0 @@ -#include "lasp_deviceinfo.h" -#include -#include - -#define MAX_DEV_COUNT_PER_API 20 - -#if LASP_HAS_ULDAQ == 1 -#include "lasp_uldaq.h" -#endif -#if LASP_HAS_RTAUDIO == 1 -#include "lasp_rtaudiodaq.h" -#endif - -string DeviceInfo::serialize() const { - // Simple serializer for this object, used because we found a bit late - // that this object needs to be send over the wire. We do not want to make - // this implementation in Python, as these objects are created here, in - // the C++ code. The Python wrapper is just a readonly wrapper. - std::stringstream str; - - str << api.apiname << "\t"; - str << api.apicode << "\t"; - str << api.api_specific_subcode << "\t"; - str << device_name << "\t"; - - str << availableDataTypes.size() << "\t"; - for (const auto &dtype : availableDataTypes) { - // WARNING: THIS GOES COMPLETELY WRONG WHEN NAMES contain A TAB!!! - str << static_cast(dtype) << "\t"; - } - str << prefDataTypeIndex << "\t"; - - str << availableSampleRates.size() << "\t"; - for (const double &fs : availableSampleRates) { - // WARNING: THIS GOES COMPLETELY WRONG WHEN NAMES contain A TAB!!! - str << fs << "\t"; - } - str << prefSampleRateIndex << "\t"; - - str << availableFramesPerBlock.size() << "\t"; - for (const us &fb : availableFramesPerBlock) { - // WARNING: THIS GOES COMPLETELY WRONG WHEN NAMES contain A TAB!!! - str << fb << "\t"; - } - str << prefFramesPerBlockIndex << "\t"; - - str << availableInputRanges.size() << "\t"; - for (const double &ir : availableInputRanges) { - // WARNING: THIS GOES COMPLETELY WRONG WHEN NAMES contain A TAB!!! - str << ir << "\t"; - } - str << prefInputRangeIndex << "\t"; - - str << ninchannels << "\t"; - str << noutchannels << "\t"; - str << int(hasInputIEPE) << "\t"; - str << int(hasInputACCouplingSwitch) << "\t"; - str << int(hasInputTrigger) << "\t"; - - return str.str(); -} - -DeviceInfo DeviceInfo::deserialize(const string &dstr) { - DeviceInfo devinfo; - - std::stringstream str(dstr); - string tmp; - us N; - // Lambda functions for deserializing - auto nexts = [&]() { - getline(str, tmp, '\t'); - return tmp; - }; - auto nexti = [&]() { - getline(str, tmp, '\t'); - return std::atoi(tmp.c_str()); - }; - auto nextf = [&]() { - getline(str, tmp, '\t'); - return std::atof(tmp.c_str()); - }; - - // Api - string apiname = nexts(); - auto apicode = nexti(); - auto api_specific_subcode = nexti(); - DaqApi api(apiname, apicode, api_specific_subcode); - devinfo.api = api; - - devinfo.device_name = nexts(); - - N = us(nexti()); - for (us i = 0; i < N; i++) { - devinfo.availableDataTypes.push_back( - static_cast(nexti())); - } - - devinfo.prefDataTypeIndex = nexti(); - - N = us(nexti()); - for (us i = 0; i < N; i++) { - devinfo.availableSampleRates.push_back(nextf()); - } - devinfo.prefSampleRateIndex = nexti(); - - N = us(nexti()); - for (us i = 0; i < N; i++) { - devinfo.availableFramesPerBlock.push_back(nexti()); - } - devinfo.prefFramesPerBlockIndex = nexti(); - - N = us(nexti()); - for (us i = 0; i < N; i++) { - devinfo.availableInputRanges.push_back(nexti()); - } - devinfo.prefInputRangeIndex = nexti(); - - devinfo.ninchannels = nexti(); - devinfo.noutchannels = nexti(); - devinfo.hasInputIEPE = bool(nexti()); - devinfo.hasInputACCouplingSwitch = bool(nexti()); - devinfo.hasInputTrigger = bool(nexti()); - - return devinfo; -} - -std::vector DeviceInfo::getDeviceInfo() { - std::vector devs; -#if LASP_HAS_ULDAQ ==1 - fillUlDaqDeviceInfo(devs); -#endif - -#if LASP_HAS_RTAUDIO == 1 - fillRtAudioDeviceInfo(devs); -#endif - - return devs; -} - diff --git a/lasp/device/lasp_devicepybind.cpp b/lasp/device/lasp_devicepybind.cpp deleted file mode 100644 index 71fd668..0000000 --- a/lasp/device/lasp_devicepybind.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include -#include -#include -#include -#include -#include "lasp_daqconfig.h" -#include "lasp_deviceinfo.h" - -using std::cerr; -namespace py = pybind11; - -PYBIND11_MODULE(lasp_device, m) { - - m.doc() = "Lasp device interface"; - - /// DataType - py::class_ dtype_desc(m, "DataTypeDescriptor"); - dtype_desc.def_readonly("name", &DataTypeDescriptor::name); - dtype_desc.def_readonly("sw", &DataTypeDescriptor::sw); - dtype_desc.def_readonly("is_floating", &DataTypeDescriptor::is_floating); - - py::enum_(dtype_desc, "DataType") - .value("dtype_fl32", DataTypeDescriptor::DataType::dtype_fl32) - .value("dtype_fl64", DataTypeDescriptor::DataType::dtype_fl64) - .value("dtype_int8", DataTypeDescriptor::DataType::dtype_int8) - .value("dtype_int16", DataTypeDescriptor::DataType::dtype_int16) - .value("dtype_int32", DataTypeDescriptor::DataType::dtype_int32).export_values(); - - dtype_desc.def_readonly("dtype", &DataTypeDescriptor::dtype); - - - /// DaqApi - py::class_ daqapi(m, "DaqApi"); - daqapi.def_readonly("apiname", &DaqApi::apiname); - daqapi.def_readonly("apicode", &DaqApi::apicode); - daqapi.def_readonly("api_specific_subcode", &DaqApi::api_specific_subcode); - daqapi.def("__str__", [](const DaqApi &d) { return std::string(d); }); - - /// DeviceInfo - py::class_ devinfo(m, "DeviceInfo"); - devinfo.def_readonly("api", &DeviceInfo::api); - devinfo.def_readonly("device_name", &DeviceInfo::device_name); - - devinfo.def_readonly("availableDataTypes", &DeviceInfo::availableDataTypes); - devinfo.def_readonly("prefDataTypeIndex", &DeviceInfo::prefDataTypeIndex); - - devinfo.def_readonly("availableSampleRates", - &DeviceInfo::availableSampleRates); - devinfo.def_readonly("prefSampleRateIndex", &DeviceInfo::prefSampleRateIndex); - - devinfo.def_readonly("availableFramesPerBlock", - &DeviceInfo::availableFramesPerBlock); - devinfo.def_readonly("prefFramesPerBlockIndex", - &DeviceInfo::prefFramesPerBlockIndex); - - devinfo.def_readonly("availableInputRanges", - &DeviceInfo::availableInputRanges); - devinfo.def_readonly("prefInputRangeIndex", &DeviceInfo::prefInputRangeIndex); - - devinfo.def_readonly("ninchannels", &DeviceInfo::ninchannels); - devinfo.def_readonly("noutchannels", &DeviceInfo::noutchannels); - devinfo.def_readonly("hasInputIEPE", &DeviceInfo::hasInputIEPE); - devinfo.def_readonly("hasInputACCouplingSwitch", - &DeviceInfo::hasInputACCouplingSwitch); - - devinfo.def("serialize", &DeviceInfo::serialize); - - devinfo.def_static("deserialize", &DeviceInfo::deserialize); - - /// DaqConfiguration - py::class_ daqconfig(m, "DaqConfiguration"); - daqconfig.def(py::init<>()); - daqconfig.def_readwrite("eninchannels", &DaqConfiguration::eninchannels); - daqconfig.def_readwrite("enoutchannels", &DaqConfiguration::enoutchannels); - - daqconfig.def_readwrite("inchannel_sensitivities", - &DaqConfiguration::inchannel_sensitivities); - daqconfig.def_readwrite("inchannel_metadata", - &DaqConfiguration::inchannel_metadata); - daqconfig.def_readwrite("inchannel_names", - &DaqConfiguration::inchannel_names); - - daqconfig.def_readwrite("outchannel_sensitivities", - &DaqConfiguration::outchannel_sensitivities); - daqconfig.def_readwrite("outchannel_names", - &DaqConfiguration::outchannel_names); - daqconfig.def_readwrite("inchannel_metadata", - &DaqConfiguration::inchannel_metadata); - - daqconfig.def_readwrite("sampleRateIndex", - &DaqConfiguration::sampleRateIndex); - daqconfig.def_readwrite("dataTypeIndex", - &DaqConfiguration::dataTypeIndex); - - daqconfig.def_readwrite("framesPerBlockIndex", - &DaqConfiguration::framesPerBlockIndex); - daqconfig.def_readwrite("monitorOutput", - &DaqConfiguration::monitorOutput); - - daqconfig.def_readwrite("inputIEPEEnabled", - &DaqConfiguration::inputIEPEEnabled); - daqconfig.def_readwrite("inputACCouplingMode", - &DaqConfiguration::inputACCouplingMode); - - daqconfig.def_readwrite("inputRangeIndices", - &DaqConfiguration::inputRangeIndices); - - daqconfig.def("match", &DaqConfiguration::match); - -} - diff --git a/lasp/dsp/CMakeLists.txt b/lasp/dsp/CMakeLists.txt deleted file mode 100644 index 344f028..0000000 --- a/lasp/dsp/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -configure_file(lasp_config.h.in lasp_config.h) - -set(lasp_dsp_files - lasp_filter.cpp - lasp_siggen.cpp - lasp_siggen_impl.cpp - lasp_window.cpp - ) - -SET_SOURCE_FILES_PROPERTIES(${lasp_dsp_files} PROPERTIES COMPILE_FLAGS - "-DARMA_DONT_USE_WRAPPER") - - -add_library(lasp_dsp_lib STATIC ${lasp_dsp_files}) - -pybind11_add_module(lasp_dsp lasp_dsp_pybind.cpp) - -target_link_libraries(lasp_dsp PRIVATE lasp_dsp_lib ${BLAS_LIBRARIES}) - -target_include_directories(lasp_dsp_lib PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) - diff --git a/lasp/dsp/__init__.py b/lasp/dsp/__init__.py deleted file mode 100644 index 47e3ef5..0000000 --- a/lasp/dsp/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .lasp_dsp import * diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..9dfcc2b --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,27 @@ +[project] # Project metadata +name = "lasp" +readme = "README.md" +requires-python = ">=3.8" +license = { "file" = "LICENSE" } +authors = [{ "name" = "J.A. de Jong et al.", "email" = "info@ascee.nl" }] +keywords = ["DSP", "DAQ", "Signal processing"] +classifiers = [ + "Topic :: Scientific/Engineering", + "Programming Language :: Python :: 3.8", + "Operating System :: POSIX :: Linux", + "Operating System :: Microsoft :: Windows", +] +# urls = { "Documentation" = "https://" } +dependencies = ["numpy", "scipy", "appdirs"] +dynamic = ["version", "description"] + +[build-system] # How pip and other frontends should build this project +requires = [ + "setuptools>=42", + "wheel", + "scikit-build", + "cmake", +] +build-backend = "setuptools.build_meta" + + diff --git a/setup.py b/setup.py old mode 100755 new mode 100644 index 2ce4fda..65fef76 --- a/setup.py +++ b/setup.py @@ -1,28 +1,15 @@ -#!/usr/bin/env python3.8 -# -*- coding: utf-8 -*- -""" -@author: J.A. de Jong - ASCEE -""" -from setuptools import setup, find_packages +from skbuild import setup setup( - name="LASP", - version="1.0", - packages=find_packages(), - long_description=open("./README.md", 'r').read(), - long_description_content_type="text/markdown", - # ext_modules=[CMakeExtension('lasp/wrappers.so'), - # ], - #package_data={'lasp': ['wrappers.so']}, - author='J.A. de Jong - ASCEE', - author_email="j.a.dejong@ascee.nl", - install_requires=['matplotlib>=1.0', - 'scipy>=1.0', 'numpy>=1.0', 'h5py', - 'dataclasses_json', 'cython', - ], - license='MIT', - description="Library for Acoustic Signal Processing", - keywords="", - url="https://www.ascee.nl/lasp/", # project home page - + name="lasp", + version="1.0", + description="LASP Library of Acoustic Signal Processing", + author='J.A. de Jong (ASCEE / Redu-Sone)', + author_email='info@ascee.nl', + license="MIT", + packages=['lasp'], + package_dir= {'': 'src'}, + cmake_install_dir='src/lasp', + # cmake_install_target='src', + python_requires='>=3.8', ) diff --git a/src/lasp/CMakeLists.txt b/src/lasp/CMakeLists.txt new file mode 100644 index 0000000..671406a --- /dev/null +++ b/src/lasp/CMakeLists.txt @@ -0,0 +1,28 @@ +# cpp_src/CMakeLists.txt + +# Armadillo +add_definitions(-DARMA_DONT_USE_WRAPPER) + +configure_file(lasp_config.h.in lasp_config.h) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) +include_directories(SYSTEM ../../third_party/armadillo-code/include) +include_directories(../../third_party/DebugTrace-cpp/include) +include_directories(../../third_party/STL-Threadsafe/include) +include_directories(../../third_party/gsl-lite/include) +include_directories(../../third_party/tomlplusplus/include) + +add_subdirectory(device) +add_subdirectory(dsp) + +pybind11_add_module(lasp_cpp MODULE lasp_cpp.cpp + pybind11/lasp_deviceinfo.cpp + pybind11/lasp_daqconfiguration.cpp + pybind11//lasp_dsp_pybind.cpp + pybind11/lasp_streammgr.cpp + pybind11/lasp_daq.cpp + pybind11/lasp_deviceinfo.cpp + ) + +target_link_libraries(lasp_cpp PRIVATE lasp_device_lib lasp_dsp_lib) + +install(TARGETS lasp_cpp DESTINATION .) diff --git a/lasp/__init__.py b/src/lasp/__init__.py similarity index 100% rename from lasp/__init__.py rename to src/lasp/__init__.py diff --git a/lasp/c/CMakeLists.txt b/src/lasp/c/CMakeLists.txt similarity index 100% rename from lasp/c/CMakeLists.txt rename to src/lasp/c/CMakeLists.txt diff --git a/lasp/c/dec_filter_4.c b/src/lasp/c/dec_filter_4.c similarity index 100% rename from lasp/c/dec_filter_4.c rename to src/lasp/c/dec_filter_4.c diff --git a/lasp/c/lasp_alg.c b/src/lasp/c/lasp_alg.c similarity index 100% rename from lasp/c/lasp_alg.c rename to src/lasp/c/lasp_alg.c diff --git a/lasp/c/lasp_alg.h b/src/lasp/c/lasp_alg.h similarity index 100% rename from lasp/c/lasp_alg.h rename to src/lasp/c/lasp_alg.h diff --git a/lasp/c/lasp_alloc.h b/src/lasp/c/lasp_alloc.h similarity index 100% rename from lasp/c/lasp_alloc.h rename to src/lasp/c/lasp_alloc.h diff --git a/lasp/c/lasp_aps.c b/src/lasp/c/lasp_aps.c similarity index 100% rename from lasp/c/lasp_aps.c rename to src/lasp/c/lasp_aps.c diff --git a/lasp/c/lasp_aps.h b/src/lasp/c/lasp_aps.h similarity index 100% rename from lasp/c/lasp_aps.h rename to src/lasp/c/lasp_aps.h diff --git a/lasp/c/lasp_assert.c b/src/lasp/c/lasp_assert.c similarity index 100% rename from lasp/c/lasp_assert.c rename to src/lasp/c/lasp_assert.c diff --git a/lasp/c/lasp_assert.h b/src/lasp/c/lasp_assert.h similarity index 100% rename from lasp/c/lasp_assert.h rename to src/lasp/c/lasp_assert.h diff --git a/lasp/c/lasp_bem.c b/src/lasp/c/lasp_bem.c similarity index 100% rename from lasp/c/lasp_bem.c rename to src/lasp/c/lasp_bem.c diff --git a/lasp/c/lasp_decimation.c b/src/lasp/c/lasp_decimation.c similarity index 100% rename from lasp/c/lasp_decimation.c rename to src/lasp/c/lasp_decimation.c diff --git a/lasp/c/lasp_decimation.h b/src/lasp/c/lasp_decimation.h similarity index 100% rename from lasp/c/lasp_decimation.h rename to src/lasp/c/lasp_decimation.h diff --git a/lasp/c/lasp_dfifo.c b/src/lasp/c/lasp_dfifo.c similarity index 100% rename from lasp/c/lasp_dfifo.c rename to src/lasp/c/lasp_dfifo.c diff --git a/lasp/c/lasp_dfifo.h b/src/lasp/c/lasp_dfifo.h similarity index 100% rename from lasp/c/lasp_dfifo.h rename to src/lasp/c/lasp_dfifo.h diff --git a/lasp/c/lasp_eq.c b/src/lasp/c/lasp_eq.c similarity index 100% rename from lasp/c/lasp_eq.c rename to src/lasp/c/lasp_eq.c diff --git a/lasp/c/lasp_eq.h b/src/lasp/c/lasp_eq.h similarity index 100% rename from lasp/c/lasp_eq.h rename to src/lasp/c/lasp_eq.h diff --git a/lasp/c/lasp_fft.c b/src/lasp/c/lasp_fft.c similarity index 100% rename from lasp/c/lasp_fft.c rename to src/lasp/c/lasp_fft.c diff --git a/lasp/c/lasp_fft.h b/src/lasp/c/lasp_fft.h similarity index 100% rename from lasp/c/lasp_fft.h rename to src/lasp/c/lasp_fft.h diff --git a/lasp/c/lasp_firfilterbank.c b/src/lasp/c/lasp_firfilterbank.c similarity index 100% rename from lasp/c/lasp_firfilterbank.c rename to src/lasp/c/lasp_firfilterbank.c diff --git a/lasp/c/lasp_firfilterbank.h b/src/lasp/c/lasp_firfilterbank.h similarity index 100% rename from lasp/c/lasp_firfilterbank.h rename to src/lasp/c/lasp_firfilterbank.h diff --git a/lasp/c/lasp_mat.c b/src/lasp/c/lasp_mat.c similarity index 100% rename from lasp/c/lasp_mat.c rename to src/lasp/c/lasp_mat.c diff --git a/lasp/c/lasp_mat.h b/src/lasp/c/lasp_mat.h similarity index 100% rename from lasp/c/lasp_mat.h rename to src/lasp/c/lasp_mat.h diff --git a/lasp/c/lasp_math_raw.c b/src/lasp/c/lasp_math_raw.c similarity index 100% rename from lasp/c/lasp_math_raw.c rename to src/lasp/c/lasp_math_raw.c diff --git a/lasp/c/lasp_math_raw.h b/src/lasp/c/lasp_math_raw.h similarity index 100% rename from lasp/c/lasp_math_raw.h rename to src/lasp/c/lasp_math_raw.h diff --git a/lasp/c/lasp_mq.c b/src/lasp/c/lasp_mq.c similarity index 100% rename from lasp/c/lasp_mq.c rename to src/lasp/c/lasp_mq.c diff --git a/lasp/c/lasp_mq.h b/src/lasp/c/lasp_mq.h similarity index 100% rename from lasp/c/lasp_mq.h rename to src/lasp/c/lasp_mq.h diff --git a/lasp/c/lasp_nprocs.c b/src/lasp/c/lasp_nprocs.c similarity index 100% rename from lasp/c/lasp_nprocs.c rename to src/lasp/c/lasp_nprocs.c diff --git a/lasp/c/lasp_nprocs.h b/src/lasp/c/lasp_nprocs.h similarity index 100% rename from lasp/c/lasp_nprocs.h rename to src/lasp/c/lasp_nprocs.h diff --git a/lasp/c/lasp_ps.c b/src/lasp/c/lasp_ps.c similarity index 100% rename from lasp/c/lasp_ps.c rename to src/lasp/c/lasp_ps.c diff --git a/lasp/c/lasp_ps.h b/src/lasp/c/lasp_ps.h similarity index 100% rename from lasp/c/lasp_ps.h rename to src/lasp/c/lasp_ps.h diff --git a/lasp/c/lasp_pyarray.h b/src/lasp/c/lasp_pyarray.h similarity index 100% rename from lasp/c/lasp_pyarray.h rename to src/lasp/c/lasp_pyarray.h diff --git a/lasp/c/lasp_python.h b/src/lasp/c/lasp_python.h similarity index 100% rename from lasp/c/lasp_python.h rename to src/lasp/c/lasp_python.h diff --git a/lasp/c/lasp_signals.h b/src/lasp/c/lasp_signals.h similarity index 100% rename from lasp/c/lasp_signals.h rename to src/lasp/c/lasp_signals.h diff --git a/lasp/c/lasp_slm.c b/src/lasp/c/lasp_slm.c similarity index 100% rename from lasp/c/lasp_slm.c rename to src/lasp/c/lasp_slm.c diff --git a/lasp/c/lasp_slm.h b/src/lasp/c/lasp_slm.h similarity index 100% rename from lasp/c/lasp_slm.h rename to src/lasp/c/lasp_slm.h diff --git a/lasp/c/lasp_sosfilterbank.c b/src/lasp/c/lasp_sosfilterbank.c similarity index 100% rename from lasp/c/lasp_sosfilterbank.c rename to src/lasp/c/lasp_sosfilterbank.c diff --git a/lasp/c/lasp_sosfilterbank.h b/src/lasp/c/lasp_sosfilterbank.h similarity index 100% rename from lasp/c/lasp_sosfilterbank.h rename to src/lasp/c/lasp_sosfilterbank.h diff --git a/lasp/c/lasp_worker.c b/src/lasp/c/lasp_worker.c similarity index 100% rename from lasp/c/lasp_worker.c rename to src/lasp/c/lasp_worker.c diff --git a/lasp/c/lasp_worker.h b/src/lasp/c/lasp_worker.h similarity index 100% rename from lasp/c/lasp_worker.h rename to src/lasp/c/lasp_worker.h diff --git a/lasp/device/CMakeLists.txt b/src/lasp/device/CMakeLists.txt similarity index 57% rename from lasp/device/CMakeLists.txt rename to src/lasp/device/CMakeLists.txt index 0490c57..77ee57e 100644 --- a/lasp/device/CMakeLists.txt +++ b/src/lasp/device/CMakeLists.txt @@ -1,3 +1,5 @@ +# cpp_src/src/device/CMakeLists.txt + add_library(lasp_device_lib OBJECT lasp_daq.cpp lasp_daqconfig.cpp @@ -8,16 +10,23 @@ add_library(lasp_device_lib OBJECT lasp_uldaq.cpp ) +# Callback requires certain arguments that are not used by code. This disables +# a compiler warning about it. +set_source_files_properties(lasp_rtaudiodaq.cpp PROPERTIES COMPILE_OPTIONS + "-Wno-unused") + target_include_directories(lasp_device_lib PUBLIC ../dsp) target_include_directories(lasp_device_lib PUBLIC ../c) +target_include_directories(lasp_device_lib INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}) + if(LASP_HAS_ULDAQ) target_link_libraries(lasp_device_lib uldaq) endif() if(LASP_HAS_RTAUDIO) target_link_libraries(lasp_device_lib rtaudio) endif() + target_link_libraries(lasp_device_lib lasp_dsp_lib) - -pybind11_add_module(lasp_device lasp_devicepybind.cpp) -target_link_libraries(lasp_device PRIVATE lasp_device_lib) +target_link_libraries(lasp_device_lib PRIVATE ) diff --git a/lasp/device/lasp_daq.cpp b/src/lasp/device/lasp_daq.cpp similarity index 51% rename from lasp/device/lasp_daq.cpp rename to src/lasp/device/lasp_daq.cpp index 44fd577..9a4821c 100644 --- a/lasp/device/lasp_daq.cpp +++ b/src/lasp/device/lasp_daq.cpp @@ -11,32 +11,15 @@ DEBUGTRACE_VARIABLES; #endif using std::runtime_error; +Daq::~Daq() {} + std::unique_ptr Daq::createDaq(const DeviceInfo &devinfo, const DaqConfiguration &config) { DEBUGTRACE_ENTER; - if (!config.match(devinfo)) { - throw std::runtime_error("DaqConfiguration does not match device info"); - } - - // Some basic sanity checks - if ((devinfo.ninchannels != config.eninchannels.size())) { - /* cerr << "devinfo.ninchannels: " << devinfo.ninchannels << endl; */ - /* cerr << "config.eninchannels.size(): " << config.eninchannels.size() << - * endl; */ - throw runtime_error("Invalid length of enabled input channels specified"); - } - if ((devinfo.noutchannels != config.enoutchannels.size())) { - throw runtime_error("outvalid length of enabled output channels specified"); - } - int apicode = devinfo.api.apicode; - if (devinfo.api == DaqApi()) { - throw std::runtime_error(string("Unable to match API: ") + - devinfo.api.apiname); - } #if LASP_HAS_ULDAQ == 1 - else if (devinfo.api == uldaqapi) { + if (devinfo.api == uldaqapi) { return createUlDaqDevice(devinfo, config); } #endif @@ -53,14 +36,23 @@ std::unique_ptr Daq::createDaq(const DeviceInfo &devinfo, Daq::Daq(const DeviceInfo &devinfo, const DaqConfiguration &config) : DaqConfiguration(config), DeviceInfo(devinfo) { - DEBUGTRACE_ENTER; + DEBUGTRACE_ENTER; - if (monitorOutput && !(nenoutchannels() > 0)) { - throw runtime_error( - "Output monitoring only possible when at least one output channel is " - "enabled. Please make sure to enable at least one output channel, or " - "disable monitoring output."); + if (!hasInternalOutputMonitor && monitorOutput) { + throw std::runtime_error( + "Output monitor flag set, but device does not have output monitor"); } + + if (!config.match(devinfo)) { + throw std::runtime_error("DaqConfiguration does not match device info"); + } + if(neninchannels(false) > ninchannels) { + throw std::runtime_error("Number of enabled input channels is higher than device capability"); + } + if(nenoutchannels() > noutchannels) { + throw std::runtime_error("Number of enabled output channels is higher than device capability"); + } + } double Daq::samplerate() const { @@ -70,25 +62,40 @@ double Daq::samplerate() const { DataTypeDescriptor::DataType Daq::dataType() const { return availableDataTypes.at(dataTypeIndex); } -DataTypeDescriptor Daq::dtypeDescr() const { - return dtype_map.at(dataType()); -} +DataTypeDescriptor Daq::dtypeDescr() const { return dtype_map.at(dataType()); } double Daq::inputRangeForChannel(us ch) const { if (!(ch < ninchannels)) { throw runtime_error("Invalid channel number"); } - return availableInputRanges.at(inputRangeIndices.at(ch)); + return availableInputRanges.at(inchannel_config[ch].rangeIndex); } us Daq::neninchannels(bool include_monitorchannel) const { - us inch = std::count(eninchannels.begin(), eninchannels.end(), true); - if (monitorOutput && include_monitorchannel) { - inch += nenoutchannels(); - } - return inch; + boolvec eninchannels = this->eninchannels(include_monitorchannel); + return std::count(eninchannels.cbegin(), eninchannels.cend(), true); } us Daq::nenoutchannels() const { - return std::count(enoutchannels.begin(), enoutchannels.end(), true); + boolvec enchannels = this->enoutchannels(); + return std::count(enchannels.cbegin(), enchannels.cend(), true); +} + +boolvec Daq::eninchannels(bool include_monitor) const { + boolvec res; + if (hasInternalOutputMonitor && include_monitor) { + res.push_back(monitorOutput); + } + + for (auto &ch : inchannel_config) { + res.push_back(ch.enabled); + } + return res; +} +boolvec Daq::enoutchannels() const { + boolvec res; + for (auto &ch : outchannel_config) { + res.push_back(ch.enabled); + } + return res; } diff --git a/lasp/device/lasp_daq.h b/src/lasp/device/lasp_daq.h similarity index 62% rename from lasp/device/lasp_daq.h rename to src/lasp/device/lasp_daq.h index 42eade1..550dd39 100644 --- a/lasp/device/lasp_daq.h +++ b/src/lasp/device/lasp_daq.h @@ -7,24 +7,6 @@ #include #include -/** - * @brief Information regarding a stream. - */ -class StreamStatus { -public: - bool isRunning = false; - bool error = false; - std::string error_msg{}; - - /** - * @brief Returns true if everything is OK with a certain stream and the - * stream is running. - * - * @return as described above. - */ - bool runningOK() const { return isRunning && !error; } -}; - /** * @brief Callback of DAQ for input data. Callback should return * false for a stop request. @@ -46,6 +28,55 @@ protected: Daq(const Daq &) = delete; public: + /** + * @brief Information regarding a stream. + */ + class StreamStatus { + public: + enum class StreamError { + noError, + inputXRun, + outputXRun, + driverError, + systemError, + threadError, + logicError, + apiSpecificError + }; + + /** + * @brief Map between error types and messages + */ + inline static const std::map errorMessages{ + {StreamError::noError, "No error"}, + {StreamError::inputXRun, "Input buffer overrun"}, + {StreamError::outputXRun, "Output buffer underrun"}, + {StreamError::driverError, "Driver error"}, + {StreamError::systemError, "System error"}, + {StreamError::threadError, "Thread error"}, + {StreamError::logicError, "Logic error (probably a bug)"}, + }; + bool isRunning = false; + /** + * @brief Check if stream has error + * + * @return true if there is an error. + */ + bool error() const { return errorType != StreamError::noError; }; + + StreamError errorType{StreamError::noError}; + + std::string errorMsg() const { return errorMessages.at(errorType); } + + /** + * @brief Returns true if everything is OK with a certain stream and the + * stream is running. + * + * @return as described above. + */ + bool runningOK() const { return isRunning && !error(); } + }; + /** * @brief Create a Daq based on given device info and configuration * @@ -65,8 +96,8 @@ public: * required, the return value of the function should be nullptr. If no input * data is presented, the function is called with a nullptr as argument. */ - virtual void start(std::optional inCallback, - std::optional outCallback) = 0; + virtual void start(InDaqCallback inCallback, + OutDaqCallback outCallback) = 0; /** * @brief Stop the Daq device. Throws an exception if the device is not @@ -74,14 +105,8 @@ public: */ virtual void stop() = 0; - /** - * @brief Returns whether the data stream is running Y/N - * - * @return true if running - */ - virtual bool isRunning() const = 0; + virtual ~Daq() = 0; - virtual ~Daq() = default; /** * @brief Returns the number of enabled input channels * @@ -91,6 +116,7 @@ public: * @return number of enabled input channels */ us neninchannels(bool include_monitorchannel = true) const; + /** * @brief Returns the number of enabled output channels * @@ -98,12 +124,30 @@ public: */ us nenoutchannels() const; + /** + * @brief Create a vector of boolean values of the enabled input channels. + * + * @param include_monitor If set to true and a monitor channel is available, + * the first index in the array will correspond to the monitor channel, and + * whether it is enabled + * + * @return Boolean vector + */ + boolvec eninchannels(bool include_monitor = true) const; + /** + * @brief Create an array of booleans for each enabled output channel. + * + * @return Boolean vector + */ + boolvec enoutchannels() const; + /** * @brief Returns current sample rate * * @return Sample rate in [Hz] */ double samplerate() const; + /** * @brief Returns the input range for each channel. Means the minimum from * the absolute value of the minumum and maximum value that is allowed to @@ -114,6 +158,7 @@ public: * @return Maximum offset from 0 before clipping. */ double inputRangeForChannel(us ch) const; + /** * @brief Returns datatype (enum) corresponding to the datatype of the * samples. @@ -121,6 +166,7 @@ public: * @return That as stated aboce */ DataTypeDescriptor::DataType dataType() const; + /** * @brief More elaborate description of the datatypes. * @@ -151,6 +197,6 @@ public: * @return true if duplex */ bool duplexMode() const { - return (neninchannels(false) > 0 && nenoutchannels() > 0); + return (neninchannels() > 0 && nenoutchannels() > 0); } }; diff --git a/src/lasp/device/lasp_daqconfig.cpp b/src/lasp/device/lasp_daqconfig.cpp new file mode 100644 index 0000000..0807b78 --- /dev/null +++ b/src/lasp/device/lasp_daqconfig.cpp @@ -0,0 +1,137 @@ +#include "lasp_daqconfig.h" +#include "lasp_deviceinfo.h" +#include +#include + +#define MAX_DEV_COUNT_PER_API 20 + +using std::vector; + +vector DaqApi::getAvailableApis() { + + vector apis; + apis.resize(6); +#if LASP_HAS_ULDAQ == 1 + apis.at(uldaqapi.apicode) = uldaqapi; +#endif +#if LASP_HAS_RTAUDIO == 1 + apis.at(rtaudioAlsaApi.apicode) = rtaudioAlsaApi; + apis.at(rtaudioPulseaudioApi.apicode) = rtaudioPulseaudioApi; + apis.at(rtaudioWasapiApi.apicode) = rtaudioWasapiApi; + apis.at(rtaudioDsApi.apicode) = rtaudioDsApi; + apis.at(rtaudioAsioApi.apicode) = rtaudioAsioApi; +#endif + return apis; +} + +DaqConfiguration::DaqConfiguration(const DeviceInfo &device) { + + api = device.api; + device_name = device.device_name; + + inchannel_config.resize(device.ninchannels); + outchannel_config.resize(device.noutchannels); + us i = 0; + for(auto& inch: inchannel_config) { + inch.name = "Unnamed input channel " + std::to_string(i); + i++; + } + + i = 0; + for(auto& outch: outchannel_config) { + outch.name = "Unnamed output channel " + std::to_string(i); + i++; + } + + sampleRateIndex = device.prefSampleRateIndex; + dataTypeIndex = device.prefDataTypeIndex; + framesPerBlockIndex = device.prefFramesPerBlockIndex; + + monitorOutput = false; + + assert(match(device)); +} + +bool DaqConfiguration::match(const DeviceInfo &dev) const { + return (dev.device_name == device_name && dev.api == api); +} + +int DaqConfiguration::getHighestInChannel() const { + for (int i = inchannel_config.size() - 1; i > -1; i--) { + if (inchannel_config.at(i).enabled) + return i; + } + return -1; +} + +int DaqConfiguration::getHighestOutChannel() const { + for (us i = outchannel_config.size() - 1; i >= 0; i--) { + if (outchannel_config.at(i).enabled) + return i; + } + return -1; +} +int DaqConfiguration::getLowestInChannel() const { + for (us i = 0; i < inchannel_config.size(); i++) { + if (inchannel_config.at(i).enabled) + return i; + } + return -1; +} +int DaqConfiguration::getLowestOutChannel() const { + for (us i = 0; i < outchannel_config.size(); i++) { + if (outchannel_config.at(i).enabled) + return i; + } + return -1; +} + +#include "toml++/toml.h" +#include + +toml::table daqChannelToTOML(const DaqChannel& ch) { + toml::table tbl; + tbl.emplace("enabled", ch.enabled); + tbl.emplace("name", ch.name); + tbl.emplace("sensitivity", ch.sensitivity); + tbl.emplace("IEPEEnabled", ch.IEPEEnabled); + tbl.emplace("ACCouplingMode", ch.ACCouplingMode); + tbl.emplace("rangeIndex", ch.rangeIndex); + tbl.emplace("qty", static_cast(ch.qty)); + tbl.emplace("digitalHighpassCutOn", ch.digitalHighPassCutOn); + + return tbl; +} + +string DaqConfiguration::toTOML() const { + + toml::table apitbl{{"name", api.apiname}, + {"code", api.apicode}, + {"subcode", api.api_specific_subcode}}; + + toml::table tbl{{"daqapi", apitbl}}; + + tbl.emplace("device_name", device_name); + tbl.emplace("sampleRateIndex", sampleRateIndex); + tbl.emplace("dataTypeIndex", dataTypeIndex); + tbl.emplace("framesPerBlockIndex", framesPerBlockIndex); + tbl.emplace("monitorOutput", monitorOutput); + + toml::array inchannel_config_tbl; + for(const auto& ch: inchannel_config) { + inchannel_config_tbl.emplace_back(daqChannelToTOML(ch)); + } + tbl.emplace("inchannel_config", inchannel_config_tbl); + + toml::array outchannel_config_tbl; + for(const auto& ch: outchannel_config) { + outchannel_config_tbl.emplace_back(daqChannelToTOML(ch)); + } + tbl.emplace("outchannel_config", outchannel_config_tbl); + + std::stringstream str; + + str << tbl; + + return str.str(); +} diff --git a/lasp/device/lasp_daqconfig.h b/src/lasp/device/lasp_daqconfig.h similarity index 75% rename from lasp/device/lasp_daqconfig.h rename to src/lasp/device/lasp_daqconfig.h index 7b161cc..cd922e6 100644 --- a/lasp/device/lasp_daqconfig.h +++ b/src/lasp/device/lasp_daqconfig.h @@ -1,8 +1,8 @@ #pragma once -#include -#include #include "lasp_config.h" #include "lasp_types.h" +#include +#include using std::string; using std::to_string; @@ -122,16 +122,85 @@ const DaqApi rtaudioAsioApi("RtAudio Windows ASIO", 5, RtAudio::Api::WINDOWS_ASIO); #endif - class DeviceInfo; + + +/** + * @brief Stores channel configuration data for each channel. + */ +class DaqChannel { + +public: + /** + * @brief Possible physical quantities that are recorded. + */ + enum class Qty { + Number, + AcousticPressure, + Voltage, + UserDefined + }; + + /** + * @brief Whether the channel is enabled. + */ + bool enabled = false; + + string name = ""; + /** + * @brief The conversion between recorded physical unit and stored / + * outputed number, i.e. Number/Volt or Number/Pa. Converting stored numbers + * to physical qty, *divide* by the sensitivity. + */ + double sensitivity = -1; + /** + * @brief For input-only: enable IEPE constant current power supply for + * this channel. Only for hardware that is capable of doing so. + */ + bool IEPEEnabled = false; + /** + * @brief Whether to enable HW AC-coupling for this channel. + */ + bool ACCouplingMode = false; + /** + * @brief Index in possible ranges for input / output + */ + int rangeIndex = 0; + /** + * @brief The physical quantity that is inputed / outputed + */ + Qty qty = Qty::Number; + + /** + * @brief Whether to enable a digital high pass on the signal before + * passing the result in case of input, or outputing the result in case of + * output. + */ + double digitalHighPassCutOn = -1; + +}; + /** * @brief Configuration of a DAQ device */ class DaqConfiguration { public: /** - * @brief The API of the DAQ system this configuration applies to. + * @brief Export the class to TOML markup language. + * + * @return String with TOML exported data. */ + std::string toTOML() const; + + /** + * @brief Load in a DAQConfiguration from TOML. + * + * @param toml String containing TOML data + * + * @return DaqConfiguration object + */ + static DaqConfiguration fromTOML(const std::string &toml); + DaqApi api; /** * @brief The internal device name this DAQ configuration applies to. @@ -139,37 +208,30 @@ public: string device_name; /** - * @brief Enabled input channels + * @brief Channel configuration for input channels */ - boolvec eninchannels; + std::vector inchannel_config; + /** - * @brief Enabled 'normal' output channels, i.e. no internal loopbacks. + * @brief Channel configuration for output channels */ - boolvec enoutchannels; - - std::vector inchannel_sensitivities; - std::vector inchannel_names; - std::vector inchannel_metadata; - - std::vector outchannel_sensitivities; - std::vector outchannel_names; - std::vector outchannel_metadata; + std::vector outchannel_config; /** * @brief Index in list of sample rates that are available for the device. */ - us sampleRateIndex = 0; // + int sampleRateIndex = 0; // /** * @brief Required datatype for output, should be present in the list */ - us dataTypeIndex = 0; + int dataTypeIndex = 0; /** * @brief The index in the array of frames per block that can be used for the * device. */ - us framesPerBlockIndex = 0; + int framesPerBlockIndex = 0; /** * @brief If set to true and if the device has this capability, the output @@ -177,22 +239,6 @@ public: */ bool monitorOutput = false; - /** - * @brief If the device is capable, enable IEPE constant current power supply - * for given channel number. - */ - boolvec inputIEPEEnabled; - /** - * @brief If the device is capable, here we can define whether the channel - * should enable hardware AC-coupling. - */ - boolvec inputACCouplingMode; - - /** - * @brief Stores the index of the used input range for each of the channels. - */ - usvec inputRangeIndices; - /** * @brief Create a default configuration, with all channels disabled on both * input and output, and default channel names @@ -200,7 +246,8 @@ public: * @param deviceinfo DeviceInfo structure */ DaqConfiguration(const DeviceInfo &DeviceInfo); - DaqConfiguration(); + + DaqConfiguration() {} /** * @brief Check to see whether the DAQ configuration matches with the device. @@ -246,5 +293,6 @@ public: * enabled. */ int getLowestOutChannel() const; -}; + +}; diff --git a/lasp/device/lasp_daqdata.cpp b/src/lasp/device/lasp_daqdata.cpp similarity index 100% rename from lasp/device/lasp_daqdata.cpp rename to src/lasp/device/lasp_daqdata.cpp diff --git a/lasp/device/lasp_daqdata.h b/src/lasp/device/lasp_daqdata.h similarity index 53% rename from lasp/device/lasp_daqdata.h rename to src/lasp/device/lasp_daqdata.h index fd71441..7ee1567 100644 --- a/lasp/device/lasp_daqdata.h +++ b/src/lasp/device/lasp_daqdata.h @@ -1,14 +1,13 @@ #pragma once +#include "lasp_daqconfig.h" +#include "lasp_types.h" #include #include #include -#include -#include "lasp_daqconfig.h" -#include "lasp_types.h" /** * @brief Data coming from / going to DAQ. **Non-interleaved format**, which - * means data in buffer is ordered by channel: _ptr[sample+channel*nframes] + * means data in buffer is ordered by channel: _ptr[frame+channel*nframes] */ class DaqData { protected: @@ -28,7 +27,14 @@ public: */ const us nframes; + /** + * @brief The data type corresponding to a sample + */ const DataTypeDescriptor::DataType dtype; + + /** + * @brief The data type description corresponding to a sample + */ const DataTypeDescriptor &dtype_descr; /** @@ -36,16 +42,29 @@ public: */ const us sw; + /** + * @brief Initialize an empty frame of data + * + * @param nchannels The number of channels + * @param nframes The number of frames + * @param dtype The data type + */ DaqData(const us nchannels, const us nframes, const DataTypeDescriptor::DataType dtype); virtual ~DaqData() = default; /** - * @brief Return reference to internal vector + * @brief Return pointer to the raw data corresponding to a certain sample. * - * @return Reference to vector of data storage. + * @param frame The frame number + * @param channel The channel number + * + * @return Pointer to sample, not casted to final type */ - int8_t *raw_ptr() { return _data.data(); } + int8_t *raw_ptr(const us frame = 0, const us channel = 0) { + return &(_data.data()[sw * (frame + channel * nframes)]); + } + /** * @brief Return the total number of bytes * @@ -76,13 +95,29 @@ public: const DataTypeDescriptor::DataType dtype_descr) : DaqData(nchannels, nframes, dtype_descr) {} - T &operator[](const us i) { return _data[this->sw * i]; } + T &operator[](const us i) { return _data[sw * i]; } - T &operator()(const us sample, const us channel) { - return reinterpret_cast(_data[sw * (sample + channel * nframes)]); + /** + * @brief Reference of sample, casted to the right type. Same as raw_ptr(), + * but then as reference, and corresponding to right type. + * + * @param frame Frame number + * @param channel Channel number + * + * @return + */ + T &operator()(const us frame = 0, const us channel = 0) { + return reinterpret_cast(*raw_ptr(frame, channel)); } - void copyToRaw(const us channel, T *ptr) { - DaqData::copyToRaw(channel, reinterpret_cast(ptr)); + /** + * @brief Copy out the data for a certain channel to a buffer + * + * @param channel The channel to copy + * @param buffer The buffer to copy to. *Make sure* it is allocated with at + * least `sw*nframes` bytes. + */ + void copyToRaw(const us channel, T *buffer) { + DaqData::copyToRaw(channel, reinterpret_cast(buffer)); } }; diff --git a/lasp/device/lasp_device_common.py b/src/lasp/device/lasp_device_common.py similarity index 100% rename from lasp/device/lasp_device_common.py rename to src/lasp/device/lasp_device_common.py diff --git a/src/lasp/device/lasp_deviceinfo.cpp b/src/lasp/device/lasp_deviceinfo.cpp new file mode 100644 index 0000000..ea382bc --- /dev/null +++ b/src/lasp/device/lasp_deviceinfo.cpp @@ -0,0 +1,27 @@ +#include "lasp_deviceinfo.h" +#include +#include + +#define MAX_DEV_COUNT_PER_API 20 + +#if LASP_HAS_ULDAQ == 1 +#include "lasp_uldaq.h" +#endif +#if LASP_HAS_RTAUDIO == 1 +#include "lasp_rtaudiodaq.h" +#endif + + +std::vector DeviceInfo::getDeviceInfo() { + std::vector devs; +#if LASP_HAS_ULDAQ ==1 + fillUlDaqDeviceInfo(devs); +#endif + +#if LASP_HAS_RTAUDIO == 1 + fillRtAudioDeviceInfo(devs); +#endif + + return devs; +} + diff --git a/lasp/device/lasp_deviceinfo.h b/src/lasp/device/lasp_deviceinfo.h similarity index 88% rename from lasp/device/lasp_deviceinfo.h rename to src/lasp/device/lasp_deviceinfo.h index 3a7a07f..c52fb43 100644 --- a/lasp/device/lasp_deviceinfo.h +++ b/src/lasp/device/lasp_deviceinfo.h @@ -93,6 +93,11 @@ public: } } + /** + * @brief Whether the device has an internal monitor of the output signal. + */ + bool hasInternalOutputMonitor = false; + /** * @brief String representation of DeviceInfo * @@ -110,20 +115,6 @@ public: return str.str(); } - /** - * @brief For backwards-compatibility: serialize device info to string - * - * @return Serialize-string - */ - string serialize() const; - /** - * @brief Create a device info structure from serialized string - * - * @param dstr String to deserialize from - * - * @return resulting DeviceInfo - */ - static DeviceInfo deserialize(const string& dstr); /** * @brief Create a list of DeviceInfo's that are at call time avalable * diff --git a/lasp/device/lasp_rtaudiodaq.cpp b/src/lasp/device/lasp_rtaudiodaq.cpp similarity index 81% rename from lasp/device/lasp_rtaudiodaq.cpp rename to src/lasp/device/lasp_rtaudiodaq.cpp index c60fff3..b7c1d8a 100644 --- a/lasp/device/lasp_rtaudiodaq.cpp +++ b/src/lasp/device/lasp_rtaudiodaq.cpp @@ -1,11 +1,10 @@ #include "lasp_rtaudiodaq.h" #if LASP_HAS_RTAUDIO == 1 -#include "lasp_daqconfig.h" +#include "debugtrace.hpp" +#include "lasp_daq.h" #include #include #include -#include -#include using std::atomic; using std::cerr; @@ -13,6 +12,8 @@ using std::endl; using std::runtime_error; using std::vector; +DEBUGTRACE_VARIABLES; + void fillRtAudioDeviceInfo(vector &devinfolist) { vector apis; @@ -103,10 +104,10 @@ void fillRtAudioDeviceInfo(vector &devinfolist) { } } -int mycallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, +static int mycallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData); -void myerrorcallback(RtAudioError::Type, const string &errorText); +static void myerrorcallback(RtAudioError::Type, const string &errorText); class RtAudioDaq : public Daq { @@ -119,20 +120,25 @@ class RtAudioDaq : public Daq { InDaqCallback _incallback; OutDaqCallback _outcallback; + std::atomic _streamStatus{}; + public: RtAudioDaq(const DeviceInfo &devinfo, const DaqConfiguration &config) : Daq(devinfo, config), rtaudio(static_cast(devinfo.api.api_specific_subcode)), nFramesPerBlock(Daq::framesPerBlock()) { - std::unique_ptr inParams, outParams; + DEBUGTRACE_ENTER; + // We make sure not to run RtAudio in duplex mode. This seems to be buggy // and untested. Better to use a hardware-type loopback into the system. - if (neninchannels(false) > 0 && nenoutchannels() > 0) { + if (duplexMode()) { throw runtime_error("RtAudio backend cannot run in duplex mode."); } assert(!monitorOutput); + std::unique_ptr inParams, outParams; + if (neninchannels() > 0) { inParams = std::make_unique(); @@ -193,57 +199,91 @@ public: unsigned int nFramesPerBlock_copy = nFramesPerBlock; // Final step: open the stream. - rtaudio.openStream(&(*outParams), &(*inParams), format, + rtaudio.openStream(outParams.get(), inParams.get(), format, static_cast(samplerate()), &nFramesPerBlock_copy, - &mycallback, (void *)this, &streamoptions, + mycallback, (void *)this, &streamoptions, &myerrorcallback); } - virtual void start(std::optional inCallback, - std::optional outCallback) override { + virtual void start(InDaqCallback inCallback, + OutDaqCallback outCallback) override { + + DEBUGTRACE_ENTER; assert(!monitorOutput); - if (isRunning()) { + if (StreamStatus().runningOK()) { throw runtime_error("Stream already running"); } + // Logical XOR - if (!inCallback != !outCallback) { + if (inCallback && outCallback) { throw runtime_error("Either input or output stream possible for RtAudio. " "Stream duplex mode not provided."); } if (inCallback) { - _incallback = *inCallback; - if (!neninchannels()) { + _incallback = inCallback; + if (neninchannels()==0) { throw runtime_error( "Input callback given, but stream does not provide input data"); } } if (outCallback) { - _outcallback = *outCallback; - if (!nenoutchannels()) { + _outcallback = outCallback; + if (nenoutchannels()==0) { throw runtime_error( "Output callback given, but stream does not provide output data"); } } rtaudio.startStream(); + + // If we are here, we are running without errors. + StreamStatus status; + status.isRunning = true; + _streamStatus = status; } - bool isRunning() const override { return (rtaudio.isStreamRunning()); } + StreamStatus getStreamStatus() const override { return _streamStatus; } void stop() override { - if (!isRunning()) { - /* cerr << "Stream is already stopped" << endl; */ - } else { + DEBUGTRACE_ENTER; + if (getStreamStatus().runningOK()) { rtaudio.stopStream(); } } int streamCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, + RtAudioStreamStatus status) { + /* DEBUGTRACE_ENTER; */ + + using se = StreamStatus::StreamError; + + int rval = 0; + auto stopWithError = [&](se e) { + DEBUGTRACE_PRINT("stopWithError"); + StreamStatus stat = _streamStatus; + stat.errorType = e; + stat.isRunning = false; + rval = 1; + }; + + switch (status) { + case RTAUDIO_INPUT_OVERFLOW: + stopWithError(se::inputXRun); + return 1; + break; + case RTAUDIO_OUTPUT_UNDERFLOW: + stopWithError(se::outputXRun); + return 1; + break; + default: + break; + } + const auto &dtype_descr = DataTypeDescriptor(); const auto dtype = dataType(); us neninchannels = this->neninchannels(); @@ -252,6 +292,7 @@ public: if (nFrames != nFramesPerBlock) { cerr << "RtAudio backend error: nFrames does not match block size!" << endl; + stopWithError(se::logicError); return 1; } @@ -260,15 +301,18 @@ public: ptrs.reserve(neninchannels); /* DaqData(neninchannels_inc_mon, nFramesPerBlock, dtype); */ for (int ch = getLowestInChannel(); ch <= getHighestInChannel(); ch++) { - if (eninchannels[ch]) { + if (inchannel_config.at(ch).enabled) { ptrs.push_back(&static_cast( inputBuffer)[sw * ninchannels * ch * nFramesPerBlock]); } } DaqData d{neninchannels, nFramesPerBlock, dtype}; d.copyInFromRaw(ptrs); + + assert(_incallback); bool ret = _incallback(d); if (!ret) { + stopWithError(se::noError); return 1; } } @@ -280,14 +324,17 @@ public: /* outCallback */ for (int ch = 0; ch <= getHighestOutChannel(); ch++) { - if (enoutchannels[ch]) { + if (outchannel_config.at(ch).enabled) { ptrs.push_back(&static_cast( outputBuffer)[sw * nenoutchannels * ch * nFramesPerBlock]); } } DaqData d{nenoutchannels, nFramesPerBlock, dtype}; + + assert(_outcallback); bool ret = _outcallback(d); if (!ret) { + stopWithError(se::noError); return 1; } us j = 0; @@ -297,7 +344,7 @@ public: } } - return 0; + return rval; } // RtAudio documentation says: if a stream is open, it will be stopped and @@ -312,7 +359,7 @@ std::unique_ptr createRtAudioDevice(const DeviceInfo &devinfo, } void myerrorcallback(RtAudioError::Type, const string &errorText) { - cerr << errorText << endl; + cerr << "RtAudio backend stream error: " << errorText << endl; } int mycallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData) { diff --git a/lasp/device/lasp_rtaudiodaq.h b/src/lasp/device/lasp_rtaudiodaq.h similarity index 100% rename from lasp/device/lasp_rtaudiodaq.h rename to src/lasp/device/lasp_rtaudiodaq.h diff --git a/lasp/device/lasp_streammgr.cpp b/src/lasp/device/lasp_streammgr.cpp similarity index 53% rename from lasp/device/lasp_streammgr.cpp rename to src/lasp/device/lasp_streammgr.cpp index 656a3b7..cd3d7ab 100644 --- a/lasp/device/lasp_streammgr.cpp +++ b/src/lasp/device/lasp_streammgr.cpp @@ -1,17 +1,19 @@ -#include -#include #include "lasp_streammgr.h" +#include +#include +#include +#include "debugtrace.hpp" InDataHandler::InDataHandler(StreamMgr &mgr) : _mgr(mgr) { mgr.addInDataHandler(*this); } InDataHandler::~InDataHandler() { _mgr.removeInDataHandler(*this); } - StreamMgr &StreamMgr::getInstance() { static StreamMgr mgr; return mgr; } +StreamMgr::StreamMgr() {} bool StreamMgr::inCallback(const DaqData &data) { std::scoped_lock lck(_inDataHandler_mtx); @@ -37,11 +39,11 @@ bool StreamMgr::inCallback(const DaqData &data) { template bool fillData(DaqData &data, const vd &signal) { assert(data.nframes == signal.size()); - T* res = reinterpret_cast(data.raw_ptr()); + T *res = reinterpret_cast(data.raw_ptr()); if (std::is_floating_point()) { for (us ch = 0; ch < data.nchannels; ch++) { for (us frame = 0; frame < data.nframes; frame++) { - res[ch * data.nframes + frame ] = signal[frame]; + res[ch * data.nframes + frame] = signal[frame]; } } } else { @@ -60,20 +62,23 @@ void StreamMgr::setSiggen(std::shared_ptr siggen) { std::scoped_lock lck(_siggen_mtx); _siggen = siggen; + // If not set to nullptr, and a stream is running, we update the signal // generator by resetting it. - if(isStreamRunning(StreamType::outputType) && siggen) { - const Daq* daq = getDaq(StreamType::outputType); + if (isStreamRunningOK(StreamType::output) && siggen) { + const Daq *daq = getDaq(StreamType::output); assert(daq != nullptr); // Reset the signal generator. _siggen->reset(daq->samplerate()); } - } -bool StreamMgr::outCallback(DaqData& data) { +bool StreamMgr::outCallback(DaqData &data) { + + /* DEBUGTRACE_ENTER; */ + std::scoped_lock lck(_siggen_mtx); - if(_siggen) { + if (_siggen) { vd signal = _siggen->genSignal(data.nframes); switch (data.dtype) { case (DataTypeDescriptor::DataType::dtype_fl32): @@ -94,51 +99,72 @@ bool StreamMgr::outCallback(DaqData& data) { } } else { // Set all values to 0. - std::fill(data.raw_ptr(), data.raw_ptr()+data.size_bytes(), 0); + std::fill(data.raw_ptr(), data.raw_ptr() + data.size_bytes(), 0); } return true; } StreamMgr::~StreamMgr() { stopAllStreams(); } -void StreamMgr::stopAllStreams() { _streams.clear(); } +void StreamMgr::stopAllStreams() { + _inputStream.reset(); + _outputStream.reset(); +} void StreamMgr::startStream(const DeviceInfo &devinfo, const DaqConfiguration &config) { - bool isInput = std::count(config.eninchannels.begin(), - config.eninchannels.end(), true) > 0; - bool isOutput = std::count(config.enoutchannels.begin(), - config.enoutchannels.end(), true) > 0; + bool isInput = std::count_if(config.inchannel_config.cbegin(), + config.inchannel_config.cend(), + [](auto &i) { return i.enabled; }); + isInput |= config.monitorOutput && devinfo.hasInternalOutputMonitor; + + bool isOutput = std::count_if(config.outchannel_config.cbegin(), + config.outchannel_config.cend(), + [](auto &i) { return i.enabled; }); + bool isDuplex = isInput && isOutput; if (!isInput && !isOutput) { throw std::runtime_error( - "Neither input, nor output channels enabled for stream"); + "Neither input, nor output channels enabled for stream. Cannotr start."); } - StreamType streamtype; - if (isDuplex) { - if (_streams.size()) { - throw std::runtime_error("Error: a stream is already running. Please " - "first stop existing stream"); - } - streamtype = StreamType::duplex; - - } else if (isInput) { - if (_streams[StreamType::input]) { - throw std::runtime_error("Error: input stream is already running. Please " - "first stop existing stream"); - } - streamtype = StreamType::input; - } else if (isOutput) { - if (_streams[StreamType::output]) { - throw std::runtime_error( - "Error: output stream is already running. Please " - "first stop existing stream"); - } - streamtype = StreamType::input; + if ((isDuplex || isInput) && _inputStream) { + throw std::runtime_error("Error: an input stream is already running. Please " + "first stop existing stream"); + } else if (isOutput && _outputStream) { + throw std::runtime_error("Error: output stream is already running. Please " + "first stop existing stream"); } - _streams[streamtype] = Daq::createDaq(devinfo, config); + + InDaqCallback inCallback; + OutDaqCallback outCallback; + + using namespace std::placeholders; + std::unique_ptr daq = Daq::createDaq(devinfo, config); + + if(isInput) { + inCallback = std::bind(&StreamMgr::inCallback, this, _1); + } + + if(isOutput) { + if(_siggen) { + _siggen->reset(daq->samplerate()); + } + outCallback = std::bind(&StreamMgr::outCallback, this, _1); + } + DEBUGTRACE_PRINT(isInput); + DEBUGTRACE_PRINT(isOutput); + + daq->start(inCallback, outCallback); + + if(isInput) { + _inputStream = std::move(daq); + } else { + assert(isOutput); + _outputStream = std::move(daq); + } + } void StreamMgr::addInDataHandler(InDataHandler &handler) { @@ -149,3 +175,28 @@ void StreamMgr::removeInDataHandler(InDataHandler &handler) { std::scoped_lock lck(_inDataHandler_mtx); _inDataHandlers.remove(&handler); } + +Daq::StreamStatus StreamMgr::getStreamStatus(const StreamType type) const { + + // Default constructor, says stream is not running, but also no errors + Daq::StreamStatus s; + + const Daq *daq = getDaq(type); + if (daq) { + s = daq->getStreamStatus(); + } + return s; +} + +const Daq *StreamMgr::getDaq(StreamType type) const { + if (type == StreamType::input) { + return _inputStream.get(); + } else { + // Output stream. If input runs in duplex mode, this is also the output + // stream. In that case, we return the outputstram + if (_inputStream && _inputStream->duplexMode()) { + return _inputStream.get(); + } + return _outputStream.get(); + } +} diff --git a/lasp/device/lasp_streammgr.h b/src/lasp/device/lasp_streammgr.h similarity index 81% rename from lasp/device/lasp_streammgr.h rename to src/lasp/device/lasp_streammgr.h index b6eec04..450f046 100644 --- a/lasp/device/lasp_streammgr.h +++ b/src/lasp/device/lasp_streammgr.h @@ -7,24 +7,6 @@ class StreamMgr; -/** - * @brief Information regarding a stream. - */ -class StreamStatus { -public: - bool isRunning = false; - bool error = false; - std::string error_msg{}; - - /** - * @brief Returns true if everything is OK with a certain stream and the - * stream is running. - * - * @return as described above. - */ - bool runningOK() const { return isRunning && !error; } -}; - class InDataHandler { protected: @@ -60,31 +42,19 @@ class StreamMgr { enum class StreamType : us { /** - * @brief Input-only stream + * @brief Input stream */ input = 1 << 0, /** - * @brief Output-only stream + * @brief Output stream */ output = 1 << 1, - /** - * @brief Duplex stream (does both input and output) - */ - duplex = 1 << 2, - /** - * @brief Either input, or duplex - */ - inputType = 1 << 3, - /** - * @brief Either output, or duplex - */ - outputType = 1 << 4 }; /** - * @brief Storage for streams + * @brief Storage for streams. */ - std::map> _streams; + std::unique_ptr _inputStream, _outputStream; /** * @brief All indata handlers are called when input data is available. Note @@ -106,10 +76,12 @@ class StreamMgr { friend class InDataHandler; friend class Siggen; + // Singleton, no public destructor + ~StreamMgr(); + public: StreamMgr(const StreamMgr &) = delete; StreamMgr &operator=(const StreamMgr &) = delete; - ~StreamMgr(); /** * @brief Get access to stream manager instance @@ -144,7 +116,7 @@ public: * * @return status object. */ - StreamStatus getStreamStatus(const StreamType type) const; + Daq::StreamStatus getStreamStatus(const StreamType type) const; /** * @brief Get DAQ pointer for a given stream. Gives a nullptr if stream is @@ -169,19 +141,29 @@ public: */ void stopAllStreams(); -private: - bool inCallback(const DaqData &data); - bool outCallback(DaqData &data); - - void addInDataHandler(InDataHandler &handler); - void removeInDataHandler(InDataHandler &handler); - /** * @brief Set active signal generator for output streams. Only one `Siggen' * is active at the same time. Siggen controls its own data race protection * using a mutex. * - * @param s Siggen pointer + * @param s New Siggen pointer */ void setSiggen(std::shared_ptr s); + +private: + bool inCallback(const DaqData &data); + bool outCallback(DaqData &data); + + void removeInDataHandler(InDataHandler &handler); + + /** + * @brief Add an input data handler. The handler's inCallback() function is + * called with data when available. This function should *NOT* be called by + * the user, instead, an InDataHandler can only be created with the StreamMgr + * as argument. + * + * @param handler The handler to add. + */ + void addInDataHandler(InDataHandler &handler); + }; diff --git a/lasp/device/lasp_uldaq.cpp b/src/lasp/device/lasp_uldaq.cpp similarity index 85% rename from lasp/device/lasp_uldaq.cpp rename to src/lasp/device/lasp_uldaq.cpp index 87c127f..a335ffd 100644 --- a/lasp/device/lasp_uldaq.cpp +++ b/src/lasp/device/lasp_uldaq.cpp @@ -1,4 +1,5 @@ #include "lasp_uldaq.h" +#if LASP_HAS_ULDAQ == 1 #include "lasp_daqconfig.h" #include #include @@ -55,11 +56,12 @@ class DT9837A : public Daq { std::thread _thread; atomic _stopThread{false}; + atomic _streamStatus; const us _nFramesPerBlock; - void threadFcn(std::optional inCallback, - std::optional outcallback); + void threadFcn(InDaqCallback inCallback, + OutDaqCallback outcallback); public: DT9837A(const DeviceInfo &devinfo, const DaqConfiguration &config); @@ -78,12 +80,17 @@ public: } } - bool isRunning() const override final { return _thread.joinable(); } - virtual void start(std::optional inCallback, - std::optional outCallback) override final; + bool isRunning() const { return _thread.joinable(); } + virtual void start(InDaqCallback inCallback, + OutDaqCallback outCallback) override final; + + virtual StreamStatus getStreamStatus() const override { + return _streamStatus; + } void stop() override final { DEBUGTRACE_ENTER; + StreamStatus status = _streamStatus; if (!isRunning()) { throw runtime_error("No data acquisition running"); } @@ -93,13 +100,16 @@ public: _thread.join(); } _stopThread = false; + status.isRunning = false; + _streamStatus = status; } + friend class InBufHandler; friend class OutBufHandler; }; -void DT9837A::start(std::optional inCallback, - std::optional outCallback) { +void DT9837A::start(InDaqCallback inCallback, + OutDaqCallback outCallback) { DEBUGTRACE_ENTER; if (isRunning()) { throw runtime_error("DAQ is already running"); @@ -163,7 +173,7 @@ public: std::vector indescs; - boolvec eninchannels = daq.eninchannels; + boolvec eninchannels = daq.eninchannels(); // Initialize input, if any for (us chin = 0; chin < 4; chin++) { @@ -360,37 +370,56 @@ public: } }; -void DT9837A::threadFcn(std::optional inCallback, - std::optional outCallback) { +void DT9837A::threadFcn(InDaqCallback inCallback, + OutDaqCallback outCallback) { DEBUGTRACE_ENTER; - std::unique_ptr ibh; - std::unique_ptr obh; - if (neninchannels() > 0) { - ibh = std::make_unique(*this, inCallback.value()); - } - if (nenoutchannels() > 0) { - obh = std::make_unique(*this, outCallback.value()); - } + cerr << "******************\n" + "Todo: the current way of handling timing in this DAQ thread is not " + "really robust, due " + "to input / output callbacks that can be too time-consuming. We have " + "to fix the " + "sleep_for to properly deal with longer callbacks." + "\n*****************" + << endl; + try { + std::unique_ptr ibh; + std::unique_ptr obh; - const double sleeptime = - static_cast(_nFramesPerBlock) / (16 * samplerate()); - const us sleeptime_us = static_cast(sleeptime * 1e6); - - while (!_stopThread) { - if (ibh) { - if (!(*ibh)()) { - _stopThread = true; - } + if (neninchannels() > 0) { + assert(inCallback); + ibh = std::make_unique(*this, inCallback); } - if (obh) { - if (!(*obh)()) { - _stopThread = true; - } + if (nenoutchannels() > 0) { + assert(outCallback); + obh = std::make_unique(*this, outCallback); } - std::this_thread::sleep_for(std::chrono::microseconds(sleeptime_us)); + + const double sleeptime = + static_cast(_nFramesPerBlock) / (16 * samplerate()); + const us sleeptime_us = static_cast(sleeptime * 1e6); + + while (!_stopThread) { + if (ibh) { + if (!(*ibh)()) { + _stopThread = true; + } + } + if (obh) { + if (!(*obh)()) { + _stopThread = true; + } + } + std::this_thread::sleep_for(std::chrono::microseconds(sleeptime_us)); + } + } catch (std::runtime_error &e) { } + StreamStatus status = _streamStatus; + ; + status.isRunning = false; + _streamStatus = status; + _stopThread = false; } std::unique_ptr createUlDaqDevice(const DeviceInfo &devinfo, @@ -403,11 +432,11 @@ DT9837A::DT9837A(const DeviceInfo &devinfo, const DaqConfiguration &config) _nFramesPerBlock(availableFramesPerBlock.at(framesPerBlockIndex)) { // Some sanity checks - if (eninchannels.size() != 4) { + if (inchannel_config.size() != 4) { throw runtime_error("Invalid length of enabled inChannels vector"); } - if (enoutchannels.size() != 1) { + if (outchannel_config.size() != 1) { throw runtime_error("Invalid length of enabled outChannels vector"); } @@ -465,14 +494,14 @@ DT9837A::DT9837A(const DeviceInfo &devinfo, const DaqConfiguration &config) throw runtime_error("Fatal: could normalize channel sensitivity"); } - CouplingMode cm = inputACCouplingMode[ch] ? CM_AC : CM_DC; + CouplingMode cm = inchannel_config.at(ch).ACCouplingMode ? CM_AC : CM_DC; err = ulAISetConfig(_handle, AI_CFG_CHAN_COUPLING_MODE, ch, cm); if (err != ERR_NO_ERROR) { showErr(err); throw runtime_error("Fatal: could not set AC/DC coupling mode"); } - IepeMode iepe = inputIEPEEnabled[ch] ? IEPE_ENABLED : IEPE_DISABLED; + IepeMode iepe = inchannel_config.at(ch).IEPEEnabled ? IEPE_ENABLED : IEPE_DISABLED; err = ulAISetConfig(_handle, AI_CFG_CHAN_IEPE_MODE, ch, iepe); if (err != ERR_NO_ERROR) { showErr(err); @@ -556,3 +585,4 @@ void fillUlDaqDeviceInfo(std::vector &devinfolist) { devinfolist.push_back(devinfo); } } +#endif diff --git a/lasp/device/lasp_uldaq.h b/src/lasp/device/lasp_uldaq.h similarity index 100% rename from lasp/device/lasp_uldaq.h rename to src/lasp/device/lasp_uldaq.h diff --git a/src/lasp/dsp/CMakeLists.txt b/src/lasp/dsp/CMakeLists.txt new file mode 100644 index 0000000..b5d679c --- /dev/null +++ b/src/lasp/dsp/CMakeLists.txt @@ -0,0 +1,16 @@ +# src/lasp/dsp/CMakeLists.txt + +set(lasp_dsp_files + lasp_filter.cpp + lasp_siggen.cpp + lasp_siggen_impl.cpp + lasp_window.cpp + ) + + +add_library(lasp_dsp_lib STATIC ${lasp_dsp_files}) +target_compile_definitions(lasp_dsp_lib PUBLIC "-DARMA_DONT_USE_WRAPPER") + +target_link_libraries(lasp_dsp_lib PRIVATE ${BLAS_LIBRARIES}) +target_include_directories(lasp_dsp_lib INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + diff --git a/lasp/dsp/lasp_filter.cpp b/src/lasp/dsp/lasp_filter.cpp similarity index 100% rename from lasp/dsp/lasp_filter.cpp rename to src/lasp/dsp/lasp_filter.cpp diff --git a/lasp/dsp/lasp_filter.h b/src/lasp/dsp/lasp_filter.h similarity index 100% rename from lasp/dsp/lasp_filter.h rename to src/lasp/dsp/lasp_filter.h diff --git a/lasp/dsp/lasp_mathtypes.h b/src/lasp/dsp/lasp_mathtypes.h similarity index 100% rename from lasp/dsp/lasp_mathtypes.h rename to src/lasp/dsp/lasp_mathtypes.h diff --git a/lasp/dsp/lasp_siggen.cpp b/src/lasp/dsp/lasp_siggen.cpp similarity index 100% rename from lasp/dsp/lasp_siggen.cpp rename to src/lasp/dsp/lasp_siggen.cpp diff --git a/lasp/dsp/lasp_siggen.h b/src/lasp/dsp/lasp_siggen.h similarity index 100% rename from lasp/dsp/lasp_siggen.h rename to src/lasp/dsp/lasp_siggen.h diff --git a/lasp/dsp/lasp_siggen_impl.cpp b/src/lasp/dsp/lasp_siggen_impl.cpp similarity index 99% rename from lasp/dsp/lasp_siggen_impl.cpp rename to src/lasp/dsp/lasp_siggen_impl.cpp index c76337d..95b0962 100644 --- a/lasp/dsp/lasp_siggen_impl.cpp +++ b/src/lasp/dsp/lasp_siggen_impl.cpp @@ -168,7 +168,7 @@ void Sweep::resetImpl() { } DEBUGTRACE_PRINT(K); - DEBUGTRACE_PRINT(K1); + DEBUGTRACE_PRINT(k1); DEBUGTRACE_PRINT(k); DEBUGTRACE_PRINT(E); @@ -215,7 +215,7 @@ void Sweep::resetImpl() { } DEBUGTRACE_PRINT(K); - DEBUGTRACE_PRINT(K1); + DEBUGTRACE_PRINT(k1); DEBUGTRACE_PRINT(k); DEBUGTRACE_PRINT(E); diff --git a/lasp/dsp/lasp_siggen_impl.h b/src/lasp/dsp/lasp_siggen_impl.h similarity index 97% rename from lasp/dsp/lasp_siggen_impl.h rename to src/lasp/dsp/lasp_siggen_impl.h index 9bc38ff..8b23478 100644 --- a/lasp/dsp/lasp_siggen_impl.h +++ b/src/lasp/dsp/lasp_siggen_impl.h @@ -35,6 +35,7 @@ class Sine : public Siggen { ~Sine() = default; virtual vd genSignalUnscaled(const us nframes) override; void setFreq(const d newFreq); + void resetImpl() override { phase=0; } }; class Periodic: public Siggen { protected: diff --git a/lasp/dsp/lasp_types.h b/src/lasp/dsp/lasp_types.h similarity index 100% rename from lasp/dsp/lasp_types.h rename to src/lasp/dsp/lasp_types.h diff --git a/lasp/dsp/lasp_window.cpp b/src/lasp/dsp/lasp_window.cpp similarity index 100% rename from lasp/dsp/lasp_window.cpp rename to src/lasp/dsp/lasp_window.cpp diff --git a/lasp/dsp/lasp_window.h b/src/lasp/dsp/lasp_window.h similarity index 100% rename from lasp/dsp/lasp_window.h rename to src/lasp/dsp/lasp_window.h diff --git a/lasp/filter/__init__.py b/src/lasp/filter/__init__.py similarity index 100% rename from lasp/filter/__init__.py rename to src/lasp/filter/__init__.py diff --git a/lasp/filter/biquad.py b/src/lasp/filter/biquad.py similarity index 100% rename from lasp/filter/biquad.py rename to src/lasp/filter/biquad.py diff --git a/lasp/filter/colorednoise.py b/src/lasp/filter/colorednoise.py similarity index 100% rename from lasp/filter/colorednoise.py rename to src/lasp/filter/colorednoise.py diff --git a/lasp/filter/decimation_fir.py b/src/lasp/filter/decimation_fir.py similarity index 100% rename from lasp/filter/decimation_fir.py rename to src/lasp/filter/decimation_fir.py diff --git a/lasp/filter/filterbank_design.py b/src/lasp/filter/filterbank_design.py similarity index 100% rename from lasp/filter/filterbank_design.py rename to src/lasp/filter/filterbank_design.py diff --git a/lasp/filter/fir_design.py b/src/lasp/filter/fir_design.py similarity index 100% rename from lasp/filter/fir_design.py rename to src/lasp/filter/fir_design.py diff --git a/lasp/filter/soundpressureweighting.py b/src/lasp/filter/soundpressureweighting.py similarity index 100% rename from lasp/filter/soundpressureweighting.py rename to src/lasp/filter/soundpressureweighting.py diff --git a/lasp/lasp_atomic.py b/src/lasp/lasp_atomic.py similarity index 100% rename from lasp/lasp_atomic.py rename to src/lasp/lasp_atomic.py diff --git a/lasp/lasp_avstream.py b/src/lasp/lasp_avstream.py similarity index 100% rename from lasp/lasp_avstream.py rename to src/lasp/lasp_avstream.py diff --git a/lasp/lasp_common.py b/src/lasp/lasp_common.py similarity index 99% rename from lasp/lasp_common.py rename to src/lasp/lasp_common.py index 38c300a..6267f08 100644 --- a/lasp/lasp_common.py +++ b/src/lasp/lasp_common.py @@ -6,7 +6,7 @@ from collections import namedtuple from dataclasses import dataclass from dataclasses_json import dataclass_json from enum import Enum, unique, auto -from .dsp import Window as wWindow +from .lasp_cpp import Window as wWindow """ Common definitions used throughout the code. diff --git a/lasp/dsp/lasp_config.h.in b/src/lasp/lasp_config.h.in similarity index 100% rename from lasp/dsp/lasp_config.h.in rename to src/lasp/lasp_config.h.in diff --git a/lasp/lasp_config.py b/src/lasp/lasp_config.py similarity index 100% rename from lasp/lasp_config.py rename to src/lasp/lasp_config.py diff --git a/src/lasp/lasp_cpp.cpp b/src/lasp/lasp_cpp.cpp new file mode 100644 index 0000000..25d7609 --- /dev/null +++ b/src/lasp/lasp_cpp.cpp @@ -0,0 +1,19 @@ +#include +namespace py = pybind11; + +void init_dsp(py::module& m); +void init_deviceinfo(py::module& m); +void init_daqconfiguration(py::module& m); +void init_daq(py::module& m); +void init_streammgr(py::module& m); + + +PYBIND11_MODULE(lasp_cpp, m) { + + init_dsp(m); + init_deviceinfo(m); + init_daqconfiguration(m); + init_daq(m); + init_streammgr(m); + +} diff --git a/lasp/lasp_imptube.py b/src/lasp/lasp_imptube.py similarity index 100% rename from lasp/lasp_imptube.py rename to src/lasp/lasp_imptube.py diff --git a/lasp/lasp_logging.py b/src/lasp/lasp_logging.py similarity index 100% rename from lasp/lasp_logging.py rename to src/lasp/lasp_logging.py diff --git a/lasp/lasp_measurement.py b/src/lasp/lasp_measurement.py similarity index 100% rename from lasp/lasp_measurement.py rename to src/lasp/lasp_measurement.py diff --git a/lasp/lasp_multiprocessingpatch.py b/src/lasp/lasp_multiprocessingpatch.py similarity index 100% rename from lasp/lasp_multiprocessingpatch.py rename to src/lasp/lasp_multiprocessingpatch.py diff --git a/lasp/lasp_octavefilter.py b/src/lasp/lasp_octavefilter.py similarity index 100% rename from lasp/lasp_octavefilter.py rename to src/lasp/lasp_octavefilter.py diff --git a/lasp/lasp_playback.py b/src/lasp/lasp_playback.py similarity index 100% rename from lasp/lasp_playback.py rename to src/lasp/lasp_playback.py diff --git a/lasp/lasp_record.py b/src/lasp/lasp_record.py similarity index 100% rename from lasp/lasp_record.py rename to src/lasp/lasp_record.py diff --git a/lasp/lasp_reverb.py b/src/lasp/lasp_reverb.py similarity index 100% rename from lasp/lasp_reverb.py rename to src/lasp/lasp_reverb.py diff --git a/lasp/lasp_siggen.py b/src/lasp/lasp_siggen.py similarity index 100% rename from lasp/lasp_siggen.py rename to src/lasp/lasp_siggen.py diff --git a/lasp/lasp_slm.py b/src/lasp/lasp_slm.py similarity index 100% rename from lasp/lasp_slm.py rename to src/lasp/lasp_slm.py diff --git a/lasp/lasp_weighcal.py b/src/lasp/lasp_weighcal.py similarity index 100% rename from lasp/lasp_weighcal.py rename to src/lasp/lasp_weighcal.py diff --git a/lasp/plot/__init__.py b/src/lasp/plot/__init__.py similarity index 100% rename from lasp/plot/__init__.py rename to src/lasp/plot/__init__.py diff --git a/lasp/plot/bar.py b/src/lasp/plot/bar.py similarity index 100% rename from lasp/plot/bar.py rename to src/lasp/plot/bar.py diff --git a/src/lasp/pybind11/lasp_daq.cpp b/src/lasp/pybind11/lasp_daq.cpp new file mode 100644 index 0000000..b213688 --- /dev/null +++ b/src/lasp/pybind11/lasp_daq.cpp @@ -0,0 +1,37 @@ +#include "lasp_daq.h" +#include +#include +#include + +using std::cerr; +namespace py = pybind11; + +void init_daq(py::module &m) { + + /// Daq + py::class_ daq(m, "Daq"); + py::class_ ss(m, "StreamStatus"); + ss.def("error", &Daq::StreamStatus::error); + ss.def("runningOK", &Daq::StreamStatus::runningOK); + ss.def_readonly("isRunning", &Daq::StreamStatus::isRunning); + + py::enum_(ss, "StreamError") + .value("noError", Daq::StreamStatus::StreamError::noError) + .value("inputXRun", Daq::StreamStatus::StreamError::inputXRun) + .value("outputXRun", Daq::StreamStatus::StreamError::outputXRun) + .value("driverError", Daq::StreamStatus::StreamError::driverError) + .value("systemError", Daq::StreamStatus::StreamError::systemError) + .value("threadError", Daq::StreamStatus::StreamError::threadError) + .value("logicError", Daq::StreamStatus::StreamError::logicError) + .value("apiSpecificError", + Daq::StreamStatus::StreamError::apiSpecificError) + .export_values(); + + daq.def("neninchannels", &Daq::neninchannels); + daq.def("nenoutchannels", &Daq::nenoutchannels); + daq.def("samplerate", &Daq::samplerate); + daq.def("dataType", &Daq::dataType); + daq.def("framesPerBlock", &Daq::framesPerBlock); + daq.def("getStreamStatus", &Daq::getStreamStatus); + +} diff --git a/src/lasp/pybind11/lasp_daqconfiguration.cpp b/src/lasp/pybind11/lasp_daqconfiguration.cpp new file mode 100644 index 0000000..a4f8edc --- /dev/null +++ b/src/lasp/pybind11/lasp_daqconfiguration.cpp @@ -0,0 +1,77 @@ +#include "lasp_daqconfig.h" +#include "lasp_deviceinfo.h" +#include +#include +#include +#include + +using std::cerr; +namespace py = pybind11; + +void init_daqconfiguration(py::module &m) { + + /// DataType + py::class_ dtype_desc(m, "DataTypeDescriptor"); + dtype_desc.def_readonly("name", &DataTypeDescriptor::name); + dtype_desc.def_readonly("sw", &DataTypeDescriptor::sw); + dtype_desc.def_readonly("is_floating", &DataTypeDescriptor::is_floating); + + py::enum_(dtype_desc, "DataType") + .value("dtype_fl32", DataTypeDescriptor::DataType::dtype_fl32) + .value("dtype_fl64", DataTypeDescriptor::DataType::dtype_fl64) + .value("dtype_int8", DataTypeDescriptor::DataType::dtype_int8) + .value("dtype_int16", DataTypeDescriptor::DataType::dtype_int16) + .value("dtype_int32", DataTypeDescriptor::DataType::dtype_int32) + .export_values(); + + dtype_desc.def_readonly("dtype", &DataTypeDescriptor::dtype); + + /// DaqApi + py::class_ daqapi(m, "DaqApi"); + daqapi.def_readonly("apiname", &DaqApi::apiname); + daqapi.def_readonly("apicode", &DaqApi::apicode); + daqapi.def_readonly("api_specific_subcode", &DaqApi::api_specific_subcode); + daqapi.def("__str__", [](const DaqApi &d) { return std::string(d); }); + + /// DaqChannel, DaqConfiguration + py::class_ daqconfig(m, "DaqConfiguration"); + + py::enum_(daqconfig, "Qty") + .value("Number", DaqChannel::Qty::Number) + .value("AcousticPressure", DaqChannel::Qty::AcousticPressure) + .value("Voltage", DaqChannel::Qty::Voltage) + .value("UserDefined", DaqChannel::Qty::UserDefined) + .export_values(); + + dtype_desc.def_readonly("dtype", &DataTypeDescriptor::dtype); + py::class_ daqchannel(m, "DaqChannel"); + daqchannel.def_readwrite("enabled", &DaqChannel::enabled); + daqchannel.def_readwrite("name", &DaqChannel::name); + daqchannel.def_readwrite("sensitivity", &DaqChannel::sensitivity); + daqchannel.def_readwrite("ACCouplingMode", &DaqChannel::ACCouplingMode); + daqchannel.def_readwrite("IEPEEnabled", &DaqChannel::IEPEEnabled); + daqchannel.def_readwrite("digitalHighpassCutOn", + &DaqChannel::digitalHighPassCutOn); + daqchannel.def_readwrite("rangeIndex", &DaqChannel::rangeIndex); + daqchannel.def_readwrite("qty", &DaqChannel::qty); + + /// DaqConfiguration + daqconfig.def(py::init<>()); + daqconfig.def(py::init()); + + daqconfig.def_readwrite("sampleRateIndex", + &DaqConfiguration::sampleRateIndex); + daqconfig.def_readwrite("dataTypeIndex", &DaqConfiguration::dataTypeIndex); + + daqconfig.def_readwrite("framesPerBlockIndex", + &DaqConfiguration::framesPerBlockIndex); + daqconfig.def_readwrite("monitorOutput", &DaqConfiguration::monitorOutput); + + daqconfig.def("match", &DaqConfiguration::match); + + daqconfig.def("toTOML", &DaqConfiguration::toTOML); + daqconfig.def_readwrite("inchannel_config", + &DaqConfiguration::inchannel_config); + daqconfig.def_readwrite("outchannel_config", + &DaqConfiguration::outchannel_config); +} diff --git a/src/lasp/pybind11/lasp_deviceinfo.cpp b/src/lasp/pybind11/lasp_deviceinfo.cpp new file mode 100644 index 0000000..65dfc5b --- /dev/null +++ b/src/lasp/pybind11/lasp_deviceinfo.cpp @@ -0,0 +1,41 @@ +#include +#include +#include +#include "lasp_deviceinfo.h" + +using std::cerr; +namespace py = pybind11; + +void init_deviceinfo(py::module& m) { + + /// DeviceInfo + py::class_ devinfo(m, "DeviceInfo"); + devinfo.def_readonly("api", &DeviceInfo::api); + devinfo.def_readonly("device_name", &DeviceInfo::device_name); + + devinfo.def_readonly("availableDataTypes", &DeviceInfo::availableDataTypes); + devinfo.def_readonly("prefDataTypeIndex", &DeviceInfo::prefDataTypeIndex); + + devinfo.def_readonly("availableSampleRates", + &DeviceInfo::availableSampleRates); + devinfo.def_readonly("prefSampleRateIndex", &DeviceInfo::prefSampleRateIndex); + + devinfo.def_readonly("availableFramesPerBlock", + &DeviceInfo::availableFramesPerBlock); + devinfo.def_readonly("prefFramesPerBlockIndex", + &DeviceInfo::prefFramesPerBlockIndex); + + devinfo.def_readonly("availableInputRanges", + &DeviceInfo::availableInputRanges); + devinfo.def_readonly("prefInputRangeIndex", &DeviceInfo::prefInputRangeIndex); + + devinfo.def_readonly("ninchannels", &DeviceInfo::ninchannels); + devinfo.def_readonly("noutchannels", &DeviceInfo::noutchannels); + devinfo.def_readonly("hasInputIEPE", &DeviceInfo::hasInputIEPE); + devinfo.def_readonly("hasInputACCouplingSwitch", + &DeviceInfo::hasInputACCouplingSwitch); + + devinfo.def_static("getDeviceInfo", DeviceInfo::getDeviceInfo); + +} + diff --git a/lasp/dsp/lasp_dsp_pybind.cpp b/src/lasp/pybind11/lasp_dsp_pybind.cpp similarity index 59% rename from lasp/dsp/lasp_dsp_pybind.cpp rename to src/lasp/pybind11/lasp_dsp_pybind.cpp index 654f82b..60fa2cd 100644 --- a/lasp/dsp/lasp_dsp_pybind.cpp +++ b/src/lasp/pybind11/lasp_dsp_pybind.cpp @@ -1,11 +1,13 @@ #include "lasp_window.h" +#include "lasp_siggen.h" +#include "lasp_siggen_impl.h" #include #include using std::cerr; namespace py = pybind11; -PYBIND11_MODULE(lasp_dsp, m) { +void init_dsp(py::module& m) { py::class_ w(m, "Window"); @@ -17,4 +19,11 @@ PYBIND11_MODULE(lasp_dsp, m) { .value("Rectangular", Window::WindowType::Rectangular) .export_values(); + py::class_> siggen(m, "Siggen"); + siggen.def("setLevel", &Siggen::setLevel, "Set the level of the signal generator"); + + + py::class_> sw(m, "Sine", siggen); + sw.def(py::init()); + } diff --git a/src/lasp/pybind11/lasp_streammgr.cpp b/src/lasp/pybind11/lasp_streammgr.cpp new file mode 100644 index 0000000..9c996b8 --- /dev/null +++ b/src/lasp/pybind11/lasp_streammgr.cpp @@ -0,0 +1,23 @@ +#include "lasp_streammgr.h" +#include +#include +#include + +using std::cerr; +namespace py = pybind11; + +void init_streammgr(py::module &m) { + + py::class_> smgr( + m, "StreamMgr"); + + smgr.def("startStream", &StreamMgr::startStream); + smgr.def("stopStream", &StreamMgr::startStream); + smgr.def_static("getInstance", []() { + return std::unique_ptr(&StreamMgr::getInstance()); + }); + smgr.def("stopAllStreams", &StreamMgr::stopAllStreams); + + smgr.def("setSiggen", &StreamMgr::setSiggen); + +} diff --git a/lasp/tools/__init__.py b/src/lasp/tools/__init__.py similarity index 100% rename from lasp/tools/__init__.py rename to src/lasp/tools/__init__.py diff --git a/lasp/tools/tools.py b/src/lasp/tools/tools.py similarity index 100% rename from lasp/tools/tools.py rename to src/lasp/tools/tools.py diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1159d75..98f23a5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,15 +1,15 @@ include_directories(${CMAKE_SOURCE_DIR}/lasp/c) include_directories(${CMAKE_SOURCE_DIR}/lasp/device) -add_executable(test_bf test_bf.c) -add_executable(test_workers test_workers.c) -add_executable(test_fft test_fft.c) -add_executable(test_math test_math.c) +# add_executable(test_bf test_bf.c) +# add_executable(test_workers test_workers.c) +# add_executable(test_fft test_fft.c) +# add_executable(test_math test_math.c) -target_link_libraries(test_bf lasp_lib ${LASP_THREADING_LIBRARIES}) -target_link_libraries(test_fft lasp_lib ${LASP_THREADING_LIBRARIES}) -target_link_libraries(test_workers lasp_lib ${LASP_THREADING_LIBRARIES}) -target_link_libraries(test_math lasp_lib ${LASP_THREADING_LIBRARIES}) +# target_link_libraries(test_bf lasp_lib ${LASP_THREADING_LIBRARIES}) +# target_link_libraries(test_fft lasp_lib ${LASP_THREADING_LIBRARIES}) +# target_link_libraries(test_workers lasp_lib ${LASP_THREADING_LIBRARIES}) +# target_link_libraries(test_math lasp_lib ${LASP_THREADING_LIBRARIES}) if(LASP_ULDAQ) add_executable(test_uldaq test_uldaq.cpp) diff --git a/test/Device Info.ipynb b/test/Device Info.ipynb new file mode 100644 index 0000000..cee0641 --- /dev/null +++ b/test/Device Info.ipynb @@ -0,0 +1,233 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "make: Entering directory '/home/anne/wip/mycode/lasp'\n", + "make[1]: Entering directory '/home/anne/wip/mycode/lasp'\n", + "make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n", + "make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n", + "[ 26%] Built target lasp_dsp_lib\n", + "make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n", + "\u001b[35m\u001b[1mScanning dependencies of target lasp_device_lib\u001b[0m\n", + "make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n", + "make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n", + "[ 31%] \u001b[32mBuilding CXX object src/lasp/device/CMakeFiles/lasp_device_lib.dir/lasp_rtaudiodaq.cpp.o\u001b[0m\n", + "\u001b[01m\u001b[K/home/anne/wip/mycode/lasp/src/lasp/device/lasp_rtaudiodaq.cpp:\u001b[m\u001b[K In lambda function:\n", + "\u001b[01m\u001b[K/home/anne/wip/mycode/lasp/src/lasp/device/lasp_rtaudiodaq.cpp:258:20:\u001b[m\u001b[K \u001b[01;35m\u001b[Kwarning: \u001b[m\u001b[Kvariable ‘\u001b[01m\u001b[Kstat\u001b[m\u001b[K’ set but not used [\u001b[01;35m\u001b[K-Wunused-but-set-variable\u001b[m\u001b[K]\n", + " 258 | StreamStatus \u001b[01;35m\u001b[Kstat\u001b[m\u001b[K = _streamStatus;\n", + " | \u001b[01;35m\u001b[K^~~~\u001b[m\u001b[K\n", + "\u001b[01m\u001b[K/home/anne/wip/mycode/lasp/src/lasp/device/lasp_rtaudiodaq.cpp:\u001b[m\u001b[K In member function ‘\u001b[01m\u001b[Kint RtAudioDaq::streamCallback(void*, void*, unsigned int, double, RtAudioStreamStatus)\u001b[m\u001b[K’:\n", + "\u001b[01m\u001b[K/home/anne/wip/mycode/lasp/src/lasp/device/lasp_rtaudiodaq.cpp:250:36:\u001b[m\u001b[K \u001b[01;35m\u001b[Kwarning: \u001b[m\u001b[Kunused parameter ‘\u001b[01m\u001b[KstreamTime\u001b[m\u001b[K’ [\u001b[01;35m\u001b[K-Wunused-parameter\u001b[m\u001b[K]\n", + " 250 | unsigned int nFrames, \u001b[01;35m\u001b[Kdouble streamTime\u001b[m\u001b[K,\n", + " | \u001b[01;35m\u001b[K~~~~~~~^~~~~~~~~~\u001b[m\u001b[K\n", + "make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n", + "[ 63%] Built target lasp_device_lib\n", + "make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n", + "make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n", + "make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n", + "[ 68%] \u001b[32m\u001b[1mLinking CXX shared module lasp_cpp.cpython-38-x86_64-linux-gnu.so\u001b[0m\n", + "make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n", + "[100%] Built target lasp_cpp\n", + "make[1]: Leaving directory '/home/anne/wip/mycode/lasp'\n", + "make: Leaving directory '/home/anne/wip/mycode/lasp'\n" + ] + } + ], + "source": [ + "!make -j -C ~/wip/mycode/lasp" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import lasp\n", + "ds = lasp.DeviceInfo.getDeviceInfo()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "init_daqconfiguration(pybind11::module_&)\r\n" + ] + } + ], + "source": [ + "!c++filt _Z21init_daqconfigurationRN8pybind117module_E" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0: Camera Mono\n", + "1: Starship/Matisse HD Audio Controller Analog Stereo\n", + "2: Baffin HDMI/DP Audio [Radeon RX 550 640SP / RX 560/560X] Digital Stereo (HDMI)\n", + "3: Monitor of Baffin HDMI/DP Audio [Radeon RX 550 640SP / RX 560/560X] Digital Stereo (HDMI)\n", + "4: Monitor of Starship/Matisse HD Audio Controller Analog Stereo\n" + ] + } + ], + "source": [ + "for i, d in enumerate(ds):\n", + " print(f'{i}: ' + d.device_name)\n", + "d = ds[1]" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "config = lasp.DaqConfiguration(d)\n", + "config.outchannel_config[0].enabled = True\n", + "config.outchannel_config[1].enabled = True" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d.noutchannels" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "#daq = lasp.Daq.createDaq(d, config)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d.ninchannels" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "mgr = lasp.StreamMgr.getInstance()" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "mgr.startStream(d, config)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "#mgr.stopAllStreams()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "mgr.stopAllStreams()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "sine = lasp.Sine(1000)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "mgr.setSiggen(sine)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt new file mode 100644 index 0000000..08bfc3d --- /dev/null +++ b/third_party/CMakeLists.txt @@ -0,0 +1,10 @@ +# third_party/CMakeLists.txt + +if(LASP_FFT_BACKEND STREQUAL "FFTW") + find_library(FFTW_LIBRARY NAMES fftw3 fftw) + set(FFT_LIBRARIES "${FFTW_LIBRARY}") +elseif(LASP_FFT_BACKEND STREQUAL "FFTPack") + include_directories(fftpack) + set(FFT_LIBRARIES fftpack) + add_subdirectory(fftpack) +endif() diff --git a/DebugTrace-cpp b/third_party/DebugTrace-cpp similarity index 100% rename from DebugTrace-cpp rename to third_party/DebugTrace-cpp diff --git a/STL-Threadsafe b/third_party/STL-Threadsafe similarity index 100% rename from STL-Threadsafe rename to third_party/STL-Threadsafe diff --git a/armadillo-code b/third_party/armadillo-code similarity index 100% rename from armadillo-code rename to third_party/armadillo-code diff --git a/fftpack/CMakeLists.txt b/third_party/fftpack/CMakeLists.txt similarity index 68% rename from fftpack/CMakeLists.txt rename to third_party/fftpack/CMakeLists.txt index 77d4bca..79aa8c8 100644 --- a/fftpack/CMakeLists.txt +++ b/third_party/fftpack/CMakeLists.txt @@ -3,5 +3,7 @@ add_library(fftpack fftpack.c ) + # Ling fft to math -target_link_libraries(fftpack m) +target_link_libraries(fftpack PRIVATE m) +interface_include_directories(fftpack .) diff --git a/fftpack/fftpack.c b/third_party/fftpack/fftpack.c similarity index 99% rename from fftpack/fftpack.c rename to third_party/fftpack/fftpack.c index eaf172c..f5fe21f 100644 --- a/fftpack/fftpack.c +++ b/third_party/fftpack/fftpack.c @@ -1204,7 +1204,7 @@ static void cfftf1(int n, Treal c[], Treal ch[], const Treal wa[], const int ifa } /* cfftf1 */ -NPY_VISIBILITY_HIDDEN void npy_cfftf(int n, Treal c[], Treal wsave[]) +void npy_cfftf(int n, Treal c[], Treal wsave[]) { int iw1, iw2; if (n == 1) return; @@ -1214,7 +1214,7 @@ NPY_VISIBILITY_HIDDEN void npy_cfftf(int n, Treal c[], Treal wsave[]) } /* npy_cfftf */ -NPY_VISIBILITY_HIDDEN void npy_cfftb(int n, Treal c[], Treal wsave[]) +void npy_cfftb(int n, Treal c[], Treal wsave[]) { int iw1, iw2; if (n == 1) return; @@ -1304,7 +1304,7 @@ static void cffti1(int n, Treal wa[], int ifac[MAXFAC+2]) } /* cffti1 */ -NPY_VISIBILITY_HIDDEN void npy_cffti(int n, Treal wsave[]) +void npy_cffti(int n, Treal wsave[]) { int iw1, iw2; if (n == 1) return; diff --git a/fftpack/fftpack.h b/third_party/fftpack/fftpack.h similarity index 100% rename from fftpack/fftpack.h rename to third_party/fftpack/fftpack.h diff --git a/gsl-lite b/third_party/gsl-lite similarity index 100% rename from gsl-lite rename to third_party/gsl-lite diff --git a/third_party/tomlplusplus b/third_party/tomlplusplus new file mode 160000 index 0000000..6a47ce7 --- /dev/null +++ b/third_party/tomlplusplus @@ -0,0 +1 @@ +Subproject commit 6a47ce703d2825dd1da0776c946f13c293e096a2