Output of a sine wave now works. Pfff
This commit is contained in:
parent
7095f9d5e7
commit
6a006e27f9
25
.gitignore
vendored
25
.gitignore
vendored
@ -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
|
||||
|
11
.gitmodules
vendored
11
.gitmodules
vendored
@ -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
|
||||
|
132
CMakeLists.txt
132
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)
|
||||
|
228
Doxyfile
228
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.
|
||||
|
||||
@ -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.
|
||||
@ -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,6 +256,10 @@ 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 =
|
||||
|
||||
@ -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,17 +297,26 @@ 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.
|
||||
#
|
||||
@ -296,7 +327,7 @@ 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
|
||||
@ -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
|
||||
@ -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 \
|
||||
@ -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
|
||||
#---------------------------------------------------------------------------
|
||||
@ -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
|
||||
@ -1380,7 +1449,7 @@ 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,21 +1466,23 @@ 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 =
|
||||
|
||||
# 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 =
|
||||
|
||||
# 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 =
|
||||
@ -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/
|
||||
@ -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
|
||||
@ -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.
|
||||
@ -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,9 +1957,9 @@ 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.
|
||||
@ -1868,8 +1968,8 @@ RTF_HYPERLINKS = NO
|
||||
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 =
|
||||
@ -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
|
||||
#---------------------------------------------------------------------------
|
||||
@ -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,15 +2276,6 @@ 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.
|
||||
@ -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
|
||||
@ -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.
|
||||
|
35
README.md
35
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
|
||||
|
29
cmake/OSSpecific.cmake
Normal file
29
cmake/OSSpecific.cmake
Normal file
@ -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.
|
26
cmake/QueryPythonForPybind11.cmake
Normal file
26
cmake/QueryPythonForPybind11.cmake
Normal file
@ -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()
|
@ -1,3 +0,0 @@
|
||||
add_subdirectory(c)
|
||||
add_subdirectory(device)
|
||||
add_subdirectory(dsp)
|
@ -1,2 +0,0 @@
|
||||
from .lasp_device import *
|
||||
|
@ -1,97 +0,0 @@
|
||||
#include "lasp_daqconfig.h"
|
||||
#include "lasp_deviceinfo.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#define MAX_DEV_COUNT_PER_API 20
|
||||
|
||||
using std::vector;
|
||||
|
||||
|
||||
vector<DaqApi> DaqApi::getAvailableApis() {
|
||||
|
||||
vector<DaqApi> 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;
|
||||
}
|
||||
|
@ -1,139 +0,0 @@
|
||||
#include "lasp_deviceinfo.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#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<int>(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<DataTypeDescriptor::DataType>(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> DeviceInfo::getDeviceInfo() {
|
||||
std::vector<DeviceInfo> devs;
|
||||
#if LASP_HAS_ULDAQ ==1
|
||||
fillUlDaqDeviceInfo(devs);
|
||||
#endif
|
||||
|
||||
#if LASP_HAS_RTAUDIO == 1
|
||||
fillRtAudioDeviceInfo(devs);
|
||||
#endif
|
||||
|
||||
return devs;
|
||||
}
|
||||
|
@ -1,111 +0,0 @@
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#include <pybind11/numpy.h>
|
||||
#include <stdint.h>
|
||||
#include <iostream>
|
||||
#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_<DataTypeDescriptor> 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_<DataTypeDescriptor::DataType>(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> 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_<DeviceInfo> 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_<DaqConfiguration> 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);
|
||||
|
||||
}
|
||||
|
@ -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})
|
||||
|
@ -1 +0,0 @@
|
||||
from .lasp_dsp import *
|
27
pyproject.toml
Normal file
27
pyproject.toml
Normal file
@ -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"
|
||||
|
||||
|
35
setup.py
Executable file → Normal file
35
setup.py
Executable file → Normal file
@ -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",
|
||||
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
|
||||
|
||||
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',
|
||||
)
|
||||
|
28
src/lasp/CMakeLists.txt
Normal file
28
src/lasp/CMakeLists.txt
Normal file
@ -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 .)
|
@ -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 )
|
||||
|
@ -11,32 +11,15 @@ DEBUGTRACE_VARIABLES;
|
||||
#endif
|
||||
using std::runtime_error;
|
||||
|
||||
Daq::~Daq() {}
|
||||
|
||||
std::unique_ptr<Daq> 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
|
||||
@ -55,12 +38,21 @@ Daq::Daq(const DeviceInfo &devinfo, const DaqConfiguration &config)
|
||||
: DaqConfiguration(config), DeviceInfo(devinfo) {
|
||||
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;
|
||||
}
|
@ -7,24 +7,6 @@
|
||||
#include <gsl/gsl-lite.hpp>
|
||||
#include <memory>
|
||||
|
||||
/**
|
||||
* @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<StreamError, std::string> 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<InDaqCallback> inCallback,
|
||||
std::optional<OutDaqCallback> 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);
|
||||
}
|
||||
};
|
137
src/lasp/device/lasp_daqconfig.cpp
Normal file
137
src/lasp/device/lasp_daqconfig.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
#include "lasp_daqconfig.h"
|
||||
#include "lasp_deviceinfo.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#define MAX_DEV_COUNT_PER_API 20
|
||||
|
||||
using std::vector;
|
||||
|
||||
vector<DaqApi> DaqApi::getAvailableApis() {
|
||||
|
||||
vector<DaqApi> 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 <sstream>
|
||||
|
||||
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<int>(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();
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "lasp_config.h"
|
||||
#include "lasp_types.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
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<DaqChannel> inchannel_config;
|
||||
|
||||
/**
|
||||
* @brief Enabled 'normal' output channels, i.e. no internal loopbacks.
|
||||
* @brief Channel configuration for output channels
|
||||
*/
|
||||
boolvec enoutchannels;
|
||||
|
||||
std::vector<double> inchannel_sensitivities;
|
||||
std::vector<string> inchannel_names;
|
||||
std::vector<string> inchannel_metadata;
|
||||
|
||||
std::vector<double> outchannel_sensitivities;
|
||||
std::vector<string> outchannel_names;
|
||||
std::vector<string> outchannel_metadata;
|
||||
std::vector<DaqChannel> 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;
|
||||
};
|
||||
|
||||
|
||||
};
|
@ -1,14 +1,13 @@
|
||||
#pragma once
|
||||
#include "lasp_daqconfig.h"
|
||||
#include "lasp_types.h"
|
||||
#include <functional>
|
||||
#include <gsl/gsl-lite.hpp>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#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<T &>(_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<T &>(*raw_ptr(frame, channel));
|
||||
}
|
||||
|
||||
void copyToRaw(const us channel, T *ptr) {
|
||||
DaqData::copyToRaw(channel, reinterpret_cast<uint8_t *>(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<uint8_t *>(buffer));
|
||||
}
|
||||
};
|
27
src/lasp/device/lasp_deviceinfo.cpp
Normal file
27
src/lasp/device/lasp_deviceinfo.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#include "lasp_deviceinfo.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#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> DeviceInfo::getDeviceInfo() {
|
||||
std::vector<DeviceInfo> devs;
|
||||
#if LASP_HAS_ULDAQ ==1
|
||||
fillUlDaqDeviceInfo(devs);
|
||||
#endif
|
||||
|
||||
#if LASP_HAS_RTAUDIO == 1
|
||||
fillRtAudioDeviceInfo(devs);
|
||||
#endif
|
||||
|
||||
return devs;
|
||||
}
|
||||
|
@ -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
|
||||
*
|
@ -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 <RtAudio.h>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <thread>
|
||||
|
||||
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<DeviceInfo> &devinfolist) {
|
||||
|
||||
vector<RtAudio::Api> apis;
|
||||
@ -103,10 +104,10 @@ void fillRtAudioDeviceInfo(vector<DeviceInfo> &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> _streamStatus{};
|
||||
|
||||
public:
|
||||
RtAudioDaq(const DeviceInfo &devinfo, const DaqConfiguration &config)
|
||||
: Daq(devinfo, config),
|
||||
rtaudio(static_cast<RtAudio::Api>(devinfo.api.api_specific_subcode)),
|
||||
nFramesPerBlock(Daq::framesPerBlock()) {
|
||||
|
||||
std::unique_ptr<RtAudio::StreamParameters> 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<RtAudio::StreamParameters> inParams, outParams;
|
||||
|
||||
if (neninchannels() > 0) {
|
||||
|
||||
inParams = std::make_unique<RtAudio::StreamParameters>();
|
||||
@ -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<us>(samplerate()), &nFramesPerBlock_copy,
|
||||
&mycallback, (void *)this, &streamoptions,
|
||||
mycallback, (void *)this, &streamoptions,
|
||||
&myerrorcallback);
|
||||
}
|
||||
|
||||
virtual void start(std::optional<InDaqCallback> inCallback,
|
||||
std::optional<OutDaqCallback> 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<uint8_t *>(
|
||||
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<uint8_t *>(
|
||||
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<Daq> 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) {
|
@ -1,17 +1,19 @@
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
#include "lasp_streammgr.h"
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#include <functional>
|
||||
#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<std::mutex> lck(_inDataHandler_mtx);
|
||||
@ -37,11 +39,11 @@ bool StreamMgr::inCallback(const DaqData &data) {
|
||||
template <typename T> bool fillData(DaqData &data, const vd &signal) {
|
||||
assert(data.nframes == signal.size());
|
||||
|
||||
T* res = reinterpret_cast<T*>(data.raw_ptr());
|
||||
T *res = reinterpret_cast<T *>(data.raw_ptr());
|
||||
if (std::is_floating_point<T>()) {
|
||||
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> siggen) {
|
||||
|
||||
std::scoped_lock<std::mutex> 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<std::mutex> 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 "
|
||||
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");
|
||||
}
|
||||
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");
|
||||
InDaqCallback inCallback;
|
||||
OutDaqCallback outCallback;
|
||||
|
||||
using namespace std::placeholders;
|
||||
std::unique_ptr<Daq> daq = Daq::createDaq(devinfo, config);
|
||||
|
||||
if(isInput) {
|
||||
inCallback = std::bind(&StreamMgr::inCallback, this, _1);
|
||||
}
|
||||
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");
|
||||
|
||||
if(isOutput) {
|
||||
if(_siggen) {
|
||||
_siggen->reset(daq->samplerate());
|
||||
}
|
||||
streamtype = StreamType::input;
|
||||
outCallback = std::bind(&StreamMgr::outCallback, this, _1);
|
||||
}
|
||||
_streams[streamtype] = Daq::createDaq(devinfo, config);
|
||||
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<std::mutex> 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();
|
||||
}
|
||||
}
|
@ -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<StreamType, std::unique_ptr<Daq>> _streams;
|
||||
std::unique_ptr<Daq> _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<Siggen> 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);
|
||||
|
||||
};
|
@ -1,4 +1,5 @@
|
||||
#include "lasp_uldaq.h"
|
||||
#if LASP_HAS_ULDAQ == 1
|
||||
#include "lasp_daqconfig.h"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
@ -55,11 +56,12 @@ class DT9837A : public Daq {
|
||||
|
||||
std::thread _thread;
|
||||
atomic<bool> _stopThread{false};
|
||||
atomic<StreamStatus> _streamStatus;
|
||||
|
||||
const us _nFramesPerBlock;
|
||||
|
||||
void threadFcn(std::optional<InDaqCallback> inCallback,
|
||||
std::optional<OutDaqCallback> 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<InDaqCallback> inCallback,
|
||||
std::optional<OutDaqCallback> 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<InDaqCallback> inCallback,
|
||||
std::optional<OutDaqCallback> 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<DaqInChanDescriptor> indescs;
|
||||
|
||||
boolvec eninchannels = daq.eninchannels;
|
||||
boolvec eninchannels = daq.eninchannels();
|
||||
|
||||
// Initialize input, if any
|
||||
for (us chin = 0; chin < 4; chin++) {
|
||||
@ -360,18 +370,30 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void DT9837A::threadFcn(std::optional<InDaqCallback> inCallback,
|
||||
std::optional<OutDaqCallback> outCallback) {
|
||||
void DT9837A::threadFcn(InDaqCallback inCallback,
|
||||
OutDaqCallback outCallback) {
|
||||
|
||||
DEBUGTRACE_ENTER;
|
||||
|
||||
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<InBufHandler> ibh;
|
||||
std::unique_ptr<OutBufHandler> obh;
|
||||
|
||||
if (neninchannels() > 0) {
|
||||
ibh = std::make_unique<InBufHandler>(*this, inCallback.value());
|
||||
assert(inCallback);
|
||||
ibh = std::make_unique<InBufHandler>(*this, inCallback);
|
||||
}
|
||||
if (nenoutchannels() > 0) {
|
||||
obh = std::make_unique<OutBufHandler>(*this, outCallback.value());
|
||||
assert(outCallback);
|
||||
obh = std::make_unique<OutBufHandler>(*this, outCallback);
|
||||
}
|
||||
|
||||
const double sleeptime =
|
||||
@ -391,6 +413,13 @@ void DT9837A::threadFcn(std::optional<InDaqCallback> inCallback,
|
||||
}
|
||||
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<Daq> 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<DeviceInfo> &devinfolist) {
|
||||
devinfolist.push_back(devinfo);
|
||||
}
|
||||
}
|
||||
#endif
|
16
src/lasp/dsp/CMakeLists.txt
Normal file
16
src/lasp/dsp/CMakeLists.txt
Normal file
@ -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})
|
||||
|
@ -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);
|
||||
|
@ -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:
|
@ -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.
|
19
src/lasp/lasp_cpp.cpp
Normal file
19
src/lasp/lasp_cpp.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
#include <pybind11/pybind11.h>
|
||||
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);
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user