Output of a sine wave now works. Pfff

This commit is contained in:
Anne de Jong 2022-07-20 14:58:48 +02:00
parent 7095f9d5e7
commit 6a006e27f9
131 changed files with 1575 additions and 966 deletions

25
.gitignore vendored
View File

@ -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
View File

@ -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

View File

@ -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
View File

@ -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.

View File

@ -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
View 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.

View 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()

View File

@ -1,3 +0,0 @@
add_subdirectory(c)
add_subdirectory(device)
add_subdirectory(dsp)

View File

@ -1,2 +0,0 @@
from .lasp_device import *

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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})

View File

@ -1 +0,0 @@
from .lasp_dsp import *

27
pyproject.toml Normal file
View 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
View 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
View 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 .)

View File

@ -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 )

View File

@ -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;
}

View File

@ -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);
}
};

View 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();
}

View File

@ -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;
};
};

View File

@ -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));
}
};

View 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;
}

View File

@ -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
*

View File

@ -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) {

View File

@ -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();
}
}

View File

@ -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);
};

View File

@ -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

View 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})

View File

@ -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);

View File

@ -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:

View File

@ -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
View 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