Merged Pybind11 branch. It is more ~ less ready
This commit is contained in:
commit
a581226f3d
28
.gitignore
vendored
28
.gitignore
vendored
@ -1,34 +1,22 @@
|
||||
*.a
|
||||
*.cxx
|
||||
*.pyc
|
||||
*.so
|
||||
dist
|
||||
src/lasp.egg-info
|
||||
test/.ipynb_checkpoints
|
||||
src/lasp/lasp_config.h
|
||||
_deps
|
||||
compile_commands.json
|
||||
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
|
||||
.ipynb_checkpoints
|
||||
.spyproject
|
||||
test/test_uldaq
|
||||
lasp/device/lasp_daq.cxx
|
||||
lasp/c/lasp_config.h
|
||||
compile_commands.json
|
||||
.cache
|
||||
_skbuild
|
||||
|
32
.gitmodules
vendored
32
.gitmodules
vendored
@ -1,3 +1,33 @@
|
||||
[submodule "STL-Threadsafe"]
|
||||
path = STL-Threadsafe
|
||||
path = third_party/STL-Threadsafe
|
||||
url = https://github.com/miachm/STL-Threadsafe
|
||||
[submodule "gsl-lite"]
|
||||
path = third_party/gsl-lite
|
||||
url = https://github.com/gsl-lite/gsl-lite
|
||||
[submodule "DebugTrace-cpp"]
|
||||
path = third_party/DebugTrace-cpp
|
||||
url = https://github.com/MasatoKokubo/DebugTrace-cpp
|
||||
[submodule "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
|
||||
[submodule "third_party/lockfree"]
|
||||
path = third_party/boost/lockfree
|
||||
url = https://github.com/boostorg/lockfree
|
||||
[submodule "third_party/carma"]
|
||||
path = third_party/carma
|
||||
url = https://github.com/RUrlus/carma
|
||||
[submodule "third_party/thread-pool"]
|
||||
path = third_party/thread-pool
|
||||
url = https://github.com/bshoshany/thread-pool
|
||||
[submodule "third_party/rtaudio"]
|
||||
path = third_party/rtaudio
|
||||
url = https://github.com/thestk/rtaudio
|
||||
[submodule "third_party/boost/core"]
|
||||
path = third_party/boost/core
|
||||
url = https://github.com/boostorg/core
|
||||
[submodule "third_party/uldaq"]
|
||||
path = third_party/uldaq
|
||||
url = https://github.com/mccdaq/uldaq
|
||||
|
226
CMakeLists.txt
226
CMakeLists.txt
@ -1,183 +1,97 @@
|
||||
cmake_minimum_required (VERSION 3.12)
|
||||
cmake_minimum_required (VERSION 3.16)
|
||||
project(LASP LANGUAGES C CXX VERSION 1.0)
|
||||
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED)
|
||||
|
||||
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)
|
||||
option(LASP_BUILD_TUNED "Tune build for current machine" OFF)
|
||||
option(LASP_WITH_OPENMP "Use OpenMP parallelization" ON)
|
||||
set(LASP_MAX_NFFT "33554432" CACHE STRING "Max FFT size")
|
||||
|
||||
# Use ccache if available
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
if(CCACHE_PROGRAM)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCACHE_PROGRAM}")
|
||||
endif()
|
||||
|
||||
# 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")
|
||||
include("BuildType")
|
||||
|
||||
|
||||
# This is used for code completion in vim
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS=ON)
|
||||
project(LASP LANGUAGES C CXX)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
# Whether we want to use blas yes or no
|
||||
option(LASP_USE_BLAS "Use external blas library for math" ON)
|
||||
option(LASP_ARM "Compile subset of code for ARM real time (Bela board)" OFF)
|
||||
option(LASP_DOUBLE_PRECISION "Compile as double precision floating point" ON)
|
||||
option(LASP_PARALLEL "Parallel processing" ON)
|
||||
option(LASP_HAS_RTAUDIO "Compile with RtAudio Daq backend" ON)
|
||||
option(LASP_HAS_ULDAQ "Compile with UlDaq backend" ON)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake")
|
||||
include("BuildType")
|
||||
include("QueryPythonForPybind11")
|
||||
|
||||
# Find the pybind11 package
|
||||
find_pybind11_python_first()
|
||||
|
||||
find_package(OpenMP REQUIRED COMPONENTS CXX)
|
||||
|
||||
set(LASP_MAX_NUM_CHANNELS 16 CACHE STRING "Maximum number of audio channels that is compiled in in code")
|
||||
set(LASP_MAX_NUM_THREADS 30 CACHE STRING "The maximum number of simultaneous threads in parallel processing")
|
||||
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")
|
||||
set_property(CACHE LASP_FFT_BACKEND PROPERTY STRINGS "FFTW" "Armadillo")
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
set(PY_FULL_VERSION ${PROJECT_VERSION}${PY_VERSION_SUFFIX})
|
||||
|
||||
# Required for PYBIND11
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
|
||||
set(LASP_DEBUG True)
|
||||
# This does not work nicely with RtAudio. However, if we compile it
|
||||
# ourselves as a third_party ref, this works nicely together.
|
||||
add_definitions(-D_GLIBCXX_DEBUG)
|
||||
|
||||
else()
|
||||
set(LASP_DEBUG False)
|
||||
endif()
|
||||
|
||||
if(LASP_PARALLEL)
|
||||
add_definitions(-D_REENTRANT)
|
||||
set(LASP_THREADING_LIBRARIES pthread)
|
||||
else()
|
||||
set(LASP_THREADING_LIBRARIES "")
|
||||
# Tune for current machine
|
||||
if(LASP_BUILD_TUNED)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
message(FATAL_ERROR
|
||||
"Cannot build optimized and tuned code when debug is switched on")
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -mtune=native")
|
||||
endif()
|
||||
|
||||
if(LASP_USE_BLAS)
|
||||
# link openblas
|
||||
set(BLA_VENDOR OpenBLAS)
|
||||
find_package(BLAS REQUIRED)
|
||||
endif()
|
||||
|
||||
|
||||
# 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
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
# ###################################### Find and link to OpenBLAS
|
||||
set(BLA_VENDOR OpenBLAS)
|
||||
find_package(BLAS REQUIRED)
|
||||
|
||||
# ###################################### Find and link to FFTW
|
||||
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)
|
||||
find_library(fftw3 REQUIRED NAMES fftw fftw3)
|
||||
set(LASP_FFT_LIBS "fftw3")
|
||||
elseif(LASP_FFT_BACKEND STREQUAL "Armadillo")
|
||||
endif()
|
||||
|
||||
# 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} -std=c11 -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()
|
||||
|
||||
set(CYTHON_FLAGS "--fast-fail")
|
||||
if(LASP_DEBUG)
|
||||
set(LASP_DEBUG_CYTHON=True)
|
||||
message("Building debug code")
|
||||
# This will produce html files
|
||||
# set(CYTHON_ANNOTATE ON)
|
||||
# Add the __FILENAME__ macro
|
||||
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__FILENAME__='\"$(subst ${CMAKE_SOURCE_DIR}/,,$(abspath $<))\"'")
|
||||
# ###################################### OpenMP related
|
||||
if(LASP_WITH_OPENMP)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
|
||||
else()
|
||||
message("Building LASP for release")
|
||||
set(LASP_DEBUG_CYTHON=False)
|
||||
set(CYTHON_ANNOTATE OFF)
|
||||
set(CYTHON_NO_DOCSTRINGS ON)
|
||||
# Strip unnecessary symbols
|
||||
# set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--gc-sections")
|
||||
# set(CMAKE_MODULE_LINKER_FLAGS "-Wl,--gc-sections")
|
||||
endif(LASP_DEBUG)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas")
|
||||
endif()
|
||||
|
||||
# The last argument here takes care of calling SIGABRT when an integer overflow
|
||||
# occures.
|
||||
############################## General compilation flags (independent of debug mode, windows or linux)
|
||||
set(CYTHON_EXTRA_C_FLAGS "-Wno-sign-compare -Wno-cpp -Wno-implicit-fallthrough -Wno-incompatible-pointer-types -Wno-strict-aliasing")
|
||||
set(CYTHON_EXTRA_CXX_FLAGS "-Wno-sign-compare -Wno-cpp -Wno-implicit-fallthrough -Wno-strict-aliasing")
|
||||
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra \
|
||||
-Wno-type-limits")
|
||||
|
||||
# Debug make flags
|
||||
set(CMAKE_C_FLAGS_DEBUG "-g" )
|
||||
|
||||
set(CMAKE_C_FLAGS_RELEASE "-O2 -mfpmath=sse -march=x86-64 -mtune=native \
|
||||
# ###################################### Compilation flags
|
||||
set(CMAKE_C_FLAGS_RELEASE "-O3 -flto -mfpmath=sse -march=x86-64 -mtune=native \
|
||||
-fdata-sections -ffunction-sections -fomit-frame-pointer -finline-functions")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-type-limits")
|
||||
|
||||
|
||||
# ############################# End compilation flags
|
||||
include_directories(/usr/lib/python3.10/site-packages/numpy/core/include)
|
||||
|
||||
# ####################################### End of user-adjustable variables section
|
||||
include(OSSpecific)
|
||||
include(rtaudio)
|
||||
include(uldaq)
|
||||
#
|
||||
add_subdirectory(src/lasp)
|
||||
|
||||
# Python searching.
|
||||
set(Python_ADDITIONAL_VERSIONS "3.8")
|
||||
set(python_version_windll "38")
|
||||
find_package(PythonLibs REQUIRED )
|
||||
find_package(PythonInterp REQUIRED)
|
||||
|
||||
# Add FFTpack dir if used as FFT backend
|
||||
if(LASP_FFTPACK_BACKEND)
|
||||
add_subdirectory(fftpack)
|
||||
include_directories(
|
||||
fftpack
|
||||
)
|
||||
endif()
|
||||
|
||||
include_directories(lasp/c)
|
||||
include_directories(STL-Threadsafe/include)
|
||||
add_subdirectory(lasp)
|
||||
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_daq")
|
||||
|
||||
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} .)")
|
||||
|
43
README.md
43
README.md
@ -31,31 +31,56 @@ 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)
|
||||
|
||||
## Editable install
|
||||
|
||||
In the root directory of the repository, run:
|
||||
|
||||
- `pip3 isntall --user --prefix=~/.local -e .`
|
||||
- `cmake .`
|
||||
- `make -j`
|
||||
|
||||
|
||||
|
||||
## 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
|
||||
|
@ -1 +0,0 @@
|
||||
Subproject commit 08b2d9e7f487121088a817071d1d42b2736996e9
|
11
cmake/BuildType.cmake
Normal file
11
cmake/BuildType.cmake
Normal file
@ -0,0 +1,11 @@
|
||||
# Set a default build type if none was specified
|
||||
|
||||
set(default_build_type "Release")
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
message(STATUS "Setting build type to '${default_build_type}' as none was specified.")
|
||||
set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose the type of build." FORCE)
|
||||
|
||||
# Set the possible values of build type for cmake-gui
|
||||
endif()
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release")
|
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()
|
12
cmake/rtaudio.cmake
Normal file
12
cmake/rtaudio.cmake
Normal file
@ -0,0 +1,12 @@
|
||||
# ###################################### RtAudio
|
||||
if(LASP_HAS_RTAUDIO)
|
||||
message("Building RtAudio backend")
|
||||
if(WIN32)
|
||||
set(RTAUDIO_API_WASAPI TRUE CACHE BOOL "Build for WASAPI" FORCE)
|
||||
else()
|
||||
set(RTAUDIO_API_PULSE TRUE CACHE BOOL "Build with PulseAudio backend" FORCE)
|
||||
set(RTAUDIO_API_ALSA OFF CACHE BOOL "Do not build with Alsa backend" FORCE)
|
||||
endif()
|
||||
set(RTAUDIO_BUILD_STATIC_LIBS ON CACHE BOOL "Build static libs for RtAudio" FORCE)
|
||||
add_subdirectory(third_party/rtaudio)
|
||||
endif()
|
30
cmake/uldaq.cmake
Normal file
30
cmake/uldaq.cmake
Normal file
@ -0,0 +1,30 @@
|
||||
# ###################################### UlDAQ
|
||||
if(LASP_HAS_ULDAQ)
|
||||
message("Building UlDAQ")
|
||||
|
||||
if(NOT WIN32)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(libusb-1.0 REQUIRED libusb-1.0)
|
||||
endif()
|
||||
|
||||
# This is rather coarse!
|
||||
file(GLOB ULDAQ_FILES1 third_party/uldaq/src/*.cpp)
|
||||
file(GLOB ULDAQ_FILES2 third_party/uldaq/src/*/*.cpp)
|
||||
file(GLOB ULDAQ_FILES3 third_party/uldaq/src/*/*/*.cpp)
|
||||
file(GLOB ULDAQ_FILES4 third_party/uldaq/src/usb/fw/*c)
|
||||
|
||||
add_library(uldaq STATIC ${ULDAQ_FILES1} ${ULDAQ_FILES2}
|
||||
${ULDAQ_FILES3} ${ULDAQ_FILES4})
|
||||
target_compile_options(uldaq PUBLIC -Wno-unused -Wno-empty-body
|
||||
-Wno-missing-field-initializers)
|
||||
|
||||
if(NOT WIN32)
|
||||
# message("libUSB libs: ${libusb-1.0_LIBRARIES}")
|
||||
target_link_libraries(uldaq ${libusb-1.0_LIBRARIES})
|
||||
|
||||
# Rules to match UlDAQ Usb devices with libusb
|
||||
install(FILES third_party/uldaq/rules/50-uldaq.rules DESTINATION
|
||||
/etc/udev/rules.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ
|
||||
GROUP_WRITE WORLD_READ)
|
||||
endif()
|
||||
endif()
|
129
examples/DaqConfiguration.ipynb
Normal file
129
examples/DaqConfiguration.ipynb
Normal file
@ -0,0 +1,129 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#!make -j -C ~/wip/mycode/lasp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import lasp\n",
|
||||
"ds = lasp.DeviceInfo.getDeviceInfo()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"for i, d in enumerate(ds):\n",
|
||||
" print(f'{i}: ' + d.device_name)\n",
|
||||
"d = ds[0]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Create a configuration using a device as prototype\n",
|
||||
"config = lasp.DaqConfiguration(d)\n",
|
||||
"\n",
|
||||
"# Enable some channels\n",
|
||||
"#config.outchannel_config[0].enabled = True\n",
|
||||
"#config.outchannel_config[1].enabled = True"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Print info\n",
|
||||
"print('Out channels:',d.noutchannels)\n",
|
||||
"print('In channels:',d.ninchannels)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Serialize configuration to TOML\n",
|
||||
"toml = config.toTOML()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(toml)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Read in configuration from serialized data\n",
|
||||
"d2 = lasp.DaqConfiguration.fromTOML(toml)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Print it\n",
|
||||
"print(d2.toTOML())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Check\n",
|
||||
"toml == d2.toTOML()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.10"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
119
examples/DaqConfigurations.ipynb
Normal file
119
examples/DaqConfigurations.ipynb
Normal file
@ -0,0 +1,119 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Assemble a set of configurations (one for input, one for output)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#!make -j -C ~/wip/mycode/lasp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from lasp import *"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import lasp\n",
|
||||
"print(lasp.__version__)\n",
|
||||
"ds = lasp.DeviceInfo.getDeviceInfo()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"for i, d in enumerate(ds):\n",
|
||||
" print(f'{i}: ' + d.device_name)\n",
|
||||
"di = ds[0]\n",
|
||||
"do = ds[1]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"iconfig = lasp.DaqConfiguration(di)\n",
|
||||
"oconfig = lasp.DaqConfiguration(do)\n",
|
||||
"ds = lasp.DaqConfigurations(False, iconfig, oconfig)\n",
|
||||
"oconfig.outchannel_config[0].enabled = True\n",
|
||||
"oconfig.outchannel_config[1].enabled = True\n",
|
||||
"iconfig.inchannel_config[0].enabled = True\n",
|
||||
"iconfig.inchannel_config[1].enabled = True"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Save configuration to a preset\n",
|
||||
"# ds.save('test')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Load all configurations present\n",
|
||||
"ds.loadAll()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Print some stuff\n",
|
||||
"print('Out channels:',do.noutchannels)\n",
|
||||
"print('In channels:',di.ninchannels)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.10"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
200
examples/test_SLM.ipynb
Normal file
200
examples/test_SLM.ipynb
Normal file
File diff suppressed because one or more lines are too long
82
examples/test_SeriesBiquad.ipynb
Normal file
82
examples/test_SeriesBiquad.ipynb
Normal file
@ -0,0 +1,82 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from lasp import SeriesBiquad\n",
|
||||
"import numpy as np"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"coefs = np.array([1.,0,0,1.,-.9,0])\n",
|
||||
"bq = SeriesBiquad(coefs)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"x = np.zeros(10, dtype=float)\n",
|
||||
"x[0] = 1\n",
|
||||
"\n",
|
||||
"x2 = bq.filter(x)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"x2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"type(x2)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.10"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
184
examples/test_input.ipynb
Normal file
184
examples/test_input.ipynb
Normal file
@ -0,0 +1,184 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "740b4091",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Test DAQ input on a device"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "ac06df04",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"make: Entering directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"make[1]: Entering directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"\u001b[35m\u001b[1mConsolidate compiler generated dependencies of target lasp_dsp_lib\u001b[0m\n",
|
||||
"make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"[ 42%] Built target lasp_dsp_lib\n",
|
||||
"make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"\u001b[35m\u001b[1mConsolidate compiler generated dependencies of target lasp_device_lib\u001b[0m\n",
|
||||
"make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"[ 67%] Built target lasp_device_lib\n",
|
||||
"make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"\u001b[35m\u001b[1mConsolidate compiler generated dependencies of target lasp_cpp\u001b[0m\n",
|
||||
"make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"[ 71%] \u001b[32mBuilding CXX object src/lasp/CMakeFiles/lasp_cpp.dir/pybind11/lasp_dsp_pybind.cpp.o\u001b[0m\n",
|
||||
"[ 75%] \u001b[32m\u001b[1mLinking CXX shared module lasp_cpp.cpython-310-x86_64-linux-gnu.so\u001b[0m\n",
|
||||
"make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"[100%] Built target lasp_cpp\n",
|
||||
"make[1]: Leaving directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"make: Leaving directory '/home/anne/wip/mycode/lasp'\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# !make -j -C ~/wip/mycode/lasp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ce0dd691",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import lasp\n",
|
||||
"# Get handle to stream manager\n",
|
||||
"mgr = lasp.StreamMgr.getInstance()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "3cd242a8",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"ds = mgr.getDeviceInfo()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "a5d878e9",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Search for a device\n",
|
||||
"for i, d in enumerate(ds):\n",
|
||||
" print(f'{i}: ' + d.device_name)\n",
|
||||
"d = ds[0]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "a456054e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Check the available block sizes\n",
|
||||
"d.availableFramesPerBlock"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "6d5b281e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Create a configuration and enable some input channels\n",
|
||||
"config = lasp.DaqConfiguration(d)\n",
|
||||
"config.inchannel_config[0].enabled = True\n",
|
||||
"config.inchannel_config[1].enabled = True\n",
|
||||
"# Choose a different number of frames per block\n",
|
||||
"config.framesPerBlockIndex = 4"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "1ead3995",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print('Out channels:',d.noutchannels)\n",
|
||||
"print('In channels:',d.ninchannels)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "12db8306",
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Start a stream with a configuration\n",
|
||||
"mgr.startStream(config)\n",
|
||||
"\n",
|
||||
"def cb(data):\n",
|
||||
" # raise RuntimeError('hh')\n",
|
||||
" print(data.shape)\n",
|
||||
"i = lasp.InDataHandler(mgr, cb)\n",
|
||||
"import time\n",
|
||||
"#time.sleep(4)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "4d9f0103",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"mgr.stopStream(lasp.StreamMgr.StreamType.input)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "61b7282f",
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"del i"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.10"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
138
examples/test_output.ipynb
Normal file
138
examples/test_output.ipynb
Normal file
@ -0,0 +1,138 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "740b4091",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Test DAQ output on a device"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ac06df04",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# !make -j -C ~/wip/mycode/lasp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ce0dd691",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import lasp\n",
|
||||
"# Get handle to stream manager\n",
|
||||
"mgr = lasp.StreamMgr.getInstance()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "3cd242a8",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"ds = mgr.getDeviceInfo()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "a5d878e9",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Search for a device\n",
|
||||
"for i, d in enumerate(ds):\n",
|
||||
" print(f'{i}: ' + d.device_name)\n",
|
||||
"d = ds[1]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "6d5b281e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Create a configuration and enable some input channels\n",
|
||||
"config = lasp.DaqConfiguration(d)\n",
|
||||
"config.outchannel_config[0].enabled = True\n",
|
||||
"config.outchannel_config[1].enabled = True\n",
|
||||
"config.setAllInputEnabled(False)\n",
|
||||
"# Choose a different number of frames per block\n",
|
||||
"config.framesPerBlockIndex = 4"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "1ead3995",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print('Out channels:',d.noutchannels)\n",
|
||||
"print('In channels:',d.ninchannels)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "12db8306",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Start a stream with a configuration\n",
|
||||
"mgr.startStream(config)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "da5e8b81-bcd6-4587-8f59-74811a263eee",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"siggen = lasp.Sine(1000)\n",
|
||||
"siggen.setLevel(-20)\n",
|
||||
"mgr.setSiggen(siggen)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "4d9f0103",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"mgr.stopStream(lasp.StreamMgr.StreamType.output)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.10"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
619
examples/test_ppm.ipynb
Normal file
619
examples/test_ppm.ipynb
Normal file
@ -0,0 +1,619 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "740b4091",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Test PPM"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "ac06df04",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"make: Entering directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"make[1]: Entering directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"[ 44%] Built target lasp_dsp_lib\n",
|
||||
"make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"[ 68%] Built target lasp_device_lib\n",
|
||||
"make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"[100%] Built target lasp_cpp\n",
|
||||
"make[1]: Leaving directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"make: Leaving directory '/home/anne/wip/mycode/lasp'\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"!make -j -C ~/wip/mycode/lasp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "ce0dd691",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"2022-10-03 13:39:03+0200 DebugTrace-cpp 2.0.0a2 (g++ 9.4.0)\n",
|
||||
"2022-10-03 13:39:03+0200 \n",
|
||||
"2022-10-03 13:39:03+0200 Enter getInstance (lasp_streammgr.cpp: 40)\n",
|
||||
"2022-10-03 13:39:03+0200 | Enter StreamMgr (lasp_streammgr.cpp: 45)\n",
|
||||
"2022-10-03 13:39:03+0200 | | Enter rescanDAQDevices (lasp_streammgr.cpp: 60)\n",
|
||||
"2022-10-03 13:39:03+0200 | | | Enter rescanDAQDevices_impl (lasp_streammgr.cpp: 80)\n",
|
||||
"2022-10-03 13:39:04+0200 | | | | Enter fillRtAudioDeviceInfo (lasp_rtaudiodaq.cpp: 20)\n",
|
||||
"2022-10-03 13:39:04+0200 | | | | Leave fillRtAudioDeviceInfo (lasp_rtaudiodaq.cpp)\n",
|
||||
"2022-10-03 13:39:04+0200 | | | Leave rescanDAQDevices_impl (lasp_streammgr.cpp)\n",
|
||||
"2022-10-03 13:39:04+0200 | | Leave rescanDAQDevices (lasp_streammgr.cpp)\n",
|
||||
"2022-10-03 13:39:04+0200 | Leave StreamMgr (lasp_streammgr.cpp)\n",
|
||||
"2022-10-03 13:39:04+0200 Leave getInstance (lasp_streammgr.cpp)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import lasp\n",
|
||||
"# Get handle to stream manager\n",
|
||||
"mgr = lasp.StreamMgr.getInstance()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "3cd242a8",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"ds = mgr.getDeviceInfo()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "a5d878e9",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"0: Monitor of Starship/Matisse HD Audio Controller Analog Stereo\n",
|
||||
"1: Starship/Matisse HD Audio Controller Analog Stereo\n",
|
||||
"2: Baffin HDMI/DP Audio [Radeon RX 550 640SP / RX 560/560X] Digital Stereo (HDMI)\n",
|
||||
"3: Monitor of Baffin HDMI/DP Audio [Radeon RX 550 640SP / RX 560/560X] Digital Stereo (HDMI)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Search for a device\n",
|
||||
"for i, d in enumerate(ds):\n",
|
||||
" print(f'{i}: ' + d.device_name)\n",
|
||||
"d = ds[0]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "6d5b281e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Create a configuration and enable some input channels\n",
|
||||
"config = lasp.DaqConfiguration(d)\n",
|
||||
"config.inchannel_config[0].enabled = True\n",
|
||||
"config.inchannel_config[1].enabled = True\n",
|
||||
"# Choose a different number of frames per block\n",
|
||||
"config.framesPerBlockIndex = 4"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "1ead3995",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Out channels: 0\n",
|
||||
"In channels: 2\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print('Out channels:',d.noutchannels)\n",
|
||||
"print('In channels:',d.ninchannels)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "12db8306",
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"2022-10-03 13:39:06+0200 \n",
|
||||
"2022-10-03 13:39:06+0200 Enter startStream (lasp_streammgr.cpp: 209)\n",
|
||||
"2022-10-03 13:39:06+0200 | Enter createDaq (lasp_daq.cpp: 18)\n",
|
||||
"2022-10-03 13:39:06+0200 | | Enter Daq (lasp_daq.cpp: 37)\n",
|
||||
"2022-10-03 13:39:06+0200 | | Leave Daq (lasp_daq.cpp)\n",
|
||||
"2022-10-03 13:39:06+0200 | | \n",
|
||||
"2022-10-03 13:39:06+0200 | | Enter RtAudioDaq (lasp_rtaudiodaq.cpp: 135)\n",
|
||||
"2022-10-03 13:39:06+0200 | | | Enter samplerate (lasp_daq.cpp: 58)\n",
|
||||
"2022-10-03 13:39:06+0200 | | | Leave samplerate (lasp_daq.cpp)\n",
|
||||
"2022-10-03 13:39:07+0200 | | Leave RtAudioDaq (lasp_rtaudiodaq.cpp)\n",
|
||||
"2022-10-03 13:39:07+0200 | Leave createDaq (lasp_daq.cpp)\n",
|
||||
"2022-10-03 13:39:07+0200 | isInput = true\n",
|
||||
"2022-10-03 13:39:07+0200 | isOutput = false\n",
|
||||
"2022-10-03 13:39:07+0200 | \n",
|
||||
"2022-10-03 13:39:07+0200 | Enter start (lasp_rtaudiodaq.cpp: 215)\n",
|
||||
"2022-10-03 13:39:07+0200 | Leave start (lasp_rtaudiodaq.cpp)\n",
|
||||
"2022-10-03 13:39:07+0200 Leave startStream (lasp_streammgr.cpp)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Start a stream with a configuration\n",
|
||||
"mgr.startStream(config)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "5323356f-4239-4f0b-ad58-0025501bf7a3",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"2022-10-03 13:39:09+0200 \n",
|
||||
"2022-10-03 13:39:09+0200 Enter InDataHandler (lasp_streammgr.cpp: 14)\n",
|
||||
"2022-10-03 13:39:09+0200 Leave InDataHandler (lasp_streammgr.cpp)\n",
|
||||
"2022-10-03 13:39:09+0200 \n",
|
||||
"2022-10-03 13:39:09+0200 Enter ThreadedInDataHandler (lasp_threadedindatahandler.cpp: 13)\n",
|
||||
"2022-10-03 13:39:09+0200 Leave ThreadedInDataHandler (lasp_threadedindatahandler.cpp)\n",
|
||||
"2022-10-03 13:39:09+0200 \n",
|
||||
"2022-10-03 13:39:09+0200 Enter PPMHandler (lasp_ppm.cpp: 14)\n",
|
||||
"2022-10-03 13:39:09+0200 | Enter start (lasp_streammgr.cpp: 16)\n",
|
||||
"2022-10-03 13:39:09+0200 | | Enter reset (lasp_ppm.cpp: 65)\n",
|
||||
"2022-10-03 13:39:09+0200 | | | Enter samplerate (lasp_daq.cpp: 58)\n",
|
||||
"2022-10-03 13:39:09+0200 | | | Leave samplerate (lasp_daq.cpp)\n",
|
||||
"2022-10-03 13:39:09+0200 | | | fs = 44100.000000\n",
|
||||
"2022-10-03 13:39:09+0200 | | Leave reset (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:09+0200 | Leave start (lasp_streammgr.cpp)\n",
|
||||
"2022-10-03 13:39:09+0200 Leave PPMHandler (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:09+0200 \n",
|
||||
"2022-10-03 13:39:09+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:09+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:09+0200 \n",
|
||||
"2022-10-03 13:39:09+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:09+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:09+0200 \n",
|
||||
"2022-10-03 13:39:09+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:09+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:10+0200 \n",
|
||||
"2022-10-03 13:39:10+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:10+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:10+0200 \n",
|
||||
"2022-10-03 13:39:10+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:10+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:10+0200 \n",
|
||||
"2022-10-03 13:39:10+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:10+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:10+0200 \n",
|
||||
"2022-10-03 13:39:10+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:10+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:11+0200 \n",
|
||||
"2022-10-03 13:39:11+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:11+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:11+0200 \n",
|
||||
"2022-10-03 13:39:11+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:11+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:11+0200 \n",
|
||||
"2022-10-03 13:39:11+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:11+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:11+0200 \n",
|
||||
"2022-10-03 13:39:11+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:11+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:11+0200 \n",
|
||||
"2022-10-03 13:39:11+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:11+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:12+0200 \n",
|
||||
"2022-10-03 13:39:12+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:12+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:12+0200 \n",
|
||||
"2022-10-03 13:39:12+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:12+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:12+0200 \n",
|
||||
"2022-10-03 13:39:12+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:12+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:12+0200 \n",
|
||||
"2022-10-03 13:39:12+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:12+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:12+0200 \n",
|
||||
"2022-10-03 13:39:12+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:12+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:13+0200 \n",
|
||||
"2022-10-03 13:39:13+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:13+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:13+0200 \n",
|
||||
"2022-10-03 13:39:13+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:13+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:13+0200 \n",
|
||||
"2022-10-03 13:39:13+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:13+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:13+0200 \n",
|
||||
"2022-10-03 13:39:13+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:13+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:14+0200 \n",
|
||||
"2022-10-03 13:39:14+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:14+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:14+0200 \n",
|
||||
"2022-10-03 13:39:14+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:14+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:14+0200 \n",
|
||||
"2022-10-03 13:39:14+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:14+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:14+0200 \n",
|
||||
"2022-10-03 13:39:14+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:14+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:14+0200 \n",
|
||||
"2022-10-03 13:39:14+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:14+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:15+0200 \n",
|
||||
"2022-10-03 13:39:15+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:15+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:15+0200 \n",
|
||||
"2022-10-03 13:39:15+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:15+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"ppm = lasp.PPMHandler(mgr)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "169c52c4-687f-4371-af04-19a414feb73d",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"ename": "NameError",
|
||||
"evalue": "name 'a' is not defined",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
|
||||
"\u001b[0;32m/tmp/ipykernel_24491/2167009006.py\u001b[0m in \u001b[0;36m<cell line: 1>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0ma\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
|
||||
"\u001b[0;31mNameError\u001b[0m: name 'a' is not defined"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"a"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "688d41ae-a3b1-4a31-b1e3-278bb4eaae4c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "4d9f0103",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Monitor of Starship/Matisse HD Audio Controller Analog Stereo\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"2022-10-03 13:39:15+0200 \n",
|
||||
"2022-10-03 13:39:15+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:15+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:15+0200 \n",
|
||||
"2022-10-03 13:39:15+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:15+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:15+0200 \n",
|
||||
"2022-10-03 13:39:15+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:15+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:16+0200 \n",
|
||||
"2022-10-03 13:39:16+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:16+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"daq = mgr.getDaq(lasp.StreamMgr.StreamType.input)\n",
|
||||
"print(daq)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"id": "26277ef0-3389-4581-9cc5-2c500e789dfa",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"2022-10-03 13:39:16+0200 \n",
|
||||
"2022-10-03 13:39:16+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:16+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ename": "RuntimeError",
|
||||
"evalue": "Mat::init(): requested size is not compatible with column vector layout",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[0;31mRuntimeError\u001b[0m Traceback (most recent call last)",
|
||||
"\u001b[0;32m/tmp/ipykernel_24664/1896157402.py\u001b[0m in \u001b[0;36m<cell line: 3>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msleep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mt\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;36m0.1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 6\u001b[0;31m \u001b[0mlevel\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mclip\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mppm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgetCurrentValue\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 7\u001b[0m \u001b[0;31m#print('\\r {\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
|
||||
"\u001b[0;31mRuntimeError\u001b[0m: Mat::init(): requested size is not compatible with column vector layout"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"2022-10-03 13:39:16+0200 \n",
|
||||
"2022-10-03 13:39:16+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:16+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:17+0200 \n",
|
||||
"2022-10-03 13:39:17+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:17+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:17+0200 \n",
|
||||
"2022-10-03 13:39:17+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:17+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:17+0200 \n",
|
||||
"2022-10-03 13:39:17+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:17+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:17+0200 \n",
|
||||
"2022-10-03 13:39:17+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:17+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:17+0200 \n",
|
||||
"2022-10-03 13:39:17+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:17+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:18+0200 \n",
|
||||
"2022-10-03 13:39:18+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:18+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:18+0200 \n",
|
||||
"2022-10-03 13:39:18+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:18+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:18+0200 \n",
|
||||
"2022-10-03 13:39:18+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:18+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:18+0200 \n",
|
||||
"2022-10-03 13:39:18+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:18+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:19+0200 \n",
|
||||
"2022-10-03 13:39:19+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:19+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:19+0200 \n",
|
||||
"2022-10-03 13:39:19+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:19+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:19+0200 \n",
|
||||
"2022-10-03 13:39:19+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:19+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:19+0200 \n",
|
||||
"2022-10-03 13:39:19+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:19+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:20+0200 \n",
|
||||
"2022-10-03 13:39:20+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:20+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:20+0200 \n",
|
||||
"2022-10-03 13:39:20+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:20+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:20+0200 \n",
|
||||
"2022-10-03 13:39:20+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:20+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:20+0200 \n",
|
||||
"2022-10-03 13:39:20+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:20+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:20+0200 \n",
|
||||
"2022-10-03 13:39:20+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:20+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:20+0200 \n",
|
||||
"2022-10-03 13:39:20+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:20+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:21+0200 \n",
|
||||
"2022-10-03 13:39:21+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:21+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:21+0200 \n",
|
||||
"2022-10-03 13:39:21+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:21+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:21+0200 \n",
|
||||
"2022-10-03 13:39:21+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:21+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:21+0200 \n",
|
||||
"2022-10-03 13:39:21+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:21+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:22+0200 \n",
|
||||
"2022-10-03 13:39:22+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:22+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:22+0200 \n",
|
||||
"2022-10-03 13:39:22+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:22+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:22+0200 \n",
|
||||
"2022-10-03 13:39:22+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:22+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:22+0200 \n",
|
||||
"2022-10-03 13:39:22+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:22+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:23+0200 \n",
|
||||
"2022-10-03 13:39:23+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:23+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:23+0200 \n",
|
||||
"2022-10-03 13:39:23+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:23+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:23+0200 \n",
|
||||
"2022-10-03 13:39:23+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:23+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:23+0200 \n",
|
||||
"2022-10-03 13:39:23+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:23+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:23+0200 \n",
|
||||
"2022-10-03 13:39:23+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:23+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:23+0200 \n",
|
||||
"2022-10-03 13:39:23+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:23+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:24+0200 \n",
|
||||
"2022-10-03 13:39:24+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:24+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:24+0200 \n",
|
||||
"2022-10-03 13:39:24+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:24+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:24+0200 \n",
|
||||
"2022-10-03 13:39:24+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:24+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:24+0200 \n",
|
||||
"2022-10-03 13:39:24+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:24+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:24+0200 \n",
|
||||
"2022-10-03 13:39:24+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:24+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:25+0200 \n",
|
||||
"2022-10-03 13:39:25+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:25+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:25+0200 \n",
|
||||
"2022-10-03 13:39:25+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:25+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:25+0200 \n",
|
||||
"2022-10-03 13:39:25+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:25+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:25+0200 \n",
|
||||
"2022-10-03 13:39:25+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:25+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:25+0200 \n",
|
||||
"2022-10-03 13:39:25+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:25+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:26+0200 \n",
|
||||
"2022-10-03 13:39:26+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:26+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:26+0200 \n",
|
||||
"2022-10-03 13:39:26+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:26+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:26+0200 \n",
|
||||
"2022-10-03 13:39:26+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:26+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:26+0200 \n",
|
||||
"2022-10-03 13:39:26+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:26+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:26+0200 \n",
|
||||
"2022-10-03 13:39:26+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:26+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:26+0200 \n",
|
||||
"2022-10-03 13:39:26+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:26+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:27+0200 \n",
|
||||
"2022-10-03 13:39:27+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:27+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:27+0200 \n",
|
||||
"2022-10-03 13:39:27+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:27+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:27+0200 \n",
|
||||
"2022-10-03 13:39:27+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:27+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n",
|
||||
"2022-10-03 13:39:27+0200 \n",
|
||||
"2022-10-03 13:39:27+0200 Enter inCallback_threaded (lasp_ppm.cpp: 18)\n",
|
||||
"2022-10-03 13:39:27+0200 Leave inCallback_threaded (lasp_ppm.cpp)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import time\n",
|
||||
"t=0\n",
|
||||
"while t < 1:\n",
|
||||
" time.sleep(0.1)\n",
|
||||
" t += 0.1\n",
|
||||
" level, clip = ppm.getCurrentValue()\n",
|
||||
" #print('\\r {"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"id": "30b77ce0-40ee-4b88-89fa-83b7a62e493c",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"2022-10-03 13:39:27+0200 \n",
|
||||
"2022-10-03 13:39:27+0200 Enter stopAllStreams (lasp_streammgr.cpp: 202)\n",
|
||||
"2022-10-03 13:39:27+0200 | Enter ~Daq (lasp_daq.cpp: 14)\n",
|
||||
"2022-10-03 13:39:27+0200 | Leave ~Daq (lasp_daq.cpp)\n",
|
||||
"2022-10-03 13:39:27+0200 Leave stopAllStreams (lasp_streammgr.cpp)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"mgr.stopAllStreams()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "78cc8353-0e0b-4d96-a263-f4ad3fe4ee4c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.10"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
506
examples/test_record.ipynb
Normal file
506
examples/test_record.ipynb
Normal file
@ -0,0 +1,506 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2406f830",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Test DAQ input on a device"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "e3d3280f",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"make: Entering directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"make[1]: Entering directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"[ 42%] Built target lasp_dsp_lib\n",
|
||||
"make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"[ 67%] Built target lasp_device_lib\n",
|
||||
"make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"[100%] Built target lasp_cpp\n",
|
||||
"make[1]: Leaving directory '/home/anne/wip/mycode/lasp'\n",
|
||||
"make: Leaving directory '/home/anne/wip/mycode/lasp'\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
" !make -j -C ~/wip/mycode/lasp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "d12f103c",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"2022-09-27 22:08:01+0200 DebugTrace-cpp 2.0.0a2 (g++ 12.2.0)\n",
|
||||
"2022-09-27 22:08:01+0200 \n",
|
||||
"2022-09-27 22:08:01+0200 Enter getInstance (lasp_streammgr.cpp: 40)\n",
|
||||
"2022-09-27 22:08:01+0200 | Enter StreamMgr (lasp_streammgr.cpp: 45)\n",
|
||||
"2022-09-27 22:08:01+0200 | | Enter rescanDAQDevices (lasp_streammgr.cpp: 60)\n",
|
||||
"2022-09-27 22:08:01+0200 | | | Enter rescanDAQDevices_impl (lasp_streammgr.cpp: 80)\n",
|
||||
"2022-09-27 22:08:01+0200 | | | | Enter fillRtAudioDeviceInfo (lasp_rtaudiodaq.cpp: 20)\n",
|
||||
"\n",
|
||||
"RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (hw:1,0), Device or resource busy.\n",
|
||||
"\n",
|
||||
"2022-09-27 22:08:01+0200 | | | | Leave fillRtAudioDeviceInfo (lasp_rtaudiodaq.cpp)\n",
|
||||
"2022-09-27 22:08:01+0200 | | | Leave rescanDAQDevices_impl (lasp_streammgr.cpp)\n",
|
||||
"2022-09-27 22:08:01+0200 | | Leave rescanDAQDevices (lasp_streammgr.cpp)\n",
|
||||
"2022-09-27 22:08:01+0200 | Leave StreamMgr (lasp_streammgr.cpp)\n",
|
||||
"2022-09-27 22:08:01+0200 Leave getInstance (lasp_streammgr.cpp)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import lasp\n",
|
||||
"# Get handle to stream manager\n",
|
||||
"mgr = lasp.StreamMgr.getInstance()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "f44b1946",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"ds = mgr.getDeviceInfo()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "db0f3bb7",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"0: Monitor of Starship/Matisse HD Audio Controller Analog Stereo\n",
|
||||
"1: Starship/Matisse HD Audio Controller Analog Stereo\n",
|
||||
"2: GP108 High Definition Audio Controller Digital Stereo (HDMI)\n",
|
||||
"3: Monitor of GP108 High Definition Audio Controller Digital Stereo (HDMI)\n",
|
||||
"4: default\n",
|
||||
"5: hw:HDA NVidia,3\n",
|
||||
"6: hw:HDA NVidia,7\n",
|
||||
"7: hw:HDA NVidia,8\n",
|
||||
"8: hw:HDA NVidia,9\n",
|
||||
"9: hw:HDA NVidia,10\n",
|
||||
"10: hw:HD-Audio Generic,0\n",
|
||||
"11: hw:HD-Audio Generic,1\n",
|
||||
"12: hw:HD-Audio Generic,2\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Search for a device\n",
|
||||
"for i, d in enumerate(ds):\n",
|
||||
" print(f'{i}: ' + d.device_name)\n",
|
||||
"d = ds[0]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "f97c13d5",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[512, 1024, 2048, 4096, 8192]"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Check the available block sizes\n",
|
||||
"d.availableFramesPerBlock"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "5c84d454",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Create a configuration and enable some input channels\n",
|
||||
"config = lasp.DaqConfiguration(d)\n",
|
||||
"config.inchannel_config[0].enabled = True\n",
|
||||
"config.inchannel_config[1].enabled = True\n",
|
||||
"# Choose a different number of frames per block\n",
|
||||
"config.framesPerBlockIndex = 4"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "a020f86f",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Out channels: 0\n",
|
||||
"In channels: 2\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print('Out channels:',d.noutchannels)\n",
|
||||
"print('In channels:',d.ninchannels)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "034b5f5e",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"2022-09-27 22:08:02+0200 \n",
|
||||
"2022-09-27 22:08:02+0200 Enter startStream (lasp_streammgr.cpp: 209)\n",
|
||||
"2022-09-27 22:08:02+0200 | Enter createDaq (lasp_daq.cpp: 18)\n",
|
||||
"2022-09-27 22:08:02+0200 | | Enter Daq (lasp_daq.cpp: 37)\n",
|
||||
"2022-09-27 22:08:02+0200 | | Leave Daq (lasp_daq.cpp)\n",
|
||||
"2022-09-27 22:08:02+0200 | | \n",
|
||||
"2022-09-27 22:08:02+0200 | | Enter RtAudioDaq (lasp_rtaudiodaq.cpp: 135)\n",
|
||||
"2022-09-27 22:08:02+0200 | | | Enter samplerate (lasp_daq.cpp: 58)\n",
|
||||
"2022-09-27 22:08:02+0200 | | | Leave samplerate (lasp_daq.cpp)\n",
|
||||
"2022-09-27 22:08:02+0200 | | Leave RtAudioDaq (lasp_rtaudiodaq.cpp)\n",
|
||||
"2022-09-27 22:08:02+0200 | Leave createDaq (lasp_daq.cpp)\n",
|
||||
"2022-09-27 22:08:02+0200 | isInput = true\n",
|
||||
"2022-09-27 22:08:02+0200 | isOutput = false\n",
|
||||
"2022-09-27 22:08:02+0200 | \n",
|
||||
"2022-09-27 22:08:02+0200 | Enter start (lasp_rtaudiodaq.cpp: 215)\n",
|
||||
"2022-09-27 22:08:02+0200 | Leave start (lasp_rtaudiodaq.cpp)\n",
|
||||
"2022-09-27 22:08:02+0200 Leave startStream (lasp_streammgr.cpp)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Start a stream with a configuration\n",
|
||||
"mgr.startStream(config)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "59326eda-f249-4a9b-9191-a784b01694b9",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"2022-09-27 22:08:02+0200 \n",
|
||||
"2022-09-27 22:08:02+0200 Enter InDataHandler (lasp_streammgr.cpp: 14)\n",
|
||||
"2022-09-27 22:08:02+0200 Leave InDataHandler (lasp_streammgr.cpp)\n",
|
||||
"2022-09-27 22:08:02+0200 \n",
|
||||
"2022-09-27 22:08:02+0200 Enter ThreadedInDataHandler (lasp_threadedindatahandler.cpp: 13)\n",
|
||||
"2022-09-27 22:08:02+0200 Leave ThreadedInDataHandler (lasp_threadedindatahandler.cpp)\n",
|
||||
"2022-09-27 22:08:02+0200 \n",
|
||||
"2022-09-27 22:08:02+0200 Enter PyIndataHandler (lasp_pyindatahandler.cpp: 78)\n",
|
||||
"2022-09-27 22:08:02+0200 | Enter start (lasp_streammgr.cpp: 16)\n",
|
||||
"2022-09-27 22:08:02+0200 | Leave start (lasp_streammgr.cpp)\n",
|
||||
"2022-09-27 22:08:02+0200 Leave PyIndataHandler (lasp_pyindatahandler.cpp)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"."
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"2022-09-27 22:08:02+0200 \n",
|
||||
"2022-09-27 22:08:02+0200 Enter samplerate (lasp_daq.cpp: 58)\n",
|
||||
"2022-09-27 22:08:02+0200 Leave samplerate (lasp_daq.cpp)\n",
|
||||
"2022-09-27 22:08:02+0200 \n",
|
||||
"2022-09-27 22:08:02+0200 Enter samplerate (lasp_daq.cpp: 58)\n",
|
||||
"2022-09-27 22:08:02+0200 Leave samplerate (lasp_daq.cpp)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
".....1....2.....3....4....5.....6....7.....8....9....10"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"2022-09-27 22:08:12+0200 \n",
|
||||
"2022-09-27 22:08:12+0200 Enter ~PyIndataHandler (lasp_pyindatahandler.cpp: 87)\n",
|
||||
"2022-09-27 22:08:12+0200 Leave ~PyIndataHandler (lasp_pyindatahandler.cpp)\n",
|
||||
"2022-09-27 22:08:12+0200 \n",
|
||||
"2022-09-27 22:08:12+0200 Enter ~ThreadedInDataHandler (lasp_threadedindatahandler.cpp: 38)\n",
|
||||
"2022-09-27 22:08:12+0200 Leave ~ThreadedInDataHandler (lasp_threadedindatahandler.cpp)\n",
|
||||
"2022-09-27 22:08:12+0200 \n",
|
||||
"2022-09-27 22:08:12+0200 Enter ~InDataHandler (lasp_streammgr.cpp: 27)\n",
|
||||
"2022-09-27 22:08:12+0200 Leave ~InDataHandler (lasp_streammgr.cpp)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"rec = lasp.Recording('test', mgr, 10.0)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "5defa804",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"2022-09-27 22:08:12+0200 \n",
|
||||
"2022-09-27 22:08:12+0200 Enter stopStream (lasp_streammgr.cpp: 287)\n",
|
||||
"2022-09-27 22:08:12+0200 | Enter ~Daq (lasp_daq.cpp: 14)\n",
|
||||
"2022-09-27 22:08:12+0200 | Leave ~Daq (lasp_daq.cpp)\n",
|
||||
"2022-09-27 22:08:12+0200 Leave stopStream (lasp_streammgr.cpp)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"mgr.stopStream(lasp.StreamMgr.StreamType.input)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "d12f8c4a",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"2022-09-27 22:08:12+0200 \n",
|
||||
"2022-09-27 22:08:12+0200 Enter stopAllStreams (lasp_streammgr.cpp: 202)\n",
|
||||
"2022-09-27 22:08:12+0200 Leave stopAllStreams (lasp_streammgr.cpp)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"mgr.stopAllStreams()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "83b1713b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def cb(data):\n",
|
||||
" # raise RuntimeError('hh')\n",
|
||||
" print(data.shape)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"id": "ff7c1e03",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"2022-09-27 22:08:12+0200 \n",
|
||||
"2022-09-27 22:08:12+0200 Enter InDataHandler (lasp_streammgr.cpp: 14)\n",
|
||||
"2022-09-27 22:08:12+0200 Leave InDataHandler (lasp_streammgr.cpp)\n",
|
||||
"2022-09-27 22:08:12+0200 \n",
|
||||
"2022-09-27 22:08:12+0200 Enter ThreadedInDataHandler (lasp_threadedindatahandler.cpp: 13)\n",
|
||||
"2022-09-27 22:08:12+0200 Leave ThreadedInDataHandler (lasp_threadedindatahandler.cpp)\n",
|
||||
"2022-09-27 22:08:12+0200 \n",
|
||||
"2022-09-27 22:08:12+0200 Enter PyIndataHandler (lasp_pyindatahandler.cpp: 78)\n",
|
||||
"2022-09-27 22:08:12+0200 | Enter start (lasp_streammgr.cpp: 16)\n",
|
||||
"2022-09-27 22:08:12+0200 | Leave start (lasp_streammgr.cpp)\n",
|
||||
"2022-09-27 22:08:12+0200 Leave PyIndataHandler (lasp_pyindatahandler.cpp)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"i = lasp.InDataHandler(mgr, cb)\n",
|
||||
"import time\n",
|
||||
"#time.sleep(4)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"id": "9ba54238",
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"2022-09-27 22:08:12+0200 \n",
|
||||
"2022-09-27 22:08:12+0200 Enter ~PyIndataHandler (lasp_pyindatahandler.cpp: 87)\n",
|
||||
"2022-09-27 22:08:12+0200 Leave ~PyIndataHandler (lasp_pyindatahandler.cpp)\n",
|
||||
"2022-09-27 22:08:12+0200 \n",
|
||||
"2022-09-27 22:08:12+0200 Enter ~ThreadedInDataHandler (lasp_threadedindatahandler.cpp: 38)\n",
|
||||
"2022-09-27 22:08:12+0200 Leave ~ThreadedInDataHandler (lasp_threadedindatahandler.cpp)\n",
|
||||
"2022-09-27 22:08:12+0200 \n",
|
||||
"2022-09-27 22:08:12+0200 Enter ~InDataHandler (lasp_streammgr.cpp: 27)\n",
|
||||
"2022-09-27 22:08:12+0200 Leave ~InDataHandler (lasp_streammgr.cpp)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"del i"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"id": "0f0edc36",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"numpy.ndarray"
|
||||
]
|
||||
},
|
||||
"execution_count": 15,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import numpy as np\n",
|
||||
"a = np.array([1,2,3])\n",
|
||||
"type(a)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"id": "ee546114-a879-4ebe-a6eb-e2354b2247fd",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"dtype('int64')"
|
||||
]
|
||||
},
|
||||
"execution_count": 16,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"a.dtype"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"id": "5d033d6d-3052-42ce-9441-faa917ece6a9",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from lasp import DaqChannel"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"id": "bb99ec99-b921-4fb2-bd79-87db8dfcdb03",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"d = DaqChannel()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"id": "ad67d273-45aa-4358-9405-de5924f087a2",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"execution_count": 19,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"d.qty.value"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "53ab5c8f-f904-4f9f-b591-6a5dd1581ddd",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.7"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
# We borrow Numpy's implementation for doing the Fast Fourier Transform.
|
||||
# This FFT code appears to be faster than KISSFFT.
|
||||
add_library(fftpack
|
||||
fftpack.c
|
||||
)
|
||||
# Ling fft to math
|
||||
target_link_libraries(fftpack m)
|
1501
fftpack/fftpack.c
1501
fftpack/fftpack.c
File diff suppressed because it is too large
Load Diff
@ -1,27 +0,0 @@
|
||||
/*
|
||||
* This file is part of tela the Tensor Language.
|
||||
* Copyright (c) 1994-1995 Pekka Janhunen
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#define NPY_VISIBILITY_HIDDEN
|
||||
|
||||
#ifdef LASP_DOUBLE_PRECISION
|
||||
#define Treal double
|
||||
#else
|
||||
#define Treal float
|
||||
#endif
|
||||
|
||||
extern NPY_VISIBILITY_HIDDEN void npy_cfftf(int N, Treal data[], const Treal wrk[]);
|
||||
extern NPY_VISIBILITY_HIDDEN void npy_cfftb(int N, Treal data[], const Treal wrk[]);
|
||||
extern NPY_VISIBILITY_HIDDEN void npy_cffti(int N, Treal wrk[]);
|
||||
|
||||
extern NPY_VISIBILITY_HIDDEN void npy_rfftf(int N, Treal data[], const Treal wrk[]);
|
||||
extern NPY_VISIBILITY_HIDDEN void npy_rfftb(int N, Treal data[], const Treal wrk[]);
|
||||
extern NPY_VISIBILITY_HIDDEN void npy_rffti(int N, Treal wrk[]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
BIN
img/LASP.pdf
BIN
img/LASP.pdf
Binary file not shown.
BIN
img/LASP_200px.png
Normal file
BIN
img/LASP_200px.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.4 KiB |
@ -1,24 +0,0 @@
|
||||
configure_file(config.pxi.in config.pxi)
|
||||
|
||||
# This is used for code completion in vim
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
set(CYTHON_EXECUTABLE "cython3")
|
||||
include(UseCython)
|
||||
include(FindNumpy)
|
||||
|
||||
include_directories(
|
||||
${PYTHON_NUMPY_INCLUDE_DIR}
|
||||
.
|
||||
c
|
||||
)
|
||||
add_subdirectory(c)
|
||||
add_subdirectory(device)
|
||||
|
||||
set_source_files_properties(wrappers.c PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS} ${CYTHON_EXTRA_C_FLAGS}")
|
||||
cython_add_module(wrappers wrappers.pyx)
|
||||
target_link_libraries(wrappers lasp_lib ${LASP_THREADING_LIBRARIES})
|
||||
if(win32)
|
||||
target_link_libraries(wrappers python${python_version_windll})
|
||||
endif(win32)
|
@ -1,14 +0,0 @@
|
||||
# Comments are what is imported, state of 6-8-2021
|
||||
|
||||
from .lasp_common import * # P_REF, FreqWeighting, TimeWeighting, getTime, getFreq, Qty, SIQtys, Window, lasp_shelve, this_lasp_shelve, W_REF, U_REF, I_REF, dBFS_REF, AvType
|
||||
from .lasp_avstream import * # StreamManager, ignoreSigInt, StreamStatus
|
||||
from .wrappers import * # AvPowerSpectra, SosFilterBank, FilterBank, Siggen, sweep_flag_forward, sweep_flag_backward, sweep_flag_linear, sweep_flag_exponential, load_fft_wisdom, store_fft_wisdom
|
||||
from .lasp_atomic import * # Atomic
|
||||
from .lasp_imptube import * # TwoMicImpedanceTube
|
||||
from .lasp_measurement import * # Measurement, scaleBlockSens
|
||||
from .lasp_octavefilter import * # FirOctaveFilterBank, FirThirdOctaveFilterBank, OverallFilterBank, SosOctaveFilterBank, SosThirdOctaveFilterBank
|
||||
from .lasp_slm import * # SLM, Dummy
|
||||
from .lasp_record import * # RecordStatus, Recording
|
||||
from .lasp_siggen import * # SignalType, NoiseType, SiggenMessage, SiggenData, Siggen
|
||||
from .lasp_weighcal import * # WeighCal
|
||||
from .tools import * # SmoothingType, smoothSpectralData, SmoothingWidth
|
@ -1,30 +0,0 @@
|
||||
configure_file(lasp_config.h.in lasp_config.h)
|
||||
if(!LASP_DEBUG)
|
||||
SET_SOURCE_FILES_PROPERTIES(lasp_sosfilterbank.c PROPERTIES COMPILE_FLAGS -O3)
|
||||
SET_SOURCE_FILES_PROPERTIES(lasp_slm.c PROPERTIES COMPILE_FLAGS -O3)
|
||||
SET_SOURCE_FILES_PROPERTIES(lasp_eq.c PROPERTIES COMPILE_FLAGS -O3)
|
||||
endif(!LASP_DEBUG)
|
||||
|
||||
add_library(lasp_lib
|
||||
lasp_fft.c
|
||||
lasp_mat.c
|
||||
lasp_math_raw.c
|
||||
lasp_alg.c
|
||||
lasp_assert.c
|
||||
lasp_tracer.c
|
||||
lasp_window.c
|
||||
lasp_aps.c
|
||||
lasp_ps.c
|
||||
lasp_mq.c
|
||||
lasp_siggen.c
|
||||
lasp_worker.c
|
||||
lasp_nprocs.c
|
||||
lasp_dfifo.c
|
||||
lasp_firfilterbank.c
|
||||
lasp_sosfilterbank.c
|
||||
lasp_decimation.c
|
||||
lasp_slm.c
|
||||
lasp_eq.c
|
||||
)
|
||||
|
||||
target_link_libraries(lasp_lib ${FFT_LIBRARIES} ${BLAS_LIBRARIES})
|
@ -1,128 +0,0 @@
|
||||
{0.000000000000000000e+00,
|
||||
-2.185738981122740943e-06,
|
||||
-2.794005403671438005e-06,
|
||||
9.474117030816240549e-06,
|
||||
4.047683164318276243e-05,
|
||||
8.159721257084205745e-05,
|
||||
1.081272986403995632e-04,
|
||||
8.819754747279720866e-05,
|
||||
1.292089375486244570e-18,
|
||||
-1.501119890529942897e-04,
|
||||
-3.184064926464186956e-04,
|
||||
-4.310893684346957895e-04,
|
||||
-4.094689334832080671e-04,
|
||||
-2.059289266182427725e-04,
|
||||
1.632175892397718904e-04,
|
||||
6.039349831684049600e-04,
|
||||
9.609848668279323634e-04,
|
||||
1.065386827404308026e-03,
|
||||
8.003236325878445483e-04,
|
||||
1.624425171981582980e-04,
|
||||
-7.076130570048387320e-04,
|
||||
-1.544654803296158230e-03,
|
||||
-2.031572983126828796e-03,
|
||||
-1.908002710205690703e-03,
|
||||
-1.080539502548179872e-03,
|
||||
3.071493710103571440e-04,
|
||||
1.878468916273352439e-03,
|
||||
3.114730098982527052e-03,
|
||||
3.515061533711236544e-03,
|
||||
2.779296151548805299e-03,
|
||||
9.515559570652546090e-04,
|
||||
-1.533803816637046430e-03,
|
||||
-3.936604506597823384e-03,
|
||||
-5.417051906137869584e-03,
|
||||
-5.310758368959681876e-03,
|
||||
-3.387308238052255758e-03,
|
||||
-2.020189585846376504e-17,
|
||||
3.941575036075541973e-03,
|
||||
7.193441582498504364e-03,
|
||||
8.546931560120834062e-03,
|
||||
7.242589074154118407e-03,
|
||||
3.295234447391589671e-03,
|
||||
-2.391580800508445910e-03,
|
||||
-8.190127435210622919e-03,
|
||||
-1.217754814426674249e-02,
|
||||
-1.272615168819673029e-02,
|
||||
-9.085393051846636994e-03,
|
||||
-1.766149937181874050e-03,
|
||||
7.423829658638344230e-03,
|
||||
1.575380997299343638e-02,
|
||||
2.029368152419728719e-02,
|
||||
1.881247426920701696e-02,
|
||||
1.060259142796622991e-02,
|
||||
-3.026237731635000611e-03,
|
||||
-1.877030360461057201e-02,
|
||||
-3.192876928283294724e-02,
|
||||
-3.747119479720732033e-02,
|
||||
-3.132999667548151679e-02,
|
||||
-1.158792905301173765e-02,
|
||||
2.076675004965774715e-02,
|
||||
6.175096553249778686e-02,
|
||||
1.050334155991766993e-01,
|
||||
1.431963133103141828e-01,
|
||||
1.693251638559906402e-01,
|
||||
1.785441122547395953e-01,
|
||||
1.691180120289761668e-01,
|
||||
1.428459416149487626e-01,
|
||||
1.046477658307891495e-01,
|
||||
6.144841153891738433e-02,
|
||||
2.063940827275027520e-02,
|
||||
-1.150252035146304662e-02,
|
||||
-3.106003822342199780e-02,
|
||||
-3.710127967927729503e-02,
|
||||
-3.157313813741360886e-02,
|
||||
-1.853722959527762462e-02,
|
||||
-2.984746593388511431e-03,
|
||||
1.044334023900628412e-02,
|
||||
1.850493210242899755e-02,
|
||||
1.993456916585750749e-02,
|
||||
1.545344532020422393e-02,
|
||||
7.271929013423104535e-03,
|
||||
-1.727500581384247688e-03,
|
||||
-8.873383043962101632e-03,
|
||||
-1.241029376178266405e-02,
|
||||
-1.185679908045850044e-02,
|
||||
-7.961640663049178793e-03,
|
||||
-2.321032949510159950e-03,
|
||||
3.192604628017779635e-03,
|
||||
7.004731963719125487e-03,
|
||||
8.251271964876608425e-03,
|
||||
6.931579083478471744e-03,
|
||||
3.790698415906206074e-03,
|
||||
-1.938928732007791559e-17,
|
||||
-3.244201578800122790e-03,
|
||||
-5.075193378062196545e-03,
|
||||
-5.164847959311490676e-03,
|
||||
-3.744259045513907962e-03,
|
||||
-1.455155744093486990e-03,
|
||||
9.003467998605640694e-04,
|
||||
2.622284972952067337e-03,
|
||||
3.306540383383136175e-03,
|
||||
2.920618605100662978e-03,
|
||||
1.755414747876200702e-03,
|
||||
2.859847735621343567e-04,
|
||||
-1.002153516667671955e-03,
|
||||
-1.762137666026810951e-03,
|
||||
-1.867706509456178859e-03,
|
||||
-1.413021131661008033e-03,
|
||||
-6.438025163091647607e-04,
|
||||
1.469135513812169238e-04,
|
||||
7.190488353391707912e-04,
|
||||
9.501796827231699877e-04,
|
||||
8.500266304384044257e-04,
|
||||
5.292428644636570966e-04,
|
||||
1.415165912868260117e-04,
|
||||
-1.763689026852747436e-04,
|
||||
-3.456944177857647653e-04,
|
||||
-3.578084944368887165e-04,
|
||||
-2.589144427740892114e-04,
|
||||
-1.190202164875697825e-04,
|
||||
9.922942950529402484e-19,
|
||||
6.497054701652780085e-05,
|
||||
7.525727688871557033e-05,
|
||||
5.231825144807937484e-05,
|
||||
2.280076622100770170e-05,
|
||||
4.215016359691300044e-06,
|
||||
-6.989289499831618075e-07,
|
||||
-0.000000000000000000e+00}
|
@ -1,223 +0,0 @@
|
||||
// lasp_alg.c
|
||||
//
|
||||
// Author: J.A. de Jong -ASCEE
|
||||
//
|
||||
// Description:
|
||||
// (Linear) algebra routine implementations
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#include "lasp_alg.h"
|
||||
|
||||
void cmv_dot(const cmat* A,const vc* restrict x,vc* restrict b){
|
||||
dbgassert(A && x && b,NULLPTRDEREF);
|
||||
assert_vx(b);
|
||||
assert_vx(x);
|
||||
dbgassert(A->n_cols == x->n_rows,SIZEINEQUAL);
|
||||
dbgassert(A->n_rows == b->n_rows,SIZEINEQUAL);
|
||||
|
||||
#if LASP_USE_BLAS == 1
|
||||
dbgassert(false,"Untested function. Is not functional for strides");
|
||||
/* typedef enum CBLAS_ORDER {CblasRowMajor=101, CblasColMajor=102} CBLAS_ORDER; */
|
||||
/* typedef enum CBLAS_TRANSPOSE {CblasNoTrans=111, CblasTrans=112, CblasConjTrans=113, CblasConjNoTrans=114} CBLAS_TRANSPOSE; */
|
||||
/*
|
||||
void cblas_zgemv(OPENBLAS_CONST enum CBLAS_ORDER order,
|
||||
OPENBLAS_CONST enum CBLAS_TRANSPOSE trans,
|
||||
OPENBLAS_CONST blasint m,
|
||||
OPENBLAS_CONST blasint n,
|
||||
OPENBLAS_CONST double *alpha,
|
||||
OPENBLAS_CONST double *a,
|
||||
OPENBLAS_CONST blasint lda,
|
||||
OPENBLAS_CONST double *x,
|
||||
OPENBLAS_CONST blasint incx,
|
||||
OPENBLAS_CONST double *beta,
|
||||
double *y,
|
||||
OPENBLAS_CONST blasint incy);
|
||||
*/
|
||||
c alpha = 1.0;
|
||||
c beta = 0.0;
|
||||
cblas_zgemv(CblasColMajor,
|
||||
CblasNoTrans,
|
||||
A->n_rows,
|
||||
A->n_cols,
|
||||
(d*) &alpha, /* alpha */
|
||||
(d*) A->_data, /* A */
|
||||
A->n_rows, /* lda */
|
||||
(d*) x->_data, /* */
|
||||
1,
|
||||
(d*) &beta, /* beta */
|
||||
(d*) b->_data,
|
||||
1);
|
||||
|
||||
|
||||
|
||||
#else
|
||||
size_t i,j;
|
||||
|
||||
vc_set(b,0.0);
|
||||
|
||||
iVARTRACE(20,A->n_cols);
|
||||
iVARTRACE(20,A->n_rows);
|
||||
|
||||
for(j=0;j<A->n_cols;j++){
|
||||
for(i=0;i<A->n_rows;i++) {
|
||||
|
||||
c* Aij = getcmatval(A,i,j);
|
||||
b->_data[i] += *Aij * *getvcval(x,j);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
/// The code below here is not yet worked on. Should be improved to
|
||||
/// directly couple to openblas, instead of using lapacke.h
|
||||
#if 0
|
||||
|
||||
/* These functions can be directly linked to openBLAS */
|
||||
#define lapack_complex_double double _Complex
|
||||
#define lapack_complex_float float _Complex
|
||||
|
||||
#define LAPACK_ROW_MAJOR 101
|
||||
#define LAPACK_COL_MAJOR 102
|
||||
|
||||
#define LAPACK_WORK_MEMORY_ERROR -1010
|
||||
#define LAPACK_TRANSPOSE_MEMORY_ERROR -1011
|
||||
|
||||
typedef int lapack_int;
|
||||
|
||||
int LAPACKE_cgelss( int matrix_layout, int m, int n,
|
||||
int nrhs, lapack_complex_float* a,
|
||||
int lda, lapack_complex_float* b,
|
||||
int ldb, float* s, float rcond,
|
||||
int* rank );
|
||||
int LAPACKE_zgelss( int matrix_layout, int m, int n,
|
||||
int nrhs, lapack_complex_double* a,
|
||||
int lda, lapack_complex_double* b,
|
||||
int ldb, double* s, double rcond,
|
||||
int* rank );
|
||||
|
||||
lapack_int LAPACKE_zgels( int matrix_layout, char trans, lapack_int m,
|
||||
lapack_int n, lapack_int nrhs,
|
||||
lapack_complex_double* a, lapack_int lda,
|
||||
lapack_complex_double* b, lapack_int ldb );
|
||||
|
||||
|
||||
|
||||
|
||||
#if LASP_FLOAT == 64
|
||||
|
||||
#define lapack_gelss LAPACKE_zgelss
|
||||
#define lapack_gels LAPACKE_zgels
|
||||
#else
|
||||
|
||||
#define lapack_gelss LAPACKE_cgelss
|
||||
#endif
|
||||
|
||||
#define max(a,b) ((a)>(b)?(a):(b))
|
||||
|
||||
|
||||
/* int lsq_solve(const cmat* A,const vc* b,vc* x){ */
|
||||
|
||||
/* int rv; */
|
||||
/* /\* M: number of rows of matrix *\/ */
|
||||
/* /\* N: Number of columns *\/ */
|
||||
/* /\* Norm: L2|b-A*x| *\/ */
|
||||
/* /\* NRHS: Number of right hand sides: Number of columns of matrix B *\/ */
|
||||
|
||||
/* assert(A->n_rows>=A->n_cols); */
|
||||
/* assert(x->size == A->n_cols); */
|
||||
/* assert(b->size == A->n_rows); */
|
||||
|
||||
/* int info; */
|
||||
|
||||
/* size_t lda = max(1,A->n_rows); */
|
||||
/* size_t ldb = max(lda,A->n_cols); */
|
||||
|
||||
/* /\* Make explicit copy of matrix A data, as it will be overwritten */
|
||||
/* * by lapack_gels *\/ */
|
||||
/* c* A_data = Pool_allocatec(&lsq_solve_pool,A->n_rows*A->n_cols); */
|
||||
/* c_copy(A_data,A->data,A->n_cols*A->n_rows); */
|
||||
|
||||
/* c* work_data = Pool_allocatec(&lsq_solve_pool,b->size); */
|
||||
/* c_copy(work_data,b->data,b->size); */
|
||||
|
||||
/* /\* Lapack documentation says: *\/ */
|
||||
/* /\* if TRANS = 'N' and m >= n, rows 1 to n of B contain the least */
|
||||
/* squares solution vectors; the residual sum of squares for the */
|
||||
/* solution in each column is given by the sum of squares of the */
|
||||
/* modulus of elements N+1 to M in that column; */
|
||||
/* *\/ */
|
||||
|
||||
|
||||
/* /\* We always assume one RHS column *\/ */
|
||||
/* const int nrhs = 1; */
|
||||
|
||||
/* /\* General Least Squares Solve *\/ */
|
||||
/* info = lapack_gels(LAPACK_COL_MAJOR, /\* Column-major ordering *\/ */
|
||||
/* 'N', */
|
||||
/* A->n_rows, /\* Number of rows in matrix *\/ */
|
||||
/* A->n_cols, /\* Number of columns *\/ */
|
||||
/* nrhs, /\* nrhs, which is number_mics *\/ */
|
||||
/* A_data, /\* The A-matrix *\/ */
|
||||
/* lda, /\* lda: the leading dimension of matrix A *\/ */
|
||||
/* work_data, /\* The b-matrix *\/ */
|
||||
/* ldb); /\* ldb: the leading dimension of b: max(1,M,N) *\/ */
|
||||
|
||||
/* if(info==0){ */
|
||||
/* c_copy(x->data,work_data,x->size); */
|
||||
/* rv = SUCCESS; */
|
||||
/* } */
|
||||
/* else { */
|
||||
/* memset(x->data,0,x->size); */
|
||||
/* WARN("LAPACK INFO VALUE"); */
|
||||
/* printf("%i\n", info ); */
|
||||
/* TRACE(15,"Solving least squares problem failed\n"); */
|
||||
|
||||
/* rv = FAILURE; */
|
||||
/* } */
|
||||
|
||||
/* return rv; */
|
||||
|
||||
/* } */
|
||||
|
||||
/* d c_normdiff(const cmat* A,const cmat* B) { */
|
||||
|
||||
/* TRACE(15,"c_normdif"); */
|
||||
|
||||
/* dbgassert(A->n_cols==B->n_cols,"Number of columns of A and B " */
|
||||
/* "should be equal"); */
|
||||
/* dbgassert(A->n_rows==B->n_rows,"Number of rows of A and B " */
|
||||
/* "should be equal"); */
|
||||
|
||||
/* size_t size = A->n_cols*A->n_rows; */
|
||||
|
||||
/* vc diff_temp = vc_al[MAX_MATRIX_SIZE]; */
|
||||
|
||||
/* c_copy(diff_temp,A->data,size); */
|
||||
|
||||
/* c alpha = -1.0; */
|
||||
|
||||
/* /\* This routine computes y <- alpha*x + beta*y *\/ */
|
||||
|
||||
|
||||
/* /\* void cblas_zaxpy(OPENBLAS_CONST blasint n, *\/ */
|
||||
/* /\* OPENBLAS_CONST double *alpha, *\/ */
|
||||
/* /\* OPENBLAS_CONST double *x, *\/ */
|
||||
/* /\* OPENBLAS_CONST blasint incx, *\/ */
|
||||
/* /\* double *y, *\/ */
|
||||
/* /\* OPENBLAS_CONST blasint incy); *\/ */
|
||||
|
||||
/* cblas_zaxpy(size, */
|
||||
/* (d*) &alpha, */
|
||||
/* (d*) B->data, */
|
||||
/* 1, */
|
||||
/* (d*) diff_temp, */
|
||||
/* 1 ); */
|
||||
|
||||
/* return c_norm(diff_temp,size); */
|
||||
/* } */
|
||||
#endif /* if 0 */
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,192 +0,0 @@
|
||||
// lasp_alg.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description:
|
||||
// (Linear) algebra routines on matrices and vectors
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef LASP_ALG_H
|
||||
#define LASP_ALG_H
|
||||
#include "lasp_config.h"
|
||||
#include "lasp_mat.h"
|
||||
|
||||
/**
|
||||
* Compute the dot product of two vectors of floats
|
||||
*
|
||||
* @param a First vector
|
||||
* @param b Second second vector
|
||||
* @return dot product as float
|
||||
*/
|
||||
static inline d vd_dot(const vd * a,const vd* b) {
|
||||
dbgassert(a && b,NULLPTRDEREF);
|
||||
assert_vx(a);
|
||||
assert_vx(b);
|
||||
dbgassert(a->n_rows == b->n_rows,SIZEINEQUAL);
|
||||
return d_dot(getvdval(a,0),getvdval(b,0),a->n_rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* y = fac * y
|
||||
*
|
||||
* @param y
|
||||
* @param fac scale factor
|
||||
*/
|
||||
static inline void dmat_scale(dmat* y,const c fac){
|
||||
dbgassert(y,NULLPTRDEREF);
|
||||
for(us col=0;col<y->n_cols;col++) {
|
||||
d_scale(getdmatval(y,0,col),fac,y->n_rows);
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* y = fac * y
|
||||
*
|
||||
* @param y
|
||||
* @param fac scale factor
|
||||
*/
|
||||
static inline void cmat_scale(cmat* y,const c fac){
|
||||
dbgassert(y,NULLPTRDEREF);
|
||||
dbgassert(y,NULLPTRDEREF);
|
||||
for(us col=0;col<y->n_cols;col++) {
|
||||
c_scale(getcmatval(y,0,col),fac,y->n_rows);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* x = x + fac*y
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @param fac
|
||||
*/
|
||||
static inline void dmat_add_dmat(dmat* x,dmat* y,d fac) {
|
||||
dbgassert(x && y,NULLPTRDEREF);
|
||||
dbgassert(x->n_cols == y->n_cols,SIZEINEQUAL);
|
||||
dbgassert(x->n_rows == y->n_rows,SIZEINEQUAL);
|
||||
for(us col=0;col<y->n_cols;col++) {
|
||||
d_add_to(getdmatval(x,0,col),
|
||||
getdmatval(y,0,col),fac,x->n_rows);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* x = x + fac*y
|
||||
*
|
||||
* @param[in,out] x
|
||||
* @param[in] y
|
||||
* @param[in] fac
|
||||
*/
|
||||
static inline void cmat_add_cmat(cmat* x,cmat* y,c fac) {
|
||||
dbgassert(x && y,NULLPTRDEREF);
|
||||
dbgassert(x->n_cols == y->n_cols,SIZEINEQUAL);
|
||||
dbgassert(x->n_rows == y->n_rows,SIZEINEQUAL);
|
||||
for(us col=0;col<y->n_cols;col++) {
|
||||
c_add_to(getcmatval(x,0,col),
|
||||
getcmatval(y,0,col),fac,x->n_rows);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the element-wise (Hadamard) product of a and b, and store
|
||||
* in result
|
||||
*
|
||||
* @param[out] result
|
||||
* @param[in] a
|
||||
* @param[in] b
|
||||
*/
|
||||
static inline void vd_elem_prod(vd* result,const vd* a,const vd* b) {
|
||||
dbgassert(result && a && b,NULLPTRDEREF);
|
||||
assert_equalsize(a,b);
|
||||
d_elem_prod_d(getvdval(result,0),
|
||||
getvdval(a,0),
|
||||
getvdval(b,0),a->n_rows);
|
||||
}
|
||||
/**
|
||||
* Compute the element-wise (Hadamard) product of a and b, and store
|
||||
* in result
|
||||
*
|
||||
* @param[out] result
|
||||
* @param[in] a
|
||||
* @param[in] b
|
||||
*/
|
||||
static inline void vc_hadamard(vc* result,const vc* a,const vc* b) {
|
||||
fsTRACE(15);
|
||||
dbgassert(result && a && b,NULLPTRDEREF);
|
||||
assert_equalsize(a,b);
|
||||
assert_equalsize(a,result);
|
||||
|
||||
c_hadamard(getvcval(result,0),
|
||||
getvcval(a,0),
|
||||
getvcval(b,0),
|
||||
a->n_rows);
|
||||
|
||||
check_overflow_vx(*result);
|
||||
check_overflow_vx(*a);
|
||||
check_overflow_vx(*b);
|
||||
feTRACE(15);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the element-wise (Hadamard) product of a and b, and store
|
||||
* in result
|
||||
*
|
||||
* @param[out] result
|
||||
* @param[in] a
|
||||
* @param[in] b
|
||||
*/
|
||||
static inline void cmat_hadamard(cmat* result,
|
||||
const cmat* a,
|
||||
const cmat* b) {
|
||||
fsTRACE(15);
|
||||
dbgassert(result && a && b,NULLPTRDEREF);
|
||||
|
||||
dbgassert(result->n_rows==a->n_rows,SIZEINEQUAL);
|
||||
dbgassert(result->n_cols==a->n_cols,SIZEINEQUAL);
|
||||
dbgassert(b->n_rows==a->n_rows,SIZEINEQUAL);
|
||||
dbgassert(b->n_cols==a->n_cols,SIZEINEQUAL);
|
||||
|
||||
for(us col=0;col<result->n_cols;col++) {
|
||||
c_hadamard(getcmatval(result,0,col),
|
||||
getcmatval(a,0,col),
|
||||
getcmatval(b,0,col),
|
||||
a->n_rows);
|
||||
}
|
||||
|
||||
feTRACE(15);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute the matrix vector product for complex-valued types: b = A*x.
|
||||
*
|
||||
* @param[in] A Matrix A
|
||||
* @param[in] x Vector x
|
||||
* @param[out] b Result of computation
|
||||
*/
|
||||
void cmv_dot(const cmat* A,
|
||||
const vc* restrict x,
|
||||
vc* restrict b);
|
||||
|
||||
int lsq_solve(const cmat* A,
|
||||
const vc* restrict b,
|
||||
vc* restrict x);
|
||||
|
||||
/**
|
||||
* Compute the norm of the difference of two complex matrices
|
||||
*
|
||||
* @param A
|
||||
* @param B
|
||||
*/
|
||||
d cmat_normdiff(const cmat* A,const cmat* B);
|
||||
|
||||
/**
|
||||
* Computes the Kronecker product of a kron b, stores result in result.
|
||||
*
|
||||
* @param a a
|
||||
* @param b b
|
||||
* @param result a kron b
|
||||
*/
|
||||
void kronecker_product(const cmat* a,const cmat* b,cmat* result);
|
||||
|
||||
#endif // LASP_ALG_H
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,33 +0,0 @@
|
||||
// lasp_alloc.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description:
|
||||
// memory allocation functions.
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef LASP_ALLOC_H
|
||||
#define LASP_ALLOC_H
|
||||
#include <malloc.h>
|
||||
#include "lasp_tracer.h"
|
||||
/**
|
||||
* Reserved words for memory allocation. Can be changed to something
|
||||
* else when required. For example for debugging purposes.
|
||||
*/
|
||||
static inline void* a_malloc(size_t nbytes) {
|
||||
#if LASP_DEBUG
|
||||
if(nbytes == 0) {
|
||||
FATAL("Tried to allocate 0 bytes");
|
||||
}
|
||||
#endif
|
||||
void* ptr = malloc(nbytes);
|
||||
if(!ptr) {
|
||||
FATAL("Memory allocation failed. Exiting");
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
#define a_free free
|
||||
#define a_realloc realloc
|
||||
|
||||
#endif // LASP_ALLOC_H
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,291 +0,0 @@
|
||||
// lasp_aps.c
|
||||
//
|
||||
// Author: J.A. de Jong -ASCEE
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#define TRACERPLUS (-5)
|
||||
#include "lasp_aps.h"
|
||||
#include "lasp_ps.h"
|
||||
#include "lasp_alg.h"
|
||||
#include "lasp_dfifo.h"
|
||||
|
||||
/* Multiplication factor for the maximum size of the fifo queue. This
|
||||
* factor is multiplied by nfft to obtain the maximum size of the
|
||||
* fifo. */
|
||||
#define FIFO_SIZE_MULT (5)
|
||||
|
||||
typedef struct AvPowerSpectra_s {
|
||||
us nfft, nchannels;
|
||||
us overlap; /* Number of samples to overlap */
|
||||
|
||||
us naverages; /* Counter that counts the number of
|
||||
* averages taken for the computation
|
||||
* of this averaged power spectra. */
|
||||
|
||||
dmat buffer; /**< Buffer storage of some of the
|
||||
* previous samples. Number of rows is
|
||||
* equal to nfft. */
|
||||
|
||||
|
||||
dFifo* fifo; /* Sample fifo storage */
|
||||
|
||||
cmat* ps_storage; /**< Here we store the averaged
|
||||
* results for each Cross-power
|
||||
* spectra computed so far. */
|
||||
|
||||
cmat ps_result;
|
||||
|
||||
cmat ps_single; /**< This is the work area for a
|
||||
* PowerSpectra computation on a
|
||||
* single block */
|
||||
|
||||
|
||||
vd weighting; /**< This array stores the time weighting
|
||||
* coefficients for a running
|
||||
* spectrogram. The vector length is
|
||||
* zero for a full averaged (not
|
||||
* running spectrogram). */
|
||||
|
||||
us oldest_block; /**< Index of oldest block in
|
||||
* Spectrogram mode */
|
||||
|
||||
|
||||
PowerSpectra* ps; /**< Pointer to underlying
|
||||
* PowerSpectra calculator. */
|
||||
|
||||
} AvPowerSpectra;
|
||||
|
||||
void AvPowerSpectra_free(AvPowerSpectra* aps) {
|
||||
fsTRACE(15);
|
||||
|
||||
PowerSpectra_free(aps->ps);
|
||||
dFifo_free(aps->fifo);
|
||||
dmat_free(&aps->buffer);
|
||||
|
||||
us nweight = aps->weighting.n_rows;
|
||||
if(nweight > 0) {
|
||||
for(us blockno = 0; blockno < nweight; blockno++) {
|
||||
cmat_free(&aps->ps_storage[blockno]);
|
||||
}
|
||||
a_free(aps->ps_storage);
|
||||
vd_free(&aps->weighting);
|
||||
}
|
||||
|
||||
cmat_free(&aps->ps_single);
|
||||
cmat_free(&aps->ps_result);
|
||||
a_free(aps);
|
||||
|
||||
feTRACE(15);
|
||||
}
|
||||
|
||||
AvPowerSpectra* AvPowerSpectra_alloc(const us nfft,
|
||||
const us nchannels,
|
||||
const d overlap_percentage,
|
||||
const WindowType wt,
|
||||
const vd* weighting) {
|
||||
|
||||
fsTRACE(15);
|
||||
|
||||
/* Check nfft */
|
||||
if(nfft==0 || nfft % 2 != 0 || nfft > LASP_MAX_NFFT) {
|
||||
WARN("Invalid nfft");
|
||||
feTRACE(15);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check overlap percentage */
|
||||
if(overlap_percentage < 0. || overlap_percentage >= 100.) {
|
||||
WARN("Invalid overlap percentage");
|
||||
feTRACE(15);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Compute and check overlap offset */
|
||||
us overlap = (us) (overlap_percentage*((d) nfft)/100);
|
||||
if(overlap == nfft) {
|
||||
WARN("Overlap percentage results in full overlap, decreasing overlap.");
|
||||
overlap--;
|
||||
}
|
||||
|
||||
PowerSpectra* ps = PowerSpectra_alloc(nfft,wt);
|
||||
if(!ps) {
|
||||
WARN(ALLOCFAILED "ps");
|
||||
feTRACE(15);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AvPowerSpectra* aps = a_malloc(sizeof(AvPowerSpectra));
|
||||
|
||||
aps->nchannels = nchannels;
|
||||
aps->nfft = nfft;
|
||||
aps->ps = ps;
|
||||
aps->naverages = 0;
|
||||
aps->overlap = overlap;
|
||||
aps->buffer = dmat_alloc(nfft,nchannels);
|
||||
aps->oldest_block = 0;
|
||||
if(weighting) {
|
||||
|
||||
us nweight = weighting->n_rows;
|
||||
iVARTRACE(15,nweight);
|
||||
/* Allocate vectors and matrices */
|
||||
aps->ps_storage = a_malloc(nweight*sizeof(cmat));
|
||||
for(us blockno = 0; blockno < nweight; blockno++) {
|
||||
aps->ps_storage[blockno] = cmat_alloc(nfft/2+1,nchannels*nchannels);
|
||||
cmat_set(&aps->ps_storage[blockno],0);
|
||||
}
|
||||
|
||||
/* Allocate space and copy weighting coefficients */
|
||||
aps->weighting = vd_alloc(weighting->n_rows);
|
||||
vd_copy(&aps->weighting,weighting);
|
||||
}
|
||||
else {
|
||||
TRACE(15,"no weighting");
|
||||
aps->weighting.n_rows = 0;
|
||||
}
|
||||
|
||||
aps->ps_result = cmat_alloc(nfft/2+1,nchannels*nchannels);
|
||||
aps->ps_single = cmat_alloc(nfft/2+1,nchannels*nchannels);
|
||||
cmat_set(&aps->ps_result,0);
|
||||
|
||||
aps->fifo = dFifo_create(nchannels,FIFO_SIZE_MULT*nfft);
|
||||
|
||||
feTRACE(15);
|
||||
return aps;
|
||||
}
|
||||
|
||||
|
||||
us AvPowerSpectra_getAverages(const AvPowerSpectra* ps) {
|
||||
return ps->naverages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that adds a block of time data to the APS
|
||||
*
|
||||
* @param aps AvPowerSpectra handle
|
||||
* @param block Time data block. Size should be exactly nfft*nchannels.
|
||||
*/
|
||||
static void AvPowerSpectra_addBlock(AvPowerSpectra* aps,
|
||||
const dmat* block) {
|
||||
fsTRACE(15);
|
||||
|
||||
dbgassert(aps && block,NULLPTRDEREF);
|
||||
dbgassert(block->n_rows == aps->nfft,"Invalid block n_rows");
|
||||
dbgassert(block->n_cols == aps->nchannels,"Invalid block n_cols");
|
||||
|
||||
const us nfft = aps->nfft;
|
||||
iVARTRACE(15,nfft);
|
||||
|
||||
cmat* ps_single = &aps->ps_single;
|
||||
cmat* ps_storage = aps->ps_storage;
|
||||
cmat* ps_result = &aps->ps_result;
|
||||
|
||||
PowerSpectra_compute(aps->ps,
|
||||
block,
|
||||
ps_single);
|
||||
|
||||
vd weighting = aps->weighting;
|
||||
us nweight = weighting.n_rows;
|
||||
|
||||
if(nweight == 0) {
|
||||
|
||||
/* Overall mode */
|
||||
c naverages = (++aps->naverages);
|
||||
|
||||
/* Scale previous result */
|
||||
cmat_scale(ps_result,
|
||||
(naverages-1)/naverages);
|
||||
|
||||
/* Add new result, scaled properly */
|
||||
cmat_add_cmat(ps_result,
|
||||
ps_single,1/naverages);
|
||||
|
||||
}
|
||||
else {
|
||||
cmat_set(ps_result,0);
|
||||
|
||||
us* oldest_block = &aps->oldest_block;
|
||||
/* uVARTRACE(20,*oldest_block); */
|
||||
|
||||
cmat_copy(&ps_storage[*oldest_block],ps_single);
|
||||
|
||||
uVARTRACE(16,*oldest_block);
|
||||
if(aps->naverages < nweight) {
|
||||
++(aps->naverages);
|
||||
}
|
||||
|
||||
/* Update pointer to oldest block */
|
||||
(*oldest_block)++;
|
||||
*oldest_block %= nweight;
|
||||
|
||||
us block_index_weight = *oldest_block;
|
||||
|
||||
for(us block = 0; block < aps->naverages; block++) {
|
||||
/* Add new result, scaled properly */
|
||||
|
||||
c weight_fac = *getvdval(&weighting,
|
||||
aps->naverages-1-block);
|
||||
|
||||
/* cVARTRACE(20,weight_fac); */
|
||||
cmat_add_cmat(ps_result,
|
||||
&ps_storage[block_index_weight],
|
||||
weight_fac);
|
||||
|
||||
block_index_weight++;
|
||||
block_index_weight %= nweight;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
feTRACE(15);
|
||||
}
|
||||
|
||||
|
||||
cmat* AvPowerSpectra_addTimeData(AvPowerSpectra* aps,
|
||||
const dmat* timedata) {
|
||||
|
||||
fsTRACE(15);
|
||||
|
||||
dbgassert(aps && timedata,NULLPTRDEREF);
|
||||
const us nchannels = aps->nchannels;
|
||||
const us nfft = aps->nfft;
|
||||
|
||||
dbgassert(timedata->n_cols == nchannels,"Invalid time data");
|
||||
dbgassert(timedata->n_rows > 0,"Invalid time data. "
|
||||
"Should at least have one row");
|
||||
|
||||
/* dFifo handle */
|
||||
dFifo* fifo = aps->fifo;
|
||||
dFifo_push(fifo,timedata);
|
||||
|
||||
/* Temporary storage buffer */
|
||||
dmat* buffer = &aps->buffer;
|
||||
|
||||
/* Pop samples from the fifo while there are still at
|
||||
* least nfft samples available */
|
||||
while (dFifo_size(fifo) >= nfft) {
|
||||
int popped = dFifo_pop(fifo,
|
||||
buffer,
|
||||
aps->overlap); /* Keep 'overlap'
|
||||
* number of samples
|
||||
* in the queue */
|
||||
|
||||
dbgassert((us) popped == nfft,"Bug in dFifo");
|
||||
/* Process the block of time data */
|
||||
AvPowerSpectra_addBlock(aps,buffer);
|
||||
|
||||
}
|
||||
|
||||
feTRACE(15);
|
||||
return &aps->ps_result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,85 +0,0 @@
|
||||
// aps.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef LASP_APS_H
|
||||
#define LASP_APS_H
|
||||
#include "lasp_types.h"
|
||||
#include "lasp_mat.h"
|
||||
#include "lasp_window.h"
|
||||
|
||||
typedef enum {
|
||||
Linear=0,
|
||||
Exponential=1
|
||||
} TimeWeighting;
|
||||
|
||||
|
||||
typedef struct AvPowerSpectra_s AvPowerSpectra;
|
||||
|
||||
/**
|
||||
* Allocates a AvPowerSpectra object which is used to compute an
|
||||
* averaged power spectra from given input time samples.
|
||||
*
|
||||
* @param[in] nfft Size of the fft,
|
||||
*
|
||||
* @param[in] nchannels Number of channels of time data to allocate
|
||||
* for.
|
||||
*
|
||||
* @param[in] overlap_percentage. Should be 0<= overlap_pct < 100. The
|
||||
* overlap percentage will be coerced, as the offset will be an
|
||||
* integer number of samples at all cases. Therefore, the real overlap
|
||||
* percentage can be obtained later on
|
||||
*
|
||||
* @param wt
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
AvPowerSpectra* AvPowerSpectra_alloc(const us nfft,
|
||||
const us nchannels,
|
||||
const d overlap_percentage,
|
||||
const WindowType wt,
|
||||
const vd* spectrogram_weighting);
|
||||
|
||||
|
||||
/**
|
||||
* Computes the real overlap percentage, from the integer overlap
|
||||
* offset.
|
||||
*
|
||||
* @param aps
|
||||
*/
|
||||
d AvPowerSpectra_realoverlap_pct(const AvPowerSpectra* aps);
|
||||
|
||||
/**
|
||||
* Return the current number of averages taken to obtain the result.
|
||||
*
|
||||
* @return The number of averages taken.
|
||||
*/
|
||||
us AvPowerSpectra_getAverages(const AvPowerSpectra*);
|
||||
|
||||
/**
|
||||
* Add time data to this Averaged Power Spectra. Remembers the part
|
||||
* that should be overlapped with the next time data.
|
||||
*
|
||||
* @param aps AvPowerSpectra handle
|
||||
*
|
||||
* @param timedata Pointer to timedata buffer. Number of rows SHOULD
|
||||
* *at least* be nfft. Number of columns should exactly be nchannels.
|
||||
*
|
||||
* @return pointer to the averaged power spectra buffer that is being
|
||||
* written to. Pointer is valid until AvPowerSpectra_free() is called.
|
||||
*/
|
||||
cmat* AvPowerSpectra_addTimeData(AvPowerSpectra* aps,
|
||||
const dmat* timedata);
|
||||
|
||||
/**
|
||||
* Free storage of the AvPowerSpectra
|
||||
*/
|
||||
void AvPowerSpectra_free(AvPowerSpectra*);
|
||||
|
||||
|
||||
#endif // LASP_APS_H
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,29 +0,0 @@
|
||||
// lasp_assert.c
|
||||
//
|
||||
// Author: J.A. de Jong -ASCEE
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#include "lasp_config.h"
|
||||
#include "lasp_assert.h"
|
||||
|
||||
#ifdef LASP_DEBUG
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "lasp_types.h"
|
||||
#define MAX_MSG 200
|
||||
|
||||
void DBG_AssertFailedExtImplementation(const char* filename,
|
||||
us linenr,
|
||||
const char* extendedinfo)
|
||||
{
|
||||
char scratchpad[MAX_MSG];
|
||||
|
||||
sprintf(scratchpad,"ASSERT: file %s line %lu: (%s)\n",
|
||||
filename, linenr, extendedinfo);
|
||||
printf("%s\n", scratchpad);
|
||||
abort();
|
||||
}
|
||||
|
||||
#endif
|
@ -1,44 +0,0 @@
|
||||
// ascee_assert.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description:
|
||||
// Basic tools for debugging using assert statements including text.
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef LASP_ASSERT_H
|
||||
#define LASP_ASSERT_H
|
||||
|
||||
#define OUTOFBOUNDSMATR "Out of bounds access on matrix row"
|
||||
#define OUTOFBOUNDSMATC "Out of bounds access on matrix column"
|
||||
#define OUTOFBOUNDSVEC "Out of bounds access on vector"
|
||||
#define SIZEINEQUAL "Array sizes not equal"
|
||||
#define ALLOCFAILED "Memory allocation failure in: "
|
||||
#define NULLPTRDEREF "Null pointer dereference in: "
|
||||
|
||||
#ifdef LASP_DEBUG
|
||||
#include "lasp_types.h"
|
||||
|
||||
|
||||
void DBG_AssertFailedExtImplementation(const char* file,
|
||||
const us line,
|
||||
const char* string);
|
||||
|
||||
#define dbgassert(assertion, assert_string) \
|
||||
if (!(assertion)) \
|
||||
{ \
|
||||
DBG_AssertFailedExtImplementation(__FILE__, __LINE__, assert_string ); \
|
||||
}
|
||||
|
||||
#define assertvalidptr(ptr) dbgassert(ptr,NULLPTRDEREF)
|
||||
|
||||
#else // LASP_DEBUG not defined
|
||||
|
||||
#define dbgassert(assertion, assert_string)
|
||||
|
||||
#define assertvalidptr(ptr) dbgassert(ptr,NULLPTRDEREF)
|
||||
|
||||
#endif // LASP_DEBUG
|
||||
|
||||
#endif // LASP_ASSERT_H
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,20 +0,0 @@
|
||||
// bem.c
|
||||
//
|
||||
// Author: J.A. de Jong -ASCEE
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#include "types.h"
|
||||
|
||||
typedef struct {
|
||||
umat elem;
|
||||
vd nodes;
|
||||
ElemType elemtype;
|
||||
} BemMesh;
|
||||
|
||||
int Ms_osc_free(BemMesh* mesh,cmat* Ms) {
|
||||
|
||||
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,221 +0,0 @@
|
||||
// lasp_decimation.c
|
||||
//
|
||||
// Author: J.A. de Jong -ASCEE
|
||||
//
|
||||
// Description:
|
||||
// Implementation of the decimator
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#include "lasp_decimation.h"
|
||||
#include "lasp_firfilterbank.h"
|
||||
#include "lasp_tracer.h"
|
||||
#include "lasp_alloc.h"
|
||||
#include "lasp_dfifo.h"
|
||||
|
||||
// The Maximum number of taps in a decimation filter
|
||||
#define DEC_FILTER_MAX_TAPS (128)
|
||||
|
||||
// The FFT length
|
||||
#define DEC_FFT_LEN (1024)
|
||||
|
||||
typedef struct {
|
||||
DEC_FAC df;
|
||||
us dec_fac;
|
||||
us ntaps;
|
||||
d h[DEC_FILTER_MAX_TAPS];
|
||||
} DecFilter;
|
||||
|
||||
static __thread DecFilter DecFilters[] = {
|
||||
{DEC_FAC_4,4,128,
|
||||
#include "dec_filter_4.c"
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
typedef struct Decimator_s {
|
||||
us nchannels;
|
||||
us dec_fac;
|
||||
Firfilterbank** fbs;
|
||||
dFifo* output_fifo;
|
||||
} Decimator;
|
||||
|
||||
|
||||
Decimator* Decimator_create(us nchannels,DEC_FAC df) {
|
||||
fsTRACE(15);
|
||||
|
||||
/* Find the filter */
|
||||
const us nfilters = sizeof(DecFilters)/sizeof(DecFilter);
|
||||
DecFilter* filter = DecFilters;
|
||||
bool found = false;
|
||||
for(us filterno = 0;filterno < nfilters; filterno++) {
|
||||
if(filter->df == df) {
|
||||
TRACE(15,"Found filter");
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
filter++;
|
||||
}
|
||||
if(!found) {
|
||||
WARN("Decimation factor not found in list of filters");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create the filterbanks */
|
||||
Decimator* dec = a_malloc(sizeof(Decimator));
|
||||
dec->fbs = a_malloc(sizeof(Firfilterbank*)*nchannels);
|
||||
dec->nchannels = nchannels;
|
||||
dec->dec_fac = filter->dec_fac;
|
||||
|
||||
dmat h = dmat_foreign_data(filter->ntaps,1,filter->h,false);
|
||||
|
||||
for(us channelno=0;channelno<nchannels;channelno++) {
|
||||
dec->fbs[channelno] = Firfilterbank_create(&h,DEC_FFT_LEN);
|
||||
}
|
||||
|
||||
dmat_free(&h);
|
||||
|
||||
/* Create input and output fifo's */
|
||||
dec->output_fifo = dFifo_create(nchannels,DEC_FFT_LEN);
|
||||
|
||||
feTRACE(15);
|
||||
return dec;
|
||||
}
|
||||
|
||||
static void lasp_downsample(dmat* downsampled,
|
||||
const dmat* filtered,
|
||||
us dec_fac) {
|
||||
fsTRACE(15);
|
||||
dbgassert(downsampled && filtered,NULLPTRDEREF);
|
||||
dbgassert(filtered->n_rows/dec_fac == downsampled->n_rows,
|
||||
"Incompatible number of rows");
|
||||
dbgassert(downsampled->n_cols == filtered->n_cols,
|
||||
"Incompatible number of rows");
|
||||
|
||||
dbgassert(dec_fac> 0,"Invalid dec_fac");
|
||||
|
||||
for(us col=0;col<downsampled->n_cols;col++) {
|
||||
/* Low-level BLAS copy. */
|
||||
d_copy(getdmatval(downsampled,0,col),
|
||||
getdmatval(filtered,0,col),
|
||||
downsampled->n_rows, /* number */
|
||||
1, /* incx out */
|
||||
dec_fac); /* incx in */
|
||||
}
|
||||
|
||||
check_overflow_xmat(*downsampled);
|
||||
check_overflow_xmat(*filtered);
|
||||
|
||||
feTRACE(15);
|
||||
}
|
||||
|
||||
dmat Decimator_decimate(Decimator* dec,const dmat* samples) {
|
||||
|
||||
fsTRACE(15);
|
||||
dbgassert(dec && samples,NULLPTRDEREF);
|
||||
const us nchannels = dec->nchannels;
|
||||
const us dec_fac = dec->dec_fac;
|
||||
dbgassert(samples->n_cols == nchannels,"Invalid number of "
|
||||
"channels in samples");
|
||||
dbgassert(samples->n_rows > 0,"Number of rows should be >0")
|
||||
|
||||
/* Not downsampled, but filtered result */
|
||||
dmat filtered;
|
||||
|
||||
/* Filter each channel and store result in filtered. In first
|
||||
* iteration the right size for filtered is allocated. */
|
||||
|
||||
for(us chan=0;chan<nchannels;chan++) {
|
||||
vd samples_channel = dmat_column((dmat*)samples,
|
||||
chan);
|
||||
|
||||
/* Low-pass filter stuff */
|
||||
dmat filtered_res = Firfilterbank_filter(dec->fbs[chan],
|
||||
&samples_channel);
|
||||
|
||||
dbgassert(filtered_res.n_cols == 1,"Bug in Firfilterbank");
|
||||
|
||||
vd_free(&samples_channel);
|
||||
|
||||
if(filtered_res.n_rows > 0) {
|
||||
if(chan==0) {
|
||||
/* Allocate space for result */
|
||||
filtered = dmat_alloc(filtered_res.n_rows,
|
||||
nchannels);
|
||||
|
||||
}
|
||||
dmat filtered_col = dmat_submat(&filtered,
|
||||
0,chan,
|
||||
filtered_res.n_rows,
|
||||
1);
|
||||
|
||||
dbgassert(filtered_res.n_rows == filtered_col.n_rows,
|
||||
"Not all Firfilterbank's have same output number"
|
||||
" of rows!");
|
||||
|
||||
dmat_copy_rows(&filtered_col,
|
||||
&filtered_res,
|
||||
0,0,filtered_res.n_rows);
|
||||
|
||||
dmat_free(&filtered_res);
|
||||
dmat_free(&filtered_col);
|
||||
}
|
||||
else {
|
||||
filtered = dmat_alloc(0, nchannels);
|
||||
}
|
||||
}
|
||||
if(filtered.n_rows > 0) {
|
||||
/* Push filtered result into output fifo */
|
||||
dFifo_push(dec->output_fifo,
|
||||
&filtered);
|
||||
}
|
||||
dmat_free(&filtered);
|
||||
|
||||
|
||||
/* Now, downsample stuff */
|
||||
dmat downsampled;
|
||||
uVARTRACE(15,dec_fac);
|
||||
us fifo_size = dFifo_size(dec->output_fifo);
|
||||
if((fifo_size / dec_fac) > 0) {
|
||||
|
||||
filtered = dmat_alloc((fifo_size/dec_fac)*dec_fac,
|
||||
nchannels);
|
||||
|
||||
int nsamples = dFifo_pop(dec->output_fifo,
|
||||
&filtered,0);
|
||||
|
||||
dbgassert((us) nsamples % dec_fac == 0 && nsamples > 0,
|
||||
"BUG");
|
||||
dbgassert((us) nsamples == (fifo_size/dec_fac)*dec_fac,"BUG");
|
||||
|
||||
downsampled = dmat_alloc(nsamples/dec_fac,nchannels);
|
||||
/* Do the downsampling work */
|
||||
lasp_downsample(&downsampled,&filtered,dec_fac);
|
||||
|
||||
dmat_free(&filtered);
|
||||
}
|
||||
else {
|
||||
TRACE(15,"Empty downsampled");
|
||||
downsampled = dmat_alloc(0,0);
|
||||
}
|
||||
|
||||
feTRACE(15);
|
||||
/* return filtered; */
|
||||
return downsampled;
|
||||
}
|
||||
|
||||
|
||||
void Decimator_free(Decimator* dec) {
|
||||
fsTRACE(15);
|
||||
dbgassert(dec,NULLPTRDEREF);
|
||||
dFifo_free(dec->output_fifo);
|
||||
|
||||
for(us chan=0;chan<dec->nchannels;chan++) {
|
||||
Firfilterbank_free(dec->fbs[chan]);
|
||||
}
|
||||
|
||||
a_free(dec->fbs);
|
||||
a_free(dec);
|
||||
|
||||
feTRACE(15);
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -1,54 +0,0 @@
|
||||
// lasp_decimation.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description: Sample decimator, works on a whole block of
|
||||
// (uninterleaved) time samples at once. Decimates (downsamples and
|
||||
// low-pass filters by a factor given integer factor.
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef LASP_DECIMATION_H
|
||||
#define LASP_DECIMATION_H
|
||||
#include "lasp_types.h"
|
||||
#include "lasp_mat.h"
|
||||
|
||||
typedef struct Decimator_s Decimator;
|
||||
|
||||
typedef enum DEC_FAC_e {
|
||||
DEC_FAC_4 = 0, // Decimate by a factor of 4
|
||||
} DEC_FAC;
|
||||
|
||||
/**
|
||||
* Create a decimation filter for a given number of channels and
|
||||
* decimation factor
|
||||
*
|
||||
* @param nchannels Number of channels
|
||||
|
||||
* @param d Decimation factor. Should be one of the implemented
|
||||
* ones. (TODO: add more. At this point we have only a decimation
|
||||
* factor of 4 implemented)
|
||||
*
|
||||
* @return Decimator handle. NULL on error
|
||||
*/
|
||||
Decimator* Decimator_create(us nchannels,DEC_FAC d);
|
||||
|
||||
/**
|
||||
* Decimate given samples
|
||||
*
|
||||
* @param samples
|
||||
*
|
||||
* @return Decimated samples. Can be an empty array.
|
||||
*/
|
||||
dmat Decimator_decimate(Decimator* dec,const dmat* samples);
|
||||
|
||||
|
||||
d Decimator_get_cutoff(Decimator*);
|
||||
/**
|
||||
* Free memory corresponding to Decimator
|
||||
*
|
||||
* @param dec Decimator handle.
|
||||
*/
|
||||
void Decimator_free(Decimator* dec);
|
||||
|
||||
#endif // LASP_DECIMATION_H
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,145 +0,0 @@
|
||||
// lasp_dfifo.c
|
||||
//
|
||||
// Author: J.A. de Jong -ASCEE
|
||||
//
|
||||
// Description:
|
||||
// Implementation of the dFifo queue
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#define TRACERPLUS (-10)
|
||||
#include "lasp_dfifo.h"
|
||||
|
||||
typedef struct dFifo_s {
|
||||
dmat queue;
|
||||
us start_row;
|
||||
us end_row;
|
||||
} dFifo;
|
||||
us dFifo_size(dFifo* fifo) {
|
||||
fsTRACE(15);
|
||||
dbgassert(fifo,NULLPTRDEREF);
|
||||
dbgassert(fifo->start_row <= fifo->end_row,"BUG");
|
||||
feTRACE(15);
|
||||
return fifo->end_row-fifo->start_row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the max size of the dFifo to a new max size specified. Max size
|
||||
* should be larger than fifo size. Resets start row to 0
|
||||
*
|
||||
* @param fifo
|
||||
* @param new_size
|
||||
*/
|
||||
static void dFifo_change_maxsize(dFifo* fifo,const us new_max_size) {
|
||||
fsTRACE(15);
|
||||
dmat old_queue = fifo->queue;
|
||||
|
||||
dbgassert(new_max_size >= dFifo_size(fifo),"BUG");
|
||||
const us size = dFifo_size(fifo);
|
||||
|
||||
dmat new_queue = dmat_alloc(new_max_size,old_queue.n_cols);
|
||||
if(size > 0) {
|
||||
dmat_copy_rows(&new_queue,
|
||||
&old_queue,
|
||||
0,
|
||||
fifo->start_row,
|
||||
size);
|
||||
}
|
||||
|
||||
dmat_free(&old_queue);
|
||||
fifo->queue = new_queue;
|
||||
fifo->end_row -= fifo->start_row;
|
||||
fifo->start_row = 0;
|
||||
|
||||
feTRACE(15);
|
||||
}
|
||||
|
||||
dFifo* dFifo_create(const us nchannels,
|
||||
const us init_size) {
|
||||
|
||||
fsTRACE(15);
|
||||
dFifo* fifo = a_malloc(sizeof(dFifo));
|
||||
fifo->queue = dmat_alloc(init_size,nchannels);
|
||||
fifo->start_row = 0;
|
||||
fifo->end_row = 0;
|
||||
feTRACE(15);
|
||||
return fifo;
|
||||
}
|
||||
void dFifo_free(dFifo* fifo) {
|
||||
fsTRACE(15);
|
||||
dmat_free(&fifo->queue);
|
||||
a_free(fifo);
|
||||
feTRACE(15);
|
||||
}
|
||||
void dFifo_push(dFifo* fifo,const dmat* data) {
|
||||
fsTRACE(15);
|
||||
dbgassert(fifo && data, NULLPTRDEREF);
|
||||
dbgassert(data->n_cols == fifo->queue.n_cols,
|
||||
"Invalid number of columns in data");
|
||||
|
||||
|
||||
const us added_size = data->n_rows;
|
||||
|
||||
dmat queue = fifo->queue;
|
||||
const us max_size = queue.n_rows;
|
||||
|
||||
us* end_row = &fifo->end_row;
|
||||
|
||||
if(added_size + dFifo_size(fifo) > max_size) {
|
||||
dFifo_change_maxsize(fifo,2*(max_size+added_size));
|
||||
|
||||
/* Now the stack of this function is not valid anymore. Best
|
||||
* thing to do is restart the function. */
|
||||
dFifo_push(fifo,data);
|
||||
feTRACE(15);
|
||||
return;
|
||||
}
|
||||
else if(*end_row + added_size > max_size) {
|
||||
dFifo_change_maxsize(fifo,max_size);
|
||||
/* Now the stack of this function is not valid anymore. Best
|
||||
* thing to do is restart the function. */
|
||||
dFifo_push(fifo,data);
|
||||
feTRACE(15);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
/* Now, copy samples */
|
||||
dmat_copy_rows(&queue, /* to */
|
||||
data, /* from */
|
||||
*end_row, /* startrow_to */
|
||||
0, /* startrow_from */
|
||||
added_size); /* n_rows */
|
||||
|
||||
/* Increase the size */
|
||||
*end_row += added_size;
|
||||
|
||||
feTRACE(15);
|
||||
}
|
||||
int dFifo_pop(dFifo* fifo,dmat* data,const us keep) {
|
||||
fsTRACE(15);
|
||||
dbgassert(fifo && data,NULLPTRDEREF);
|
||||
dbgassert(data->n_cols == fifo->queue.n_cols,
|
||||
"Invalid number of columns in data");
|
||||
dbgassert(keep < data->n_rows, "Number of samples to keep should"
|
||||
" be smaller than requested number of samples");
|
||||
|
||||
us* start_row = &fifo->start_row;
|
||||
us cur_size = dFifo_size(fifo);
|
||||
us requested = data->n_rows;
|
||||
|
||||
us obtained = requested > cur_size ? cur_size : requested;
|
||||
dbgassert(obtained > keep,"Number of samples to keep should be"
|
||||
" smaller than requested number of samples");
|
||||
|
||||
|
||||
dmat_copy_rows(data,
|
||||
&fifo->queue,
|
||||
0,
|
||||
*start_row,
|
||||
obtained);
|
||||
|
||||
*start_row += obtained - keep;
|
||||
|
||||
feTRACE(15);
|
||||
return (int) obtained;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,73 +0,0 @@
|
||||
// lasp_dfifo.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description:
|
||||
// API of a contiguous fifo buffer of samples.
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef LASP_DFIFO_H
|
||||
#define LASPDFIFO_H
|
||||
#include "lasp_types.h"
|
||||
#include "lasp_mat.h"
|
||||
|
||||
typedef struct dFifo_s dFifo;
|
||||
|
||||
/**
|
||||
* Create a fifo buffer
|
||||
*
|
||||
* @param nchannels Number of channels to store for
|
||||
* @param max_size Maximum size of the queue.
|
||||
*
|
||||
* @return Pointer to fifo queue (no null ptr)
|
||||
*/
|
||||
dFifo* dFifo_create(const us nchannels,
|
||||
const us init_size);
|
||||
|
||||
|
||||
/**
|
||||
* Pushes samples into the fifo.
|
||||
*
|
||||
* @param fifo dFifo handle
|
||||
*
|
||||
* @param data data to push. Number of columns should be equal to
|
||||
* nchannels.
|
||||
*/
|
||||
void dFifo_push(dFifo* fifo,const dmat* data);
|
||||
|
||||
/**
|
||||
* Pop samples from the queue
|
||||
*
|
||||
* @param[in] fifo dFifo handle
|
||||
|
||||
* @param[out] data Pointer to dmat where popped data will be
|
||||
* stored. Should have nchannels number of columns. If n_rows is
|
||||
* larger than current storage, the queue is emptied.
|
||||
|
||||
* @param[in] keep Keeps a number of samples for the next dFifo_pop(). If
|
||||
* keep=0, then no samples will be left. Keep should be smaller than
|
||||
* the number of rows in data.
|
||||
*
|
||||
* @return Number of samples obtained in data.
|
||||
*/
|
||||
int dFifo_pop(dFifo* fifo,dmat* data,const us keep);
|
||||
|
||||
/**
|
||||
* Returns current size of the fifo
|
||||
*
|
||||
* @param[in] fifo dFifo handle
|
||||
*
|
||||
* @return Current size
|
||||
*/
|
||||
us dFifo_size(dFifo* fifo);
|
||||
|
||||
/**
|
||||
* Free a dFifo object
|
||||
*
|
||||
* @param[in] fifo dFifo handle.
|
||||
*/
|
||||
void dFifo_free(dFifo* fifo);
|
||||
|
||||
#endif // LASP_DFIFO_H
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -1,70 +0,0 @@
|
||||
#include "lasp_eq.h"
|
||||
#include "lasp_assert.h"
|
||||
|
||||
typedef struct Eq {
|
||||
Sosfilterbank* fb;
|
||||
us nfilters;
|
||||
vd ampl_values;
|
||||
} Eq;
|
||||
|
||||
Eq* Eq_create(Sosfilterbank* fb) {
|
||||
fsTRACE(15);
|
||||
assertvalidptr(fb);
|
||||
Eq* eq = a_malloc(sizeof(Eq));
|
||||
eq->fb = fb;
|
||||
eq->nfilters = Sosfilterbank_getFilterbankSize(fb);
|
||||
eq->ampl_values = vd_alloc(eq->nfilters);
|
||||
vd_set(&(eq->ampl_values), 1.0);
|
||||
feTRACE(15);
|
||||
return eq;
|
||||
}
|
||||
vd Eq_equalize(Eq* eq,const vd* input_data) {
|
||||
fsTRACE(15);
|
||||
assertvalidptr(eq);
|
||||
assert_vx(input_data);
|
||||
vd result = vd_alloc(input_data->n_rows);
|
||||
dmat_set(&result, 0);
|
||||
dmat filtered = Sosfilterbank_filter(eq->fb, input_data);
|
||||
|
||||
for(us filter=0;filter<eq->nfilters;filter++) {
|
||||
d ampl = *getvdval(&(eq->ampl_values), filter);
|
||||
/// TODO: Replace this code with something more fast from BLAS.
|
||||
for(us sample=0;sample<filtered.n_rows;sample++) {
|
||||
d* res = getvdval(&result, sample);
|
||||
*res = *res + *getdmatval(&filtered, sample, filter) * ampl;
|
||||
}
|
||||
}
|
||||
dmat_free(&filtered);
|
||||
|
||||
feTRACE(15);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Eq_setLevels(Eq* eq,const vd* levels) {
|
||||
fsTRACE(15);
|
||||
assertvalidptr(eq);
|
||||
assert_vx(levels);
|
||||
dbgassert(levels->n_rows == eq->nfilters, "Invalid levels size");
|
||||
for(us ch=0;ch<eq->nfilters;ch++){
|
||||
d level = *getvdval(levels, ch);
|
||||
*getvdval(&(eq->ampl_values), ch) = d_pow(10, level/20);
|
||||
}
|
||||
|
||||
feTRACE(15);
|
||||
}
|
||||
|
||||
us Eq_getNLevels(const Eq* eq) {
|
||||
fsTRACE(15);
|
||||
assertvalidptr(eq);
|
||||
feTRACE(15);
|
||||
return eq->nfilters;
|
||||
}
|
||||
|
||||
void Eq_free(Eq* eq) {
|
||||
fsTRACE(15);
|
||||
assertvalidptr(eq);
|
||||
assertvalidptr(eq->fb);
|
||||
Sosfilterbank_free(eq->fb);
|
||||
vd_free(&(eq->ampl_values));
|
||||
feTRACE(15);
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
// lasp_eq.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description: Implementation of an equalizer using the Second Order Sections
|
||||
// filter bank implementation. Applies all filterbanks in parallel and
|
||||
// recombines to create a single output signal.
|
||||
#pragma once
|
||||
#ifndef LASP_EQ_H
|
||||
#define LASP_EQ_H
|
||||
#include "lasp_sosfilterbank.h"
|
||||
typedef struct Eq Eq;
|
||||
|
||||
/**
|
||||
* Initialize an equalizer using the given Filterbank. Note: takes over pointer
|
||||
* ownership of fb! Sets levels of all filterbanks initially to 0 dB.
|
||||
*
|
||||
* @param[in] fb Filterbank to be used in equalizer
|
||||
* @return Equalizer handle, NULL on error
|
||||
* */
|
||||
Eq* Eq_create(Sosfilterbank* fb);
|
||||
|
||||
/**
|
||||
* Equalize a given piece of data using current settings of the levels.
|
||||
*
|
||||
* @param[in] eq Equalizer handle
|
||||
* @param[in] input_data Input data to equalize
|
||||
* @return Equalized data. Newly allocated vector. Ownership is transferred.
|
||||
* */
|
||||
vd Eq_equalize(Eq* eq,const vd* input_data);
|
||||
|
||||
/**
|
||||
* Returns number of channels of the equalizer. Note: takes over pointer
|
||||
* ownership of fb!
|
||||
*
|
||||
* @param[in] eq Equalizer handle
|
||||
* */
|
||||
us Eq_getNLevels(const Eq* eq);
|
||||
|
||||
/**
|
||||
* Set amplification values for each filter in the equalizer.
|
||||
*
|
||||
* @param[in] eq Equalizer handle
|
||||
* @param[in] levels: Vector with level values for each channel. Should have
|
||||
* length equal to the number of filters in the filterbank.
|
||||
* */
|
||||
void Eq_setLevels(Eq* eq, const vd* levels);
|
||||
|
||||
/**
|
||||
* Cleans up an existing Equalizer
|
||||
*
|
||||
* @param[in] eq Equalizer handle
|
||||
*/
|
||||
void Eq_free(Eq* eq);
|
||||
|
||||
|
||||
#endif // LASP_EQ_H
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
|
@ -1,263 +0,0 @@
|
||||
// lasp_fft.c
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#define TRACERPLUS (-5)
|
||||
#include "lasp_types.h"
|
||||
#include "lasp_config.h"
|
||||
#include "lasp_tracer.h"
|
||||
#include "lasp_fft.h"
|
||||
|
||||
#if LASP_FFT_BACKEND == FFTPack
|
||||
#include "fftpack.h"
|
||||
typedef struct Fft_s {
|
||||
us nfft;
|
||||
vd fft_work; // Storage memory for fftpack
|
||||
} Fft_s;
|
||||
#elif LASP_FFT_BACKEND == FFTW
|
||||
#include <fftw3.h>
|
||||
|
||||
typedef struct Fft_s {
|
||||
us nfft;
|
||||
fftw_plan forward_plan;
|
||||
fftw_plan reverse_plan;
|
||||
c* complex_storage;
|
||||
d* real_storage;
|
||||
} Fft_s;
|
||||
#else
|
||||
#error "Cannot compile lasp_ffc.c, no FFT backend specified. Should either be FFTPack, or FFTW"
|
||||
#endif
|
||||
|
||||
void load_fft_wisdom(const char* wisdom) {
|
||||
#if LASP_FFT_BACKEND == FFTPack
|
||||
#elif LASP_FFT_BACKEND == FFTW
|
||||
if(wisdom) {
|
||||
int rv= fftw_import_wisdom_from_string(wisdom);
|
||||
if(rv != 1) {
|
||||
fprintf(stderr, "Error loading FFTW wisdom");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
char* store_fft_wisdom() {
|
||||
#if LASP_FFT_BACKEND == FFTPack
|
||||
return NULL;
|
||||
#elif LASP_FFT_BACKEND == FFTW
|
||||
return fftw_export_wisdom_to_string();
|
||||
#endif
|
||||
}
|
||||
|
||||
Fft* Fft_create(const us nfft) {
|
||||
fsTRACE(15);
|
||||
if(nfft == 0) {
|
||||
WARN("nfft should be > 0");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Fft* fft = a_malloc(sizeof(Fft));
|
||||
|
||||
fft->nfft = nfft;
|
||||
|
||||
#if LASP_FFT_BACKEND == FFTPack
|
||||
/* Initialize foreign fft lib */
|
||||
fft->fft_work = vd_alloc(2*nfft+15);
|
||||
npy_rffti(nfft,getvdval(&fft->fft_work,0));
|
||||
check_overflow_vx(fft->fft_work);
|
||||
#elif LASP_FFT_BACKEND == FFTW
|
||||
fft->complex_storage = fftw_malloc(sizeof(c) * (nfft/2 + 1));
|
||||
fft->real_storage = fftw_malloc(sizeof(d) * nfft);
|
||||
|
||||
fft->forward_plan = fftw_plan_dft_r2c_1d(nfft,
|
||||
fft->real_storage,
|
||||
fft->complex_storage,
|
||||
FFTW_MEASURE);
|
||||
fft->reverse_plan = fftw_plan_dft_c2r_1d(nfft,
|
||||
fft->complex_storage,
|
||||
fft->real_storage,
|
||||
FFTW_MEASURE);
|
||||
|
||||
#endif
|
||||
|
||||
/* print_vd(&fft->fft_work); */
|
||||
|
||||
feTRACE(15);
|
||||
return fft;
|
||||
}
|
||||
void Fft_free(Fft* fft) {
|
||||
fsTRACE(15);
|
||||
dbgassert(fft,NULLPTRDEREF);
|
||||
#if LASP_FFT_BACKEND == FFTPack
|
||||
vd_free(&fft->fft_work);
|
||||
#elif LASP_FFT_BACKEND == FFTW
|
||||
fftw_free(fft->complex_storage);
|
||||
fftw_free(fft->real_storage);
|
||||
fftw_destroy_plan(fft->forward_plan);
|
||||
fftw_destroy_plan(fft->reverse_plan);
|
||||
#endif
|
||||
a_free(fft);
|
||||
feTRACE(15);
|
||||
}
|
||||
|
||||
us Fft_nfft(const Fft* fft) {return fft->nfft;}
|
||||
|
||||
void Fft_ifft_single(const Fft* fft,const vc* freqdata,vd* result) {
|
||||
fsTRACE(15);
|
||||
dbgassert(fft && freqdata && result,NULLPTRDEREF);
|
||||
const us nfft = fft->nfft;
|
||||
dbgassert(result->n_rows == nfft,
|
||||
"Invalid size for time data rows."
|
||||
" Should be equal to nfft");
|
||||
|
||||
dbgassert(freqdata->n_rows == (nfft/2+1),"Invalid number of rows in"
|
||||
" result array");
|
||||
|
||||
|
||||
d* result_ptr = getvdval(result,0);
|
||||
|
||||
#if LASP_FFT_BACKEND == FFTPack
|
||||
d* freqdata_ptr = (d*) getvcval(freqdata,0);
|
||||
/* Copy freqdata, to fft_result. */
|
||||
d_copy(&result_ptr[1],&freqdata_ptr[2],nfft-1,1,1);
|
||||
result_ptr[0] = freqdata_ptr[0];
|
||||
|
||||
/* Perform inplace backward transform */
|
||||
npy_rfftb(nfft,
|
||||
result_ptr,
|
||||
getvdval(&fft->fft_work,0));
|
||||
|
||||
|
||||
#elif LASP_FFT_BACKEND == FFTW
|
||||
c* freqdata_ptr = (c*) getvcval(freqdata,0);
|
||||
|
||||
c_copy(fft->complex_storage, freqdata_ptr,nfft/2+1);
|
||||
|
||||
fftw_execute(fft->reverse_plan);
|
||||
|
||||
d_copy(result_ptr, fft->real_storage, nfft, 1, 1);
|
||||
|
||||
#endif
|
||||
check_overflow_vx(*result);
|
||||
|
||||
/* Scale by dividing by nfft. Checked with numpy implementation
|
||||
* that this indeed needs to be done for FFTpack. */
|
||||
d_scale(result_ptr,1/((d) nfft),nfft);
|
||||
feTRACE(15);
|
||||
}
|
||||
void Fft_ifft(const Fft* fft,const cmat* freqdata,dmat* timedata) {
|
||||
fsTRACE(15);
|
||||
|
||||
dbgassert(fft && timedata && freqdata,NULLPTRDEREF);
|
||||
|
||||
const us nchannels = timedata->n_cols;
|
||||
dbgassert(timedata->n_cols == freqdata->n_cols,
|
||||
"Number of columns in timedata and result"
|
||||
" should be equal.");
|
||||
|
||||
for(us col=0;col<nchannels;col++) {
|
||||
|
||||
vd timedata_col = dmat_column(timedata,col);
|
||||
vc freqdata_col = cmat_column((cmat*)freqdata,col);
|
||||
|
||||
Fft_ifft_single(fft,&freqdata_col,&timedata_col);
|
||||
|
||||
vd_free(&timedata_col);
|
||||
vc_free(&freqdata_col);
|
||||
}
|
||||
check_overflow_xmat(*timedata);
|
||||
check_overflow_xmat(*freqdata);
|
||||
|
||||
feTRACE(15);
|
||||
}
|
||||
|
||||
void Fft_fft_single(const Fft* fft,const vd* timedata,vc* result) {
|
||||
|
||||
fsTRACE(15);
|
||||
dbgassert(fft && timedata && result,NULLPTRDEREF);
|
||||
|
||||
const us nfft = fft->nfft;
|
||||
assert_vx(timedata);
|
||||
assert_vx(result);
|
||||
dbgassert(timedata->n_rows == nfft,
|
||||
"Invalid size for time data rows."
|
||||
" Should be equal to nfft");
|
||||
|
||||
dbgassert(result->n_rows == (nfft/2+1),"Invalid number of rows in"
|
||||
" result array");
|
||||
|
||||
|
||||
#if LASP_FFT_BACKEND == FFTPack
|
||||
d* result_ptr = (d*) getvcval(result,0);
|
||||
|
||||
/* Fftpack stores the data a bit strange, the resulting array
|
||||
* has the DC value at 0,the first cosine at 1, the first sine
|
||||
* at 2 etc. 1
|
||||
* resulting matrix, as for the complex data, the imaginary
|
||||
* part of the DC component equals zero. */
|
||||
|
||||
/* Copy timedata, as it will be overwritten in the fft pass. */
|
||||
d_copy(&result_ptr[1],getvdval(timedata,0),nfft,1,1);
|
||||
|
||||
|
||||
/* Perform fft */
|
||||
npy_rfftf(nfft,&result_ptr[1],
|
||||
getvdval(&fft->fft_work,0));
|
||||
|
||||
/* Set real part of DC component to first index of the rfft
|
||||
* routine */
|
||||
result_ptr[0] = result_ptr[1];
|
||||
|
||||
result_ptr[1] = 0; /* Set imaginary part of DC component
|
||||
* to zero */
|
||||
|
||||
/* For an even fft, the imaginary part of the Nyquist frequency
|
||||
* bin equals zero.*/
|
||||
if(islikely(nfft%2 == 0)) {
|
||||
result_ptr[nfft+1] = 0;
|
||||
}
|
||||
check_overflow_vx(fft->fft_work);
|
||||
#elif LASP_FFT_BACKEND == FFTW
|
||||
|
||||
d* timedata_ptr = getvdval(timedata,0);
|
||||
c* result_ptr = getvcval(result,0);
|
||||
d_copy(fft->real_storage,timedata_ptr, nfft, 1, 1);
|
||||
|
||||
fftw_execute(fft->forward_plan);
|
||||
|
||||
c_copy(result_ptr, fft->complex_storage, nfft/2+1);
|
||||
|
||||
#endif
|
||||
check_overflow_vx(*result);
|
||||
feTRACE(15);
|
||||
|
||||
}
|
||||
void Fft_fft(const Fft* fft,const dmat* timedata,cmat* result) {
|
||||
fsTRACE(15);
|
||||
|
||||
dbgassert(fft && timedata && result,NULLPTRDEREF);
|
||||
|
||||
const us nchannels = timedata->n_cols;
|
||||
dbgassert(timedata->n_cols == result->n_cols,
|
||||
"Number of columns in timedata and result"
|
||||
" should be equal.");
|
||||
|
||||
for(us col=0;col<nchannels;col++) {
|
||||
|
||||
vd timedata_col = dmat_column((dmat*) timedata,col);
|
||||
vc result_col = cmat_column(result,col);
|
||||
|
||||
Fft_fft_single(fft,&timedata_col,&result_col);
|
||||
|
||||
vd_free(&timedata_col);
|
||||
vc_free(&result_col);
|
||||
}
|
||||
check_overflow_xmat(*timedata);
|
||||
check_overflow_xmat(*result);
|
||||
|
||||
feTRACE(15);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,103 +0,0 @@
|
||||
// lasp_fft.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description:
|
||||
// Interface to the FFT library, multiple channel FFT's
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef LASP_FFT_H
|
||||
#define LASP_FFT_H
|
||||
#include "lasp_types.h"
|
||||
#include "lasp_mat.h"
|
||||
|
||||
/**
|
||||
* Load wisdom data from a NULL-terminated character string. Note that at this
|
||||
* point in time this is only used by the FFTW fft backend.
|
||||
*
|
||||
* @param[in] wisdom: Wisdom string
|
||||
*/
|
||||
void load_fft_wisdom(const char* wisdom);
|
||||
|
||||
/**
|
||||
* Creates a NULL-terminated string containing FFTW wisdom, or state knowledge
|
||||
* from other libraries.
|
||||
*
|
||||
* @returns A NULL-terminated string. *Should be deallocated after being used*
|
||||
*/
|
||||
char* store_fft_wisdom(void);
|
||||
|
||||
|
||||
/**
|
||||
* Perform forward FFT's on real time data.
|
||||
*
|
||||
*/
|
||||
typedef struct Fft_s Fft;
|
||||
|
||||
/**
|
||||
* Construct an Fft object
|
||||
*
|
||||
* @param nfft Nfft size
|
||||
*
|
||||
* @return Pointer to Fft handle, NULL on error
|
||||
*/
|
||||
Fft* Fft_create(const us nfft);
|
||||
/**
|
||||
* Returns the nfft for this Fft instance
|
||||
*
|
||||
* @return nfft
|
||||
*/
|
||||
us Fft_nfft(const Fft*);
|
||||
|
||||
/**
|
||||
* Compute the fft of a single channel.
|
||||
*
|
||||
* @param[in] fft Fft handle.
|
||||
*
|
||||
* @param[in] timedata Input time data pointer, should have size nfft
|
||||
* @param[out] result Pointer to result vector should have size
|
||||
* nfft/2+1
|
||||
*/
|
||||
void Fft_fft_single(const Fft* fft,const vd* timedata,vc* result);
|
||||
|
||||
/**
|
||||
* Compute the fft of the data matrix, first axis is assumed to be
|
||||
* the time axis.
|
||||
*
|
||||
* @param[in] fft Fft handle.
|
||||
|
||||
* @param[in] timedata Input time data. First axis is assumed to be
|
||||
* the time, second the channel number.
|
||||
*
|
||||
* @param[out] result: Fft't data, should have size (nfft/2+1) *
|
||||
* nchannels
|
||||
*/
|
||||
void Fft_fft(const Fft* fft,const dmat* timedata,cmat* result);
|
||||
|
||||
/**
|
||||
* Perform inverse fft on a single channel.
|
||||
*
|
||||
* @param[in] fft Fft handle.
|
||||
* @param[in] freqdata Frequency domain input data, to be iFft'th.
|
||||
* @param[out] timedata: iFft't data, should have size (nfft).
|
||||
*/
|
||||
void Fft_ifft_single(const Fft* fft,const vc* freqdata,vd* timedata);
|
||||
|
||||
/**
|
||||
* Perform inverse FFT
|
||||
*
|
||||
* @param[in] fft Fft handle
|
||||
* @param[in] freqdata Frequency domain data
|
||||
* @param[out] timedata Time domain result
|
||||
*/
|
||||
void Fft_ifft(const Fft* fft,const cmat* freqdata,dmat* timedata);
|
||||
|
||||
/**
|
||||
* Free up resources of Fft handle.
|
||||
*
|
||||
* @param fft Fft handle.
|
||||
*/
|
||||
void Fft_free(Fft* fft);
|
||||
|
||||
#endif // LASP_FFT_H
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,174 +0,0 @@
|
||||
// lasp_filterbank.c
|
||||
//
|
||||
// Author: J.A. de Jong -ASCEE
|
||||
//
|
||||
// Description:
|
||||
// Firfilterbank implementation.
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#define TRACERPLUS (-5)
|
||||
#include "lasp_firfilterbank.h"
|
||||
#include "lasp_fft.h"
|
||||
#include "lasp_dfifo.h"
|
||||
#include "lasp_tracer.h"
|
||||
#include "lasp_alg.h"
|
||||
#define FIFO_SIZE_MULT 2
|
||||
|
||||
typedef struct Firfilterbank_s {
|
||||
us nfft;
|
||||
|
||||
us P_m_1; /**< Filter length minus one */
|
||||
|
||||
cmat filters; /* Frequency-domain filter
|
||||
* coefficients */
|
||||
dFifo* output_fifo;
|
||||
dFifo* input_fifo;
|
||||
|
||||
Fft* fft; /* Handle to internal FFT-function */
|
||||
|
||||
} Firfilterbank;
|
||||
|
||||
Firfilterbank* Firfilterbank_create(const dmat* h,
|
||||
const us nfft) {
|
||||
|
||||
fsTRACE(15);
|
||||
dbgassert(h,NULLPTRDEREF);
|
||||
const us P = h->n_rows;
|
||||
const us nfilters = h->n_cols;
|
||||
|
||||
if(P > nfft/2) {
|
||||
WARN("Filter order should be <= nfft/2");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Fft* fft = Fft_create(nfft);
|
||||
if(!fft) {
|
||||
WARN("Fft allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Firfilterbank* fb = a_malloc(sizeof(Firfilterbank));
|
||||
|
||||
fb->nfft = nfft;
|
||||
fb->P_m_1 = P-1;
|
||||
fb->fft = fft;
|
||||
fb->filters = cmat_alloc(nfft/2+1,nfilters);
|
||||
|
||||
fb->output_fifo = dFifo_create(nfilters,FIFO_SIZE_MULT*nfft);
|
||||
fb->input_fifo = dFifo_create(1,FIFO_SIZE_MULT*nfft);
|
||||
|
||||
// Initialize the input fifo with zeros.
|
||||
// dmat init_zero = dmat_alloc(nfft - P, 1);
|
||||
// dmat_set(&init_zero,0);
|
||||
// dFifo_push(fb->input_fifo, &init_zero);
|
||||
// dmat_free(&init_zero);
|
||||
|
||||
/* Create a temporary buffer which is going to be FFT'th to
|
||||
* contain the filter transfer functions.
|
||||
*/
|
||||
dmat temp = dmat_alloc(nfft,nfilters);
|
||||
dmat_set(&temp,0);
|
||||
dmat_copy_rows(&temp,h,0,0,h->n_rows);
|
||||
|
||||
/* Fft the FIR impulse responses */
|
||||
Fft_fft(fb->fft,&temp,&fb->filters);
|
||||
|
||||
dmat_free(&temp);
|
||||
|
||||
feTRACE(15);
|
||||
return fb;
|
||||
}
|
||||
void Firfilterbank_free(Firfilterbank* fb) {
|
||||
fsTRACE(15);
|
||||
dbgassert(fb,NULLPTRDEREF);
|
||||
cmat_free(&fb->filters);
|
||||
dFifo_free(fb->input_fifo);
|
||||
dFifo_free(fb->output_fifo);
|
||||
Fft_free(fb->fft);
|
||||
a_free(fb);
|
||||
feTRACE(15);
|
||||
}
|
||||
dmat Firfilterbank_filter(Firfilterbank* fb,
|
||||
const vd* x) {
|
||||
|
||||
fsTRACE(15);
|
||||
dbgassert(fb && x ,NULLPTRDEREF);
|
||||
|
||||
dFifo* input_fifo = fb->input_fifo;
|
||||
dFifo* output_fifo = fb->output_fifo;
|
||||
|
||||
const us nfft = fb->nfft;
|
||||
const us nfilters = fb->filters.n_cols;
|
||||
|
||||
/* Push samples to the input fifo */
|
||||
dFifo_push(fb->input_fifo,x);
|
||||
|
||||
dmat input_block = dmat_alloc(nfft,1);
|
||||
|
||||
/* FFT'th filter coefficients */
|
||||
cmat input_fft = cmat_alloc(nfft/2+1,1);
|
||||
|
||||
/* Output of the fast convolution */
|
||||
cmat output_fft = cmat_alloc(nfft/2+1,nfilters);
|
||||
|
||||
/* Inverse FFT'th output */
|
||||
dmat output_block = dmat_alloc(nfft,nfilters);
|
||||
|
||||
while (dFifo_size(input_fifo) >= nfft) {
|
||||
|
||||
us nsamples = dFifo_pop(input_fifo,
|
||||
&input_block,
|
||||
fb->P_m_1 /* save P-1 samples */
|
||||
);
|
||||
|
||||
dbgassert(nsamples == nfft,"BUG in dFifo");
|
||||
|
||||
Fft_fft(fb->fft,&input_block,&input_fft);
|
||||
|
||||
vc input_fft_col = cmat_column(&input_fft,0);
|
||||
|
||||
for(us col=0;col<nfilters;col++) {
|
||||
vc output_fft_col = cmat_column(&output_fft,col);
|
||||
vc filter_col = cmat_column(&fb->filters,col);
|
||||
|
||||
vc_hadamard(&output_fft_col,
|
||||
&input_fft_col,
|
||||
&filter_col);
|
||||
|
||||
vc_free(&output_fft_col);
|
||||
vc_free(&filter_col);
|
||||
|
||||
}
|
||||
|
||||
vc_free(&input_fft_col);
|
||||
|
||||
Fft_ifft(fb->fft,&output_fft,&output_block);
|
||||
|
||||
dmat valid_samples = dmat_submat(&output_block,
|
||||
fb->P_m_1,0, /* startrow, startcol */
|
||||
nfft-fb->P_m_1, /* Number of rows */
|
||||
output_block.n_cols);
|
||||
|
||||
/* Push the valid samples to the output FIFO */
|
||||
dFifo_push(fb->output_fifo,&valid_samples);
|
||||
dmat_free(&valid_samples);
|
||||
|
||||
}
|
||||
|
||||
dmat_free(&input_block);
|
||||
cmat_free(&input_fft);
|
||||
cmat_free(&output_fft);
|
||||
dmat_free(&output_block);
|
||||
|
||||
us samples_done = dFifo_size(output_fifo);
|
||||
uVARTRACE(15,samples_done);
|
||||
dmat filtered_result = dmat_alloc(samples_done,nfilters);
|
||||
if(samples_done) {
|
||||
us samples_done2 = dFifo_pop(output_fifo,&filtered_result,0);
|
||||
dbgassert(samples_done2 == samples_done,"BUG in dFifo");
|
||||
}
|
||||
feTRACE(15);
|
||||
return filtered_result;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,59 +0,0 @@
|
||||
// lasp_firfilterbank.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description: Implemententation of a discrete FIR filterbank using fast
|
||||
// convolution and the overlap-save (overlap-scrap method). Multiple
|
||||
// filters can be applied to the same input data (*filterbank*).
|
||||
// Implementation is computationally efficient, as the forward FFT is
|
||||
// performed only over the input data, and the backwards transfer for
|
||||
// each filter in the filterbank.
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef LASP_FIRFILTERBANK_H
|
||||
#define LASP_FIRFILTERBANK_H
|
||||
#include "lasp_types.h"
|
||||
#include "lasp_mat.h"
|
||||
typedef struct Firfilterbank_s Firfilterbank;
|
||||
|
||||
/**
|
||||
* Initializes a fast convolution filter bank and returns a Firfilterbank
|
||||
* handle. The nfft will be chosen to be at least four times the
|
||||
* length of the FIR filters.
|
||||
*
|
||||
* @param h: matrix with filter coefficients of each of the
|
||||
* filters. First axis is the axis of the filter coefficients, second
|
||||
* axis is the filter number. Maximum length of the filter is nfft/2.
|
||||
*
|
||||
* @param nfft: FTT length for fast convolution. For good performance,
|
||||
* nfft should be chosen as the nearest power of 2, approximately four
|
||||
* times the filter lengths. For the lowest possible latency, it is
|
||||
* better to set nfft at twice the filter length.
|
||||
*
|
||||
* @return Firfilterbank handle, NULL on error.
|
||||
*/
|
||||
Firfilterbank* Firfilterbank_create(const dmat* h,const us nfft);
|
||||
|
||||
/**
|
||||
* Filters x using h, returns y
|
||||
*
|
||||
* @param x Input time sequence block. Should have at least one sample.
|
||||
|
||||
* @return Filtered output in an allocated array. The number of
|
||||
* columns in this array equals the number of filters in the
|
||||
* filterbank. The number of output samples is equal to the number of
|
||||
* input samples in x.
|
||||
*/
|
||||
dmat Firfilterbank_filter(Firfilterbank* fb,
|
||||
const vd* x);
|
||||
|
||||
/**
|
||||
* Cleans up an existing filter bank.
|
||||
*
|
||||
* @param f Filter handle
|
||||
*/
|
||||
void Firfilterbank_free(Firfilterbank* f);
|
||||
|
||||
|
||||
#endif // LASP_FIRFILTERBANK_H
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,54 +0,0 @@
|
||||
// lasp_mat.c
|
||||
//
|
||||
// Author: J.A. de Jong -ASCEE
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#define TRACERPLUS (-10)
|
||||
#include "lasp_mat.h"
|
||||
#include "lasp_assert.h"
|
||||
#include "lasp_tracer.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#if LASP_DEBUG == 1
|
||||
void print_dmat(const dmat* m) {
|
||||
fsTRACE(50);
|
||||
size_t row,col;
|
||||
for(row=0;row<m->n_rows;row++){
|
||||
indent_trace();
|
||||
for(col=0;col<m->n_cols;col++){
|
||||
d val = *getdmatval(m,row,col);
|
||||
printf("%c%2.2e ", val<0?'-':' ' ,d_abs(val));
|
||||
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
}
|
||||
feTRACE(50);
|
||||
}
|
||||
void print_cmat(const cmat* m) {
|
||||
fsTRACE(50);
|
||||
size_t row,col;
|
||||
for(row=0;row<m->n_rows;row++){
|
||||
indent_trace();
|
||||
for(col=0;col<m->n_cols;col++){
|
||||
c val = *getcmatval(m,row,col);
|
||||
|
||||
d rval = creal(val);
|
||||
d ival = cimag(val);
|
||||
|
||||
printf("%c%2.2e%c%2.2ei ",rval< 0 ?'-': ' ',
|
||||
d_abs(rval),ival<0 ? '-' : '+',d_abs(ival) ) ;
|
||||
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
}
|
||||
feTRACE(50);
|
||||
}
|
||||
|
||||
#endif // LASP_DEBUG == 1
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,597 +0,0 @@
|
||||
// lasp_mat.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description: Basic routines for allocating, setting, freeing and
|
||||
// copying of matrices and vectors.
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef LASP_MAT_H
|
||||
#define LASP_MAT_H
|
||||
#include "lasp_math_raw.h"
|
||||
#include "lasp_alloc.h"
|
||||
#include "lasp_assert.h"
|
||||
#include "lasp_tracer.h"
|
||||
#include "lasp_assert.h"
|
||||
|
||||
/// Dense matrix of floating point values
|
||||
typedef struct {
|
||||
us n_rows;
|
||||
us n_cols;
|
||||
bool _foreign_data;
|
||||
us colstride;
|
||||
d* _data;
|
||||
} dmat;
|
||||
|
||||
/// Dense matrix of complex floating point values
|
||||
typedef struct {
|
||||
us n_rows;
|
||||
us n_cols;
|
||||
bool _foreign_data;
|
||||
us colstride;
|
||||
c* _data;
|
||||
} cmat;
|
||||
|
||||
typedef dmat vd;
|
||||
typedef cmat vc;
|
||||
|
||||
#define assert_equalsize(a,b) \
|
||||
dbgassert((a)->n_rows == (b)->n_rows,SIZEINEQUAL); \
|
||||
dbgassert((a)->n_cols == (b)->n_cols,SIZEINEQUAL);
|
||||
|
||||
#define is_vx(vx) ((vx)->n_cols == 1)
|
||||
#define assert_vx(vx) dbgassert(is_vx(vx),"Not a vector!")
|
||||
|
||||
#define setvecval(vec,index,val) \
|
||||
assert_vx(vec); \
|
||||
dbgassert((((us) index) < (vec)->n_rows),OUTOFBOUNDSVEC); \
|
||||
(vec)->_data[index] = val;
|
||||
|
||||
#define setmatval(mat,row,col,val) \
|
||||
dbgassert((((us) row) <= (mat)->n_rows),OUTOFBOUNDSMATR); \
|
||||
dbgassert((((us) col) <= (mat)->n_cols),OUTOFBOUNDSMATC); \
|
||||
(mat)->_data[(col)*(mat)->colstride+(row)] = val;
|
||||
|
||||
/**
|
||||
* Return pointer to a value from a vector
|
||||
*
|
||||
* @param mat The vector
|
||||
* @param row The row
|
||||
*/
|
||||
/**
|
||||
* Return a value from a matrix of floating points
|
||||
*
|
||||
* @param mat The matrix
|
||||
* @param row The row
|
||||
* @param col The column
|
||||
*/
|
||||
static inline d* getdmatval(const dmat* mat,us row,us col){
|
||||
dbgassert(mat,NULLPTRDEREF);
|
||||
dbgassert(row < mat->n_rows,OUTOFBOUNDSMATR);
|
||||
dbgassert(col < mat->n_cols,OUTOFBOUNDSMATC);
|
||||
return &mat->_data[col*mat->colstride+row];
|
||||
}
|
||||
/**
|
||||
* Return a value from a matrix of complex floating points
|
||||
*
|
||||
* @param mat The matrix
|
||||
* @param row The row
|
||||
* @param col The column
|
||||
*/
|
||||
static inline c* getcmatval(const cmat* mat,const us row,const us col){
|
||||
dbgassert(mat,NULLPTRDEREF);
|
||||
dbgassert(row < mat->n_rows,OUTOFBOUNDSMATR);
|
||||
dbgassert(col < mat->n_cols,OUTOFBOUNDSMATC);
|
||||
return &mat->_data[col*mat->colstride+row];
|
||||
}
|
||||
|
||||
static inline d* getvdval(const vd* vec,us row){
|
||||
dbgassert(vec,NULLPTRDEREF);
|
||||
assert_vx(vec);
|
||||
return getdmatval(vec,row,0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return pointer to a value from a complex vector
|
||||
*
|
||||
* @param mat The vector
|
||||
* @param row The row
|
||||
*/
|
||||
static inline c* getvcval(const vc* vec,us row){
|
||||
dbgassert(vec,NULLPTRDEREF);
|
||||
assert_vx(vec);
|
||||
return getcmatval(vec,row,0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef LASP_DEBUG
|
||||
#define OVERFLOW_MAGIC_NUMBER (-10e-45)
|
||||
|
||||
#define check_overflow_xmat(xmat) \
|
||||
TRACE(15,"Checking overflow " #xmat); \
|
||||
if(!(xmat)._foreign_data) { \
|
||||
dbgassert((xmat)._data[((xmat).n_cols-1)*(xmat).colstride+(xmat).n_rows] \
|
||||
== OVERFLOW_MAGIC_NUMBER, \
|
||||
"Buffer overflow detected on" #xmat ); \
|
||||
} \
|
||||
|
||||
#define check_overflow_vx check_overflow_xmat
|
||||
|
||||
#else
|
||||
#define check_overflow_vx(vx)
|
||||
#define check_overflow_xmat(xmat)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Sets all values in a matrix to the value
|
||||
*
|
||||
* @param mat The matrix to set
|
||||
* @param value
|
||||
*/
|
||||
static inline void dmat_set(dmat* mat,const d value){
|
||||
dbgassert(mat,NULLPTRDEREF);
|
||||
if(islikely(mat->n_cols * mat->n_rows > 0)) {
|
||||
for(us col=0;col<mat->n_cols;col++) {
|
||||
d_set(getdmatval(mat,0,col),value,mat->n_rows);
|
||||
}
|
||||
}
|
||||
}
|
||||
#define vd_set dmat_set
|
||||
|
||||
/**
|
||||
* Sets all values in a matrix to the value
|
||||
*
|
||||
* @param mat The matrix to set
|
||||
* @param value
|
||||
*/
|
||||
static inline void cmat_set(cmat* mat,const c value){
|
||||
dbgassert(mat,NULLPTRDEREF);
|
||||
if(islikely(mat->n_cols * mat->n_rows > 0)) {
|
||||
for(us col=0;col<mat->n_cols;col++) {
|
||||
c_set(getcmatval(mat,0,col),value,mat->n_rows);
|
||||
}
|
||||
}
|
||||
}
|
||||
#define vc_set cmat_set
|
||||
|
||||
/**
|
||||
* Allocate data for a matrix of floating points
|
||||
*
|
||||
* @param n_rows Number of rows
|
||||
* @param n_cols Number of columns
|
||||
* @param p Memory pool
|
||||
*
|
||||
* @return dmat with allocated data
|
||||
*/
|
||||
static inline dmat dmat_alloc(us n_rows,
|
||||
us n_cols) {
|
||||
dmat result = { n_rows, n_cols,
|
||||
false,
|
||||
n_rows, // The column stride
|
||||
NULL};
|
||||
|
||||
#ifdef LASP_DEBUG
|
||||
result._data = (d*) a_malloc((n_rows*n_cols+1)*sizeof(d));
|
||||
result._data[n_rows*n_cols] = OVERFLOW_MAGIC_NUMBER;
|
||||
#else
|
||||
result._data = (d*) a_malloc((n_rows*n_cols)*sizeof(d));
|
||||
#endif // LASP_DEBUG
|
||||
|
||||
#ifdef LASP_DEBUG
|
||||
dmat_set(&result,NAN);
|
||||
#endif // LASP_DEBUG
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a matrix of complex floating points
|
||||
*
|
||||
* @param n_rows Number of rows
|
||||
* @param n_cols Number of columns
|
||||
* @param p Memory pool
|
||||
*
|
||||
* @return cmat with allocated data
|
||||
*/
|
||||
static inline cmat cmat_alloc(const us n_rows,
|
||||
const us n_cols) {
|
||||
cmat result = { n_rows, n_cols, false, n_rows, NULL};
|
||||
|
||||
#ifdef LASP_DEBUG
|
||||
result._data = (c*) a_malloc((n_rows*n_cols+1)*sizeof(c));
|
||||
result._data[n_rows*n_cols] = OVERFLOW_MAGIC_NUMBER;
|
||||
#else
|
||||
result._data = (c*) a_malloc((n_rows*n_cols)*sizeof(c));
|
||||
#endif // LASP_DEBUG
|
||||
|
||||
#ifdef LASP_DEBUG
|
||||
cmat_set(&result,NAN+I*NAN);
|
||||
#endif // LASP_DEBUG
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate data for a float vector.
|
||||
*
|
||||
* @param size Size of the vector
|
||||
*
|
||||
* @return vd with allocated data
|
||||
*/
|
||||
static inline vd vd_alloc(us size) {
|
||||
return dmat_alloc(size,1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate data for a complex vector.
|
||||
*
|
||||
* @param size Size of the vector
|
||||
*
|
||||
* @return vc with allocated data
|
||||
*/
|
||||
static inline vc vc_alloc(us size) {
|
||||
return cmat_alloc(size,1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a dmat from foreign data. Does not copy the data, but only
|
||||
* initializes the row pointers. Assumes column-major ordering for the
|
||||
* data. Please do not keep this one alive after the data has been
|
||||
* destroyed. Assumes the column colstride equals to n_rows.
|
||||
*
|
||||
* @param n_rows Number of rows
|
||||
* @param n_cols Number of columns
|
||||
* @param data
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
static inline dmat dmat_foreign(dmat* other) {
|
||||
dbgassert(other,NULLPTRDEREF);
|
||||
dmat result = {other->n_rows,
|
||||
other->n_cols,
|
||||
true,
|
||||
other->colstride,
|
||||
other->_data};
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Create a dmat from foreign data. Assumes the colstride of the data is
|
||||
* n_rows.
|
||||
*
|
||||
* @param n_rows Number of rows
|
||||
* @param n_cols Number of columns
|
||||
* @param data Pointer to data storage
|
||||
*
|
||||
* @return dmat
|
||||
*/
|
||||
static inline dmat dmat_foreign_data(us n_rows,
|
||||
us n_cols,
|
||||
d* data,
|
||||
bool own_data) {
|
||||
|
||||
dbgassert(data,NULLPTRDEREF);
|
||||
dmat result = {n_rows,
|
||||
n_cols,
|
||||
!own_data,
|
||||
n_rows,
|
||||
data};
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Create a cmat from foreign data. Assumes the colstride of the data is
|
||||
* n_rows.
|
||||
*
|
||||
* @param n_rows Number of rows
|
||||
* @param n_cols Number of columns
|
||||
* @param data Pointer to data storage
|
||||
*
|
||||
* @return dmat
|
||||
*/
|
||||
static inline cmat cmat_foreign_data(us n_rows,
|
||||
us n_cols,
|
||||
c* data,
|
||||
bool own_data) {
|
||||
|
||||
dbgassert(data,NULLPTRDEREF);
|
||||
cmat result = {n_rows,
|
||||
n_cols,
|
||||
!own_data,
|
||||
n_rows,
|
||||
data};
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cmat from foreign data. Does not copy the data, but only
|
||||
* initializes the row pointers. Assumes column-major ordering for the
|
||||
* data. Please do not keep this one alive after the data has been
|
||||
* destroyed. Assumes the column colstride equals to n_rows.
|
||||
*
|
||||
* @param n_rows
|
||||
* @param n_cols
|
||||
* @param data
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
static inline cmat cmat_foreign(cmat* other) {
|
||||
dbgassert(other,NULLPTRDEREF);
|
||||
cmat result = {other->n_rows,
|
||||
other->n_cols,
|
||||
true,
|
||||
other->colstride,
|
||||
other->_data};
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Free's data of dmat. Safe to run on sub-matrices as well.
|
||||
*
|
||||
* @param m Matrix to free
|
||||
*/
|
||||
static inline void dmat_free(dmat* m) {
|
||||
dbgassert(m,NULLPTRDEREF);
|
||||
if(!(m->_foreign_data)) a_free(m->_data);
|
||||
}
|
||||
#define vd_free dmat_free
|
||||
|
||||
/**
|
||||
* Free's data of dmat. Safe to run on sub-matrices as well.
|
||||
*
|
||||
* @param m Matrix to free
|
||||
*/
|
||||
static inline void cmat_free(cmat* m) {
|
||||
dbgassert(m,NULLPTRDEREF);
|
||||
if(!(m->_foreign_data)) a_free(m->_data);
|
||||
}
|
||||
#define vc_free cmat_free
|
||||
|
||||
/**
|
||||
* Copy some rows from one matrix to another
|
||||
*
|
||||
* @param to Matrix to copy to
|
||||
* @param from Matrix to copy from
|
||||
* @param startrow_from Starting row where to get the values
|
||||
* @param startrow_to Starting row where to insert the values
|
||||
* @param nrows Number of rows to copy
|
||||
*/
|
||||
static inline void dmat_copy_rows(dmat* to,const dmat* from,
|
||||
us startrow_to,
|
||||
us startrow_from,
|
||||
us nrows) {
|
||||
us col,ncols = to->n_cols;
|
||||
dbgassert(to && from,NULLPTRDEREF);
|
||||
dbgassert(to->n_cols == from->n_cols,SIZEINEQUAL);
|
||||
dbgassert(startrow_from+nrows <= from->n_rows,OUTOFBOUNDSMATR);
|
||||
dbgassert(startrow_to+nrows <= to->n_rows,OUTOFBOUNDSMATR);
|
||||
|
||||
for(col=0;col<ncols;col++) {
|
||||
d* to_d = getdmatval(to,startrow_to,col);
|
||||
d* from_d = getdmatval(from,startrow_from,col);
|
||||
d_copy(to_d,from_d,nrows,1,1);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Allocate a sub-matrix view of the parent
|
||||
*
|
||||
* @param parent Parent matrix
|
||||
* @param startrow Startrow
|
||||
* @param startcol Start column
|
||||
* @param n_rows Number of rows in sub-matrix
|
||||
* @param n_cols Number of columns in sub-matrix
|
||||
*
|
||||
* @return submatrix view
|
||||
*/
|
||||
static inline dmat dmat_submat(const dmat* parent,
|
||||
const us startrow,
|
||||
const us startcol,
|
||||
const us n_rows,
|
||||
const us n_cols) {
|
||||
|
||||
dbgassert(parent,NULLPTRDEREF);
|
||||
dbgassert(n_rows+startrow <= parent->n_rows,OUTOFBOUNDSMATR);
|
||||
dbgassert(n_cols+startcol <= parent->n_cols,OUTOFBOUNDSMATC);
|
||||
|
||||
dmat result = { n_rows,n_cols,
|
||||
true, // Foreign data = true
|
||||
parent->n_rows, // This is the colstride to get to
|
||||
// the next column.
|
||||
getdmatval(parent,startrow,startcol)};
|
||||
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Allocate a sub-matrix view of the parent
|
||||
*
|
||||
* @param parent Parent matrix
|
||||
* @param startrow Startrow
|
||||
* @param startcol Start column
|
||||
* @param n_rows Number of rows in sub-matrix
|
||||
* @param n_cols Number of columns in sub-matrix
|
||||
*
|
||||
* @return submatrix view
|
||||
*/
|
||||
static inline cmat cmat_submat(cmat* parent,
|
||||
const us startrow,
|
||||
const us startcol,
|
||||
const us n_rows,
|
||||
const us n_cols) {
|
||||
dbgassert(false,"untested");
|
||||
dbgassert(parent,NULLPTRDEREF);
|
||||
dbgassert(n_rows+startrow <= parent->n_rows,OUTOFBOUNDSMATR);
|
||||
dbgassert(n_cols+startcol <= parent->n_cols,OUTOFBOUNDSMATC);
|
||||
|
||||
|
||||
cmat result = { n_rows,n_cols,
|
||||
true, // Foreign data = true
|
||||
parent->n_rows, // This is the colstride to get to
|
||||
// the next column.
|
||||
getcmatval(parent,startrow,startcol)};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy contents of one matrix to another. Sizes should be equal
|
||||
*
|
||||
* @param to
|
||||
* @param from
|
||||
*/
|
||||
static inline void dmat_copy(dmat* to,const dmat* from) {
|
||||
dbgassert(to && from,NULLPTRDEREF);
|
||||
dbgassert(to->n_rows==from->n_rows,SIZEINEQUAL);
|
||||
dbgassert(to->n_cols==from->n_cols,SIZEINEQUAL);
|
||||
for(us col=0;col<to->n_cols;col++) {
|
||||
d_copy(getdmatval(to,0,col),
|
||||
getdmatval(from,0,col),
|
||||
to->n_rows,1,1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a new array, with size based on other.
|
||||
*
|
||||
* @param[in] from: Array to copy
|
||||
*/
|
||||
static inline dmat dmat_alloc_from_dmat(const dmat* from) {
|
||||
assertvalidptr(from);
|
||||
dmat thecopy = dmat_alloc(from->n_rows, from->n_cols);
|
||||
return thecopy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy contents of one matrix to another. Sizes should be equal
|
||||
*
|
||||
* @param to
|
||||
* @param from
|
||||
*/
|
||||
static inline void cmat_copy(cmat* to,const cmat* from) {
|
||||
dbgassert(to && from,NULLPTRDEREF);
|
||||
dbgassert(to->n_rows==from->n_rows,SIZEINEQUAL);
|
||||
dbgassert(to->n_cols==from->n_cols,SIZEINEQUAL);
|
||||
for(us col=0;col<to->n_cols;col++) {
|
||||
c_copy(getcmatval(to,0,col),
|
||||
getcmatval(from,0,col),
|
||||
to->n_rows);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy contents of one vector to another
|
||||
*
|
||||
* @param to : Vector to write to
|
||||
* @param from : Vector to read from
|
||||
*/
|
||||
static inline void vd_copy(vd* to,const vd* from) {
|
||||
dbgassert(to && from,NULLPTRDEREF);
|
||||
assert_vx(to);
|
||||
assert_vx(from);
|
||||
dmat_copy(to,from);
|
||||
}
|
||||
/**
|
||||
* Copy contents of one vector to another
|
||||
*
|
||||
* @param to : Vector to write to
|
||||
* @param from : Vector to read from
|
||||
*/
|
||||
static inline void vc_copy(vc* to,const vc* from) {
|
||||
dbgassert(to && from,NULLPTRDEREF);
|
||||
assert_vx(to);
|
||||
assert_vx(from);
|
||||
cmat_copy(to,from);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a reference to a column of a matrix as a vector
|
||||
*
|
||||
* @param x Matrix
|
||||
* @param col Column number
|
||||
*
|
||||
* @return vector with reference to column
|
||||
*/
|
||||
static inline vd dmat_column(dmat* x,us col) {
|
||||
vd res = {x->n_rows,1,true,x->colstride,getdmatval(x,0,col)};
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a reference to a column of a matrix as a vector
|
||||
*
|
||||
* @param x Matrix
|
||||
* @param col Column number
|
||||
*
|
||||
* @return vector with reference to column
|
||||
*/
|
||||
static inline vc cmat_column(cmat* x,us col) {
|
||||
vc res = {x->n_rows,1,true,x->colstride,getcmatval(x,0,col)};
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the complex conjugate of b and store result in a
|
||||
*
|
||||
* @param a
|
||||
* @param b
|
||||
*/
|
||||
static inline void cmat_conj(cmat* a,const cmat* b) {
|
||||
fsTRACE(15);
|
||||
dbgassert(a && b,NULLPTRDEREF);
|
||||
dbgassert(a->n_cols == b->n_cols,SIZEINEQUAL);
|
||||
dbgassert(a->n_rows == b->n_rows,SIZEINEQUAL);
|
||||
for(us col=0;col<a->n_cols;col++) {
|
||||
carray_conj(getcmatval(a,0,col),getcmatval(b,0,col),a->n_rows);
|
||||
}
|
||||
feTRACE(15);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the complex conjugate of x, in place
|
||||
*
|
||||
* @param x
|
||||
*/
|
||||
static inline void cmat_conj_inplace(cmat* x) {
|
||||
dbgassert(x,NULLPTRDEREF);
|
||||
for(us col=0;col<x->n_cols;col++) {
|
||||
c_conj_inplace(getcmatval(x,0,col),x->n_rows);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the maximum value for each row, returns a vector with maximum
|
||||
* values for each column in the matrix.
|
||||
*
|
||||
* @param x
|
||||
*/
|
||||
static inline vd dmat_max(const dmat x) {
|
||||
vd max_vals = vd_alloc(x.n_cols);
|
||||
d max_val = -d_inf;
|
||||
for(us j=0; j< x.n_cols; j++) {
|
||||
for(us i=0; i< x.n_rows; i++) {
|
||||
max_val = *getdmatval(&x, i, j) < max_val? max_val : *getdmatval(&x, i,j);
|
||||
}
|
||||
*getvdval(&max_vals, j) = max_val;
|
||||
}
|
||||
return max_vals;
|
||||
}
|
||||
|
||||
|
||||
#if LASP_DEBUG == 1
|
||||
|
||||
void print_cmat(const cmat* m);
|
||||
void print_dmat(const dmat* m);
|
||||
#define print_vc(x) assert_vx(x) print_cmat(x)
|
||||
#define print_vd(x) assert_vx(x) print_dmat(x)
|
||||
|
||||
#else
|
||||
#define print_cmat(m)
|
||||
#define print_dmat(m)
|
||||
#define print_vc(m)
|
||||
#define print_vd(m)
|
||||
|
||||
#endif // LASP_DEBUG == 1
|
||||
|
||||
#endif // LASP_MAT_H
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,152 +0,0 @@
|
||||
// lasp_math_raw.c
|
||||
//
|
||||
// last-edit-by: J.A. de Jong
|
||||
//
|
||||
// Description:
|
||||
// Operations working on raw arrays of floating point numbers
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#define TRACERPLUS (-5)
|
||||
#include "lasp_math_raw.h"
|
||||
#if LASP_USE_BLAS == 1
|
||||
#include <cblas.h>
|
||||
#endif
|
||||
|
||||
#ifdef __MKL_CBLAS_H__
|
||||
/* Symbol not present in MKL blas */
|
||||
#define blasint CBLAS_INDEX
|
||||
#else
|
||||
#endif
|
||||
|
||||
void d_elem_prod_d(d res[],
|
||||
const d arr1[],
|
||||
const d arr2[],
|
||||
const us size) {
|
||||
|
||||
#if LASP_USE_BLAS == 1
|
||||
|
||||
#if LASP_DEBUG
|
||||
|
||||
if(arr1 == arr2) {
|
||||
DBGWARN("d_elem_prod_d: Array 1 and array 2 point to the same"
|
||||
" memory. This results in pointer aliasing, for which"
|
||||
" testing is still to be done. Results might be"
|
||||
" unrealiable.");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if LASP_DOUBLE_PRECISION
|
||||
#define elem_prod_fun cblas_dsbmv
|
||||
#else
|
||||
#define elem_prod_fun cblas_ssbmv
|
||||
#endif
|
||||
/* These parameters do not matter for this specific case */
|
||||
const CBLAS_ORDER mat_order= CblasColMajor;
|
||||
const CBLAS_UPLO uplo = CblasLower;
|
||||
|
||||
/* Extra multiplication factor */
|
||||
const d alpha = 1.0;
|
||||
|
||||
/* void cblas_dsbmv(OPENBLAS_CONST enum CBLAS_ORDER order, */
|
||||
/* OPENBLAS_CONST enum CBLAS_UPLO Uplo, */
|
||||
/* OPENBLAS_CONST blasint N, */
|
||||
/* OPENBLAS_CONST blasint K, */
|
||||
/* OPENBLAS_CONST double alpha, */
|
||||
/* OPENBLAS_CONST double *A, */
|
||||
/* OPENBLAS_CONST blasint lda, */
|
||||
/* OPENBLAS_CONST double *X, */
|
||||
/* OPENBLAS_CONST blasint incX, */
|
||||
/* OPENBLAS_CONST double beta, */
|
||||
/* double *Y, */
|
||||
/* OPENBLAS_CONST blasint incY); */
|
||||
|
||||
elem_prod_fun(mat_order,
|
||||
uplo,
|
||||
(blasint) size,
|
||||
0, // Just the diagonal; 0 super-diagonal bands
|
||||
alpha, /* Multiplication factor alpha */
|
||||
arr1,
|
||||
1, /* LDA */
|
||||
arr2, /* x */
|
||||
1, /* incX = 1 */
|
||||
0.0, /* Beta */
|
||||
res, /* The Y matrix to write to */
|
||||
1); /* incY */
|
||||
#undef elem_prod_fun
|
||||
|
||||
#else /* No blas routines, routine is very simple, but here we
|
||||
* go! */
|
||||
DBGWARN("Performing slow non-blas vector-vector multiplication");
|
||||
for(us i=0;i<size;i++) {
|
||||
res[i] = arr1[i]*arr2[i];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void c_hadamard(c res[],
|
||||
const c arr1[],
|
||||
const c arr2[],
|
||||
const us size) {
|
||||
|
||||
fsTRACE(15);
|
||||
uVARTRACE(15,size);
|
||||
dbgassert(arr1 && arr2 && res,NULLPTRDEREF);
|
||||
|
||||
#if LASP_USE_BLAS == 1
|
||||
|
||||
#if LASP_DEBUG
|
||||
|
||||
if(arr1 == arr2) {
|
||||
DBGWARN("c_elem_prod_c: Array 1 and array 2 point to the same"
|
||||
" memory. This results in pointer aliasing, for which"
|
||||
" testing is still to be done. Results might be"
|
||||
" unrealiable.");
|
||||
}
|
||||
|
||||
#endif /* LASP_DEBUG */
|
||||
|
||||
|
||||
#if LASP_DOUBLE_PRECISION == 1
|
||||
#define elem_prod_fun cblas_zgbmv
|
||||
#else
|
||||
#define elem_prod_fun cblas_cgbmv
|
||||
#endif
|
||||
|
||||
c alpha = 1.0;
|
||||
c beta = 0.0;
|
||||
|
||||
TRACE(15,"Calling " annestr(elem_prod_fun));
|
||||
uVARTRACE(15,size);
|
||||
elem_prod_fun(CblasColMajor,
|
||||
CblasNoTrans,
|
||||
(blasint) size, /* M: Number of rows */
|
||||
(blasint) size, /* B: Number of columns */
|
||||
0, /* KL: Number of sub-diagonals */
|
||||
0, /* KU: Number of super-diagonals */
|
||||
(d*) &alpha, /* Multiplication factor */
|
||||
(d*) arr2, /* A */
|
||||
1, /* LDA */
|
||||
(d*) arr1, /* x */
|
||||
1, /* incX = 1 */
|
||||
(d*) &beta,
|
||||
(d*) res, /* The Y matrix to write to */
|
||||
1); /* incY (increment in res) */
|
||||
|
||||
#undef elem_prod_fun
|
||||
|
||||
#else /* No blas routines, routine is very simple, but here we
|
||||
* go! */
|
||||
DBGWARN("Performing slower non-blas vector-vector multiplication");
|
||||
for(us i=0;i<size;i++) {
|
||||
res[i] = arr1[i]*arr2[i];
|
||||
}
|
||||
#endif
|
||||
feTRACE(15);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -1,507 +0,0 @@
|
||||
// lasp_math_raw.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description: Raw math routines working on raw arrays of floats and
|
||||
// complex numbers.
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef LASP_MATH_RAW_H
|
||||
#define LASP_MATH_RAW_H
|
||||
#include "lasp_config.h"
|
||||
#include "lasp_assert.h"
|
||||
#include "lasp_tracer.h"
|
||||
#include "lasp_types.h"
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
#if LASP_USE_BLAS == 1
|
||||
#include <cblas.h>
|
||||
|
||||
#elif LASP_USE_BLAS == 0
|
||||
#else
|
||||
#error "LASP_USE_BLAS should be set to either 0 or 1"
|
||||
#endif
|
||||
|
||||
#if LASP_DOUBLE_PRECISION == 1
|
||||
#define c_real creal
|
||||
#define c_imag cimag
|
||||
#define d_abs fabs
|
||||
#define c_abs cabs
|
||||
#define c_conj conj
|
||||
#define d_atan2 atan2
|
||||
#define d_acos acos
|
||||
#define d_sqrt sqrt
|
||||
#define c_exp cexp
|
||||
#define d_sin sin
|
||||
#define d_cos cos
|
||||
#define d_pow pow
|
||||
#define d_log10 log10
|
||||
#define d_ln log
|
||||
#define d_epsilon (DBL_EPSILON)
|
||||
|
||||
#else // LASP_DOUBLE_PRECISION not defined
|
||||
#define c_conj conjf
|
||||
#define c_real crealf
|
||||
#define c_imag cimagf
|
||||
#define d_abs fabsf
|
||||
#define c_abs cabsf
|
||||
#define d_atan2 atan2f
|
||||
#define d_acos acosf
|
||||
#define d_sqrt sqrtf
|
||||
#define c_exp cexpf
|
||||
#define d_sin sinf
|
||||
#define d_cos cosf
|
||||
#define d_pow powf
|
||||
#define d_log10 log10f
|
||||
#define d_ln logf
|
||||
#define d_epsilon (FLT_EPSILON)
|
||||
#endif // LASP_DOUBLE_PRECISION
|
||||
|
||||
/// Positive infinite
|
||||
#define d_inf (INFINITY)
|
||||
|
||||
#ifdef M_PI
|
||||
static const d number_pi = M_PI;
|
||||
#else
|
||||
static const d number_pi = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Set all elements in an array equal to val
|
||||
*
|
||||
* @param to
|
||||
* @param val
|
||||
* @param size
|
||||
*/
|
||||
static inline void d_set(d to[],d val,us size) {
|
||||
for(us i=0;i<size;i++) {
|
||||
to[i]=val;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Set all elements in an array equal to val
|
||||
*
|
||||
* @param to
|
||||
* @param val
|
||||
* @param size
|
||||
*/
|
||||
static inline void c_set(c to[],c val,us size) {
|
||||
for(us i=0;i<size;i++) {
|
||||
to[i]=val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum of two doubles
|
||||
*
|
||||
* @param a value 1
|
||||
* @param b value 2
|
||||
*
|
||||
* @returns the maximum of value 1 and 2
|
||||
*/
|
||||
static inline d d_max(const d a,const d b) {
|
||||
return a>b?a:b;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the dot product of two arrays, one of them complex-valued,
|
||||
* the other real-valued
|
||||
*
|
||||
* @param a the complex-valued array
|
||||
* @param b the real-valued array
|
||||
* @param size the size of the arrays. *Should be equal-sized!*
|
||||
*
|
||||
* @return the dot product
|
||||
*/
|
||||
static inline c cd_dot(const c a[],const d b[],us size){
|
||||
c result = 0;
|
||||
us i;
|
||||
for(i=0;i<size;i++){
|
||||
result+=a[i]*b[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Return the dot product of two complex-valued arrays. Wraps BLAS
|
||||
* when LASP_USE_BLAS == 1.
|
||||
*
|
||||
* @param a complex-valued array
|
||||
* @param b complex-valued array
|
||||
* @param size the size of the arrays. *Should be equal-sized!*
|
||||
*
|
||||
* @return the dot product
|
||||
*/
|
||||
static inline c cc_dot(const c a[],const c b[],us size){
|
||||
#if LASP_USE_BLAS == 1
|
||||
WARN("CBlas zdotu not yet tested");
|
||||
#if LASP_DOUBLE_PRECISION == 1
|
||||
// Openblas and MKL blas provide some subroutines with a different name, we
|
||||
// correct for this fact here
|
||||
#ifdef __MKL_CBLAS_H__
|
||||
d res;
|
||||
cblas_zdotu_sub(size,(d*) a,1,(d*) b,1, &res);
|
||||
return res;
|
||||
#else
|
||||
return cblas_zdotu(size,(d*) a,1,(d*) b,1);
|
||||
#endif
|
||||
|
||||
#else
|
||||
#ifdef __MKL_CBLAS_H__
|
||||
d res;
|
||||
cblas_cdotu_sub(size,(d*) a,1,(d*) b,1, &res);
|
||||
return res;
|
||||
#endif
|
||||
return cblas_cdotu(size,(d*) a,1,(d*) b,1);
|
||||
#endif // LASP_DOUBLE_PRECISION
|
||||
#else
|
||||
c result = 0;
|
||||
us i;
|
||||
for(i=0;i<size;i++){
|
||||
result+=a[i]*b[i];
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the dot product of two real arrays.
|
||||
*
|
||||
* @param a First array.
|
||||
* @param b Second array.
|
||||
* @param size Size of the arrays.
|
||||
* @return The result.
|
||||
*/
|
||||
static inline d d_dot(const d a[],const d b[],const us size){
|
||||
#if LASP_USE_BLAS == 1
|
||||
#if LASP_DOUBLE_PRECISION
|
||||
return cblas_ddot(size,a,1,b,1);
|
||||
#else // Single precision function
|
||||
return cblas_sdot(size,a,1,b,1);
|
||||
#endif
|
||||
#else // No BLAS, do it manually
|
||||
d result = 0;
|
||||
us i;
|
||||
for(i=0;i<size;i++){
|
||||
result+=a[i]*b[i];
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy array of floats.
|
||||
*
|
||||
* @param to : Array to write to
|
||||
* @param from : Array to read from
|
||||
* @param size : Size of arrays
|
||||
* @param to_inc : Mostly equal to 1, the stride of the array to copy to
|
||||
* @param from_inc : Mostly equal to 1, the stride of the array to copy from
|
||||
*/
|
||||
static inline void d_copy(d to[],
|
||||
const d from[],
|
||||
const us size,
|
||||
const us to_inc,
|
||||
const us from_inc){
|
||||
#if LASP_USE_BLAS == 1
|
||||
cblas_dcopy(size,from,from_inc,to,to_inc);
|
||||
#else
|
||||
us i;
|
||||
for(i=0;i<size;i++)
|
||||
to[i*to_inc] = from[i*from_inc];
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy array of floats to array of complex floats. Imaginary part set
|
||||
* to zero.
|
||||
*
|
||||
* @param to : Array to write to
|
||||
* @param from : Array to read from
|
||||
* @param size : Size of arrays
|
||||
*/
|
||||
static inline void cd_copy(c to[],const d from[],const us size) {
|
||||
us i;
|
||||
for(i=0;i<size;i++) {
|
||||
to[i] = (c) (from[i]);
|
||||
dbgassert(cimag(to[i]) == 0,"Imaginary part not equal to zero");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy float vector to complex vector. Imaginary part set
|
||||
* to zero.
|
||||
*
|
||||
* @param to : Vector to write to
|
||||
* @param from : Vector to read from
|
||||
*/
|
||||
static inline void c_copy(c to[],const c from[],const us size){
|
||||
|
||||
#if LASP_USE_BLAS == 1
|
||||
#if LASP_DOUBLE_PRECISION == 1
|
||||
cblas_zcopy(size,(d*) from,1,(d*) to,1);
|
||||
#else
|
||||
cblas_ccopy(size,(d*) from,1,(d*) to,1);
|
||||
#endif
|
||||
#else
|
||||
us i;
|
||||
for(i=0;i<size;i++)
|
||||
to[i] = from[i];
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* Multiply y with fac, and add result to x
|
||||
*
|
||||
* @param x[in,out] Array to add to
|
||||
* @param y[in] Array to add to x
|
||||
* @param[in] fac Factor with which to multiply y
|
||||
* @param[in] size Size of the arrays
|
||||
*/
|
||||
static inline void d_add_to(d x[],const d y[],
|
||||
const d fac,const us size){
|
||||
#if LASP_USE_BLAS == 1
|
||||
#if LASP_DOUBLE_PRECISION == 1
|
||||
cblas_daxpy(size,fac,y,1,x,1);
|
||||
#else
|
||||
cblas_saxpy(size,fac,y,1,x,1);
|
||||
#endif
|
||||
#else
|
||||
us i;
|
||||
for(i=0;i<size;i++)
|
||||
x[i]+=fac*y[i];
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* x = x + fac*y
|
||||
*
|
||||
* @param[in,out] x Array to add to
|
||||
* @param[in] y Array to add to x
|
||||
* @param[in] fac Factor with which to multiply y
|
||||
* @param[in] size Size of the arrays
|
||||
*/
|
||||
static inline void c_add_to(c x[],const c y[],
|
||||
const c fac,const us size){
|
||||
fsTRACE(15);
|
||||
#if LASP_USE_BLAS == 1
|
||||
#if LASP_DOUBLE_PRECISION == 1
|
||||
cblas_zaxpy(size,(d*) &fac,(d*) y,1,(d*) x,1);
|
||||
#else
|
||||
cblas_caxpy(size,(d*) &fac,(d*) y,1,(d*) x,1);
|
||||
#endif
|
||||
#else
|
||||
us i;
|
||||
for(i=0;i<size;i++)
|
||||
x[i]+=fac*y[i];
|
||||
#endif
|
||||
feTRACE(15);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scale an array of doubles
|
||||
*
|
||||
* @param a array
|
||||
* @param scale_fac scale factor
|
||||
* @param size size of the array
|
||||
*/
|
||||
static inline void d_scale(d a[],const d scale_fac,us size){
|
||||
#if LASP_USE_BLAS == 1
|
||||
#if LASP_DOUBLE_PRECISION == 1
|
||||
cblas_dscal(size,scale_fac,a,1);
|
||||
#else
|
||||
cblas_sscal(size,scale_fac,a,1);
|
||||
#endif
|
||||
#else
|
||||
us i;
|
||||
for(i=0;i<size;i++)
|
||||
a[i]*=scale_fac;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scale an array of complex floats
|
||||
*
|
||||
* @param a array
|
||||
* @param scale_fac scale factor
|
||||
* @param size size of the array
|
||||
*/
|
||||
static inline void c_scale(c a[],const c scale_fac,us size){
|
||||
#if LASP_USE_BLAS == 1
|
||||
// Complex argument should be given in as array of two double
|
||||
// values. The first the real part, the second the imaginary
|
||||
// part. Fortunately the (c) type stores the two values in this
|
||||
// order. To be portable and absolutely sure anything goes well,
|
||||
// we convert it explicitly here.
|
||||
|
||||
#if LASP_DOUBLE_PRECISION == 1
|
||||
cblas_zscal(size,(d*) &scale_fac,(d*) a,1);
|
||||
#else
|
||||
cblas_cscal(size,(d*) &scale_fac,(d*) a,1);
|
||||
#endif
|
||||
#else
|
||||
us i;
|
||||
for(i=0;i<size;i++)
|
||||
a[i]*=scale_fac;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute the maximum value of an array
|
||||
*
|
||||
* @param a array
|
||||
* @param size size of the array
|
||||
* @return maximum
|
||||
*/
|
||||
static inline d darray_max(const d a[],us size){
|
||||
us i;
|
||||
d max = a[0];
|
||||
for(i=1;i<size;i++){
|
||||
if(a[i] > max) max=a[i];
|
||||
}
|
||||
return max;
|
||||
}
|
||||
/**
|
||||
* Compute the minimum of an array
|
||||
*
|
||||
* @param a array
|
||||
* @param size size of the array
|
||||
* @return minimum
|
||||
*/
|
||||
static inline d d_min(const d a[],us size){
|
||||
us i;
|
||||
d min = a[0];
|
||||
for(i=1;i<size;i++){
|
||||
if(a[i] > min) min=a[i];
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the \f$ L_2 \f$ norm of an array of doubles
|
||||
*
|
||||
* @param a Array
|
||||
* @param size Size of array
|
||||
*/
|
||||
static inline d d_norm(const d a[],us size){
|
||||
#if LASP_USE_BLAS == 1
|
||||
return cblas_dnrm2(size,a,1);
|
||||
#else
|
||||
d norm = 0;
|
||||
us i;
|
||||
for(i=0;i<size;i++){
|
||||
norm+=a[i]*a[i];
|
||||
}
|
||||
norm = d_sqrt(norm);
|
||||
return norm;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the \f$ L_2 \f$ norm of an array of complex floats
|
||||
*
|
||||
* @param a Array
|
||||
* @param size Size of array
|
||||
*/
|
||||
static inline d c_norm(const c a[],us size){
|
||||
#if LASP_USE_BLAS == 1
|
||||
return cblas_dznrm2(size,(d*) a,1);
|
||||
#else
|
||||
d norm = 0;
|
||||
us i;
|
||||
for(i=0;i<size;i++){
|
||||
d absa = c_abs(a[i]);
|
||||
norm+=absa*absa;
|
||||
}
|
||||
norm = d_sqrt(norm);
|
||||
return norm;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Computes the element-wise vector product, or Hadamard product of
|
||||
* arr1 and arr2
|
||||
*
|
||||
* @param res Where the result will be stored
|
||||
* @param arr1 Array 1
|
||||
* @param vec2 Array 2
|
||||
* @param size: Size of the arrays
|
||||
*/
|
||||
void d_elem_prod_d(d res[],
|
||||
const d arr1[],
|
||||
const d arr2[],
|
||||
const us size);
|
||||
|
||||
/**
|
||||
* Computes the element-wise vector product, or Hadamard product of
|
||||
* arr1 and arr2 for complex floats
|
||||
*
|
||||
* @param res Where the result will be stored
|
||||
* @param arr1 Array 1
|
||||
* @param vec2 Array 2
|
||||
* @param size: Size of the arrays
|
||||
*/
|
||||
void c_hadamard(c res[],
|
||||
const c arr1[],
|
||||
const c arr2[],
|
||||
const us size);
|
||||
|
||||
/**
|
||||
* Compute the complex conjugate of a complex vector and store the
|
||||
* result.
|
||||
*
|
||||
* @param res Result vector
|
||||
* @param in Input vector
|
||||
* @param size Size of the vector
|
||||
*/
|
||||
static inline void carray_conj(c res[],const c in[],const us size) {
|
||||
// First set the result vector to zero
|
||||
fsTRACE(15);
|
||||
c_set(res,0,size);
|
||||
#if LASP_USE_BLAS == 1
|
||||
#if LASP_DOUBLE_PRECISION == 1
|
||||
// Cast as a float, scale all odd elements with minus one to find
|
||||
// the complex conjugate.
|
||||
cblas_daxpy(size ,1.0,(d*) in,2,(d*) res,2);
|
||||
cblas_daxpy(size,-1.0,&((d*) in)[1],2,&((d*) res)[1],2);
|
||||
#else
|
||||
cblas_faxpy(size ,1,(d*) in,2,(d*) res,2);
|
||||
cblas_faxpy(size,-1,&((d*) in)[1],2,&((d*) res)[1],2);
|
||||
#endif // LASP_DOUBLE_PRECISION
|
||||
#else
|
||||
for(us i=0;i<size;i++) {
|
||||
res[i] = c_conj(in[i]);
|
||||
}
|
||||
#endif // LASP_USE_BLAS
|
||||
feTRACE(15);
|
||||
}
|
||||
/**
|
||||
* In place complex conjugation
|
||||
*
|
||||
* @param res Result vector
|
||||
* @param size Size of the vector
|
||||
*/
|
||||
static inline void c_conj_inplace(c res[],us size) {
|
||||
#if LASP_USE_BLAS
|
||||
#if LASP_DOUBLE_PRECISION == 1
|
||||
// Cast as a float, scale all odd elements with minus one to find
|
||||
// the complex conjugate.
|
||||
cblas_dscal(size,-1,&((d*) res)[1],2);
|
||||
#else
|
||||
cblas_sscal(size,-1,&((d*) res)[1],2);
|
||||
#endif // LASP_DOUBLE_PRECISION
|
||||
#else
|
||||
for(us i=0;i<size;i++) {
|
||||
res[i] = c_conj(res[i]);
|
||||
}
|
||||
#endif // LASP_USE_BLAS
|
||||
}
|
||||
|
||||
#endif // LASP_MATH_RAW_H
|
||||
//////////////////////////////////////////////////////////////////////
|
367
lasp/c/lasp_mq.c
367
lasp/c/lasp_mq.c
@ -1,367 +0,0 @@
|
||||
// lasp_mq.c
|
||||
//
|
||||
// Author: J.A. de Jong -ASCEE
|
||||
//
|
||||
// Description: Message queue implementation using a linked
|
||||
// list. Using mutexes and condition variables to sync access between
|
||||
// different threads.
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#define TRACERPLUS (-6)
|
||||
#include "lasp_types.h"
|
||||
#include "lasp_tracer.h"
|
||||
#include "lasp_assert.h"
|
||||
#include "lasp_alloc.h"
|
||||
#include "lasp_mq.h"
|
||||
#include <pthread.h>
|
||||
|
||||
/* #ifdef linux */
|
||||
#define _GNU_SOURCE
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#ifndef MS_WIN64
|
||||
#include <sys/syscall.h>
|
||||
#endif
|
||||
/* #endif */
|
||||
|
||||
typedef struct {
|
||||
void* job_ptr;
|
||||
bool running;
|
||||
bool ready;
|
||||
} Job;
|
||||
|
||||
typedef struct JobQueue_s {
|
||||
#ifdef LASP_PARALLEL
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cv_plus; /**< Condition variable for the
|
||||
* "workers". */
|
||||
pthread_cond_t cv_minus; /**< Condition variable for the
|
||||
* main thread. */
|
||||
#endif
|
||||
Job* jobs; /**< Pointer to job vector */
|
||||
us max_jobs; /**< Stores the maximum number of
|
||||
* items */
|
||||
} JobQueue;
|
||||
|
||||
static us count_jobs(JobQueue* jq) {
|
||||
fsTRACE(15);
|
||||
us njobs = 0;
|
||||
for(us i=0;i<jq->max_jobs;i++){
|
||||
if(jq->jobs[i].ready)
|
||||
njobs++;
|
||||
}
|
||||
return njobs;
|
||||
}
|
||||
static Job* get_ready_job(JobQueue* jq) {
|
||||
fsTRACE(15);
|
||||
Job* j = jq->jobs;
|
||||
for(us i=0;i<jq->max_jobs;i++){
|
||||
if(j->ready && !j->running)
|
||||
return j;
|
||||
j++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
void print_job_queue(JobQueue* jq) {
|
||||
fsTRACE(15);
|
||||
for(us i=0;i<jq->max_jobs;i++) {
|
||||
printf("Job %zu", i);
|
||||
if(jq->jobs[i].ready)
|
||||
printf(" available");
|
||||
if(jq->jobs[i].running)
|
||||
printf(" running");
|
||||
|
||||
printf(" - ptr %zu\n", (us) jq->jobs[i].job_ptr);
|
||||
|
||||
}
|
||||
feTRACE(15);
|
||||
}
|
||||
|
||||
#ifdef LASP_PARALLEL
|
||||
#define LOCK_MUTEX \
|
||||
/* Lock the mutex to let the threads wait initially */ \
|
||||
int rv = pthread_mutex_lock(&jq->mutex); \
|
||||
if(rv !=0) { \
|
||||
WARN("Mutex lock failed"); \
|
||||
}
|
||||
#define UNLOCK_MUTEX \
|
||||
rv = pthread_mutex_unlock(&jq->mutex); \
|
||||
if(rv !=0) { \
|
||||
WARN("Mutex unlock failed"); \
|
||||
}
|
||||
|
||||
#else
|
||||
#define LOCK_MUTEX
|
||||
#define UNLOCK_MUTEX
|
||||
#endif // LASP_PARALLEL
|
||||
|
||||
JobQueue* JobQueue_alloc(const us max_jobs) {
|
||||
TRACE(15,"JobQueue_alloc");
|
||||
/* if(max_jobs > LASP_MAX_NUM_CHANNELS) { */
|
||||
/* WARN("Max jobs restricted to LASP_MAX_NUM_CHANNELS"); */
|
||||
/* return NULL; */
|
||||
/* } */
|
||||
JobQueue* jq = a_malloc(sizeof(JobQueue));
|
||||
|
||||
|
||||
if(!jq) {
|
||||
WARN("Allocation of JobQueue failed");
|
||||
return NULL;
|
||||
}
|
||||
jq->max_jobs = max_jobs;
|
||||
|
||||
jq->jobs = a_malloc(max_jobs*sizeof(Job));
|
||||
if(!jq->jobs) {
|
||||
WARN("Allocation of JobQueue jobs failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Job* j = jq->jobs;
|
||||
for(us jindex=0;jindex<max_jobs;jindex++) {
|
||||
j->job_ptr = NULL;
|
||||
j->ready = false;
|
||||
j->running = false;
|
||||
j++;
|
||||
}
|
||||
|
||||
#ifdef LASP_PARALLEL
|
||||
/* Initialize thread mutex */
|
||||
int rv = pthread_mutex_init(&jq->mutex,NULL);
|
||||
if(rv !=0) {
|
||||
WARN("Mutex initialization failed");
|
||||
return NULL;
|
||||
}
|
||||
rv = pthread_cond_init(&jq->cv_plus,NULL);
|
||||
if(rv !=0) {
|
||||
WARN("Condition variable initialization failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rv = pthread_cond_init(&jq->cv_minus,NULL);
|
||||
if(rv !=0) {
|
||||
WARN("Condition variable initialization failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif // LASP_PARALLEL
|
||||
/* print_job_queue(jq); */
|
||||
return jq;
|
||||
}
|
||||
|
||||
void JobQueue_free(JobQueue* jq) {
|
||||
|
||||
TRACE(15,"JobQueue_free");
|
||||
dbgassert(jq,NULLPTRDEREF "jq in JobQueue_free");
|
||||
|
||||
int rv;
|
||||
|
||||
if(count_jobs(jq) != 0) {
|
||||
WARN("Job queue not empty!");
|
||||
}
|
||||
|
||||
a_free(jq->jobs);
|
||||
|
||||
#ifdef LASP_PARALLEL
|
||||
/* Destroy the mutexes and condition variables */
|
||||
rv = pthread_mutex_destroy(&jq->mutex);
|
||||
if(rv != 0){
|
||||
WARN("Mutex destroy failed. Do not know what to do.");
|
||||
}
|
||||
|
||||
rv = pthread_cond_destroy(&jq->cv_plus);
|
||||
if(rv != 0){
|
||||
WARN("Condition variable destruction failed. "
|
||||
"Do not know what to do.");
|
||||
}
|
||||
|
||||
rv = pthread_cond_destroy(&jq->cv_minus);
|
||||
if(rv != 0){
|
||||
WARN("Condition variable destruction failed. "
|
||||
"Do not know what to do.");
|
||||
}
|
||||
#endif // LASP_PARALLEL
|
||||
|
||||
}
|
||||
|
||||
int JobQueue_push(JobQueue* jq,void* job_ptr) {
|
||||
|
||||
TRACE(15,"JobQueue_push");
|
||||
dbgassert(jq,NULLPTRDEREF "jq in JobQueue_push");
|
||||
|
||||
/* print_job_queue(jq); */
|
||||
/* uVARTRACE(15,(us) job_ptr); */
|
||||
|
||||
LOCK_MUTEX;
|
||||
|
||||
us max_jobs = jq->max_jobs;
|
||||
|
||||
#ifdef LASP_PARALLEL
|
||||
/* Check if queue is full */
|
||||
while(count_jobs(jq) == max_jobs) {
|
||||
|
||||
WARN("Queue full. Wait until some jobs are done.");
|
||||
rv = pthread_cond_wait(&jq->cv_minus,&jq->mutex);
|
||||
if(rv !=0) {
|
||||
WARN("Condition variable wait failed");
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* If job queue is full, not in parallel, we just fail to add something
|
||||
* without waiting*/
|
||||
if(count_jobs(jq) == max_jobs) {
|
||||
return LASP_FAILURE;
|
||||
}
|
||||
#endif // LASP_PARALLEL
|
||||
|
||||
dbgassert(count_jobs(jq) != max_jobs,
|
||||
"Queue cannot be full!");
|
||||
|
||||
/* Queue is not full try to find a place, fill it */
|
||||
Job* j = jq->jobs;
|
||||
us i;
|
||||
for(i=0;i<max_jobs;i++) {
|
||||
if(j->ready == false ) {
|
||||
dbgassert(j->job_ptr==NULL,"Job ptr should be 0");
|
||||
dbgassert(j->ready==false,"Job cannot be assigned");
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
dbgassert(i!=jq->max_jobs,"Should have found a job!");
|
||||
|
||||
j->job_ptr = job_ptr;
|
||||
j->ready = true;
|
||||
|
||||
#ifdef LASP_PARALLEL
|
||||
/* Notify worker threads that a new job has arrived */
|
||||
if(count_jobs(jq) == max_jobs) {
|
||||
/* Notify ALL threads. Action required! */
|
||||
rv = pthread_cond_broadcast(&jq->cv_plus);
|
||||
if(rv !=0) {
|
||||
WARN("Condition variable broadcast failed");
|
||||
}
|
||||
|
||||
} else {
|
||||
/* Notify some thread that there has been some change to
|
||||
* the Queue */
|
||||
rv = pthread_cond_signal(&jq->cv_plus);
|
||||
if(rv !=0) {
|
||||
WARN("Condition variable signal failed");
|
||||
}
|
||||
}
|
||||
#endif // LASP_PARALLEL
|
||||
|
||||
/* print_job_queue(jq); */
|
||||
|
||||
UNLOCK_MUTEX;
|
||||
|
||||
return LASP_SUCCESS;
|
||||
}
|
||||
void* JobQueue_assign(JobQueue* jq) {
|
||||
|
||||
TRACE(15,"JobQueue_assign");
|
||||
|
||||
LOCK_MUTEX;
|
||||
|
||||
Job* j;
|
||||
#ifdef LASP_PARALLEL
|
||||
/* Wait until a job is available */
|
||||
while ((j=get_ready_job(jq))==NULL) {
|
||||
|
||||
TRACE(15,"JobQueue_assign: no ready job");
|
||||
pthread_cond_wait(&jq->cv_plus,&jq->mutex);
|
||||
|
||||
}
|
||||
#else
|
||||
if(count_jobs(jq) == 0) { return NULL; }
|
||||
else { j = get_ready_job(jq); }
|
||||
#endif // LASP_PARALLEL
|
||||
|
||||
TRACE(16,"JobQueue_assign: found ready job. Assigned to:");
|
||||
#ifdef LASP_DEBUG
|
||||
#ifdef LASP_PARALLEL
|
||||
pthread_t thisthread = pthread_self();
|
||||
iVARTRACE(16,thisthread);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* print_job_queue(jq); */
|
||||
/* Find a job from the queue, assign it and return it */
|
||||
j->running = true;
|
||||
|
||||
#ifdef LASP_PARALLEL
|
||||
if(count_jobs(jq) > 1) {
|
||||
/* Signal different thread that there is more work to do */
|
||||
rv = pthread_cond_signal(&jq->cv_plus);
|
||||
if(rv !=0) {
|
||||
WARN("Condition variable broadcast failed");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
UNLOCK_MUTEX;
|
||||
|
||||
TRACE(15,"End JobQueue_assign");
|
||||
|
||||
return j->job_ptr;
|
||||
}
|
||||
void JobQueue_done(JobQueue* jq,void* job_ptr) {
|
||||
|
||||
TRACE(15,"JobQueue_done");
|
||||
dbgassert(jq,NULLPTRDEREF "jq in JobQueue_done");
|
||||
|
||||
LOCK_MUTEX;
|
||||
|
||||
/* print_job_queue(jq); */
|
||||
|
||||
/* Find the job from the queue, belonging to the job_ptr */
|
||||
Job* j=jq->jobs;
|
||||
us i;
|
||||
for(i=0;i<jq->max_jobs;i++) {
|
||||
iVARTRACE(10,i);
|
||||
if(j->ready && j->running && j->job_ptr == job_ptr) {
|
||||
TRACE(15,"Found the job that has been done:");
|
||||
j->ready = false;
|
||||
j->job_ptr = NULL;
|
||||
j->running = false;
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
|
||||
/* print_job_queue(jq); */
|
||||
|
||||
#ifdef LASP_PARALLEL
|
||||
/* Job done, broadcast this */
|
||||
rv = pthread_cond_signal(&jq->cv_minus);
|
||||
if(rv !=0) {
|
||||
WARN("Condition variable broadcast failed");
|
||||
}
|
||||
#endif
|
||||
|
||||
UNLOCK_MUTEX;
|
||||
}
|
||||
|
||||
#ifdef LASP_PARALLEL
|
||||
void JobQueue_wait_alldone(JobQueue* jq) {
|
||||
TRACE(15,"JobQueue_wait_alldone");
|
||||
dbgassert(jq,NULLPTRDEREF "jq in JobQueue_wait_alldone");
|
||||
|
||||
LOCK_MUTEX;
|
||||
|
||||
/* Wait until number of jobs is 0 */
|
||||
while (count_jobs(jq)!=0) {
|
||||
|
||||
if(rv !=0) {
|
||||
WARN("Condition variable broadcast failed");
|
||||
}
|
||||
|
||||
pthread_cond_wait(&jq->cv_minus,&jq->mutex);
|
||||
}
|
||||
|
||||
UNLOCK_MUTEX;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,78 +0,0 @@
|
||||
// mq.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description:
|
||||
// Multithreaded job queue implementation
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef MQ_H
|
||||
#define MQ_H
|
||||
#include "lasp_types.h"
|
||||
|
||||
typedef struct JobQueue_s JobQueue;
|
||||
|
||||
/**
|
||||
* Allocate a new job queue.
|
||||
*
|
||||
* @param max_msg Maximum number of jobs that can be put in the
|
||||
* queue.
|
||||
*
|
||||
* @return Pointer to new JobQueue instance. NULL on error.
|
||||
*/
|
||||
JobQueue* JobQueue_alloc(const us max_msg);
|
||||
|
||||
/**
|
||||
* Free an existing job queue. If it is not empty and threads are
|
||||
* still waiting for jobs, the behaviour is undefined. So please
|
||||
* make sure all threads are done before free'ing the queue.
|
||||
*
|
||||
* @param jq: JobQueue to free
|
||||
*/
|
||||
void JobQueue_free(JobQueue* jq);
|
||||
|
||||
/**
|
||||
* Pops a job from the queue. Waits indefinitely until some job is
|
||||
* available. If in parallel mode. In serial mode, it returns NULL if no job
|
||||
* is available (LASP_PARALLEL compilation flag not set).
|
||||
*
|
||||
* @param jq: JobQueue handle
|
||||
* @return Pointer to the job, NULL on error.
|
||||
*/
|
||||
void* JobQueue_assign(JobQueue* jq);
|
||||
|
||||
/**
|
||||
* Tell the queue the job that has been popped is done. Only after
|
||||
* this function call, the job is really removed from the queue.
|
||||
*
|
||||
* @param jq: JobQueue handle
|
||||
* @param job
|
||||
*/
|
||||
void JobQueue_done(JobQueue* jq,void* job);
|
||||
|
||||
/**
|
||||
* A push on the job queue will notify one a single thread that is
|
||||
* blocked waiting in the JobQueue_assign() function. If the job
|
||||
* queue is full, however all waiters will be signaled and the
|
||||
* function will block until there is some space in the job queue.
|
||||
*
|
||||
* @param jp JobQueue
|
||||
* @param job_ptr Pointer to job to be done
|
||||
* @return 0 on success.
|
||||
*/
|
||||
int JobQueue_push(JobQueue* jq,void* job_ptr);
|
||||
|
||||
/**
|
||||
* Wait until the job queue is empty. Please use this function with
|
||||
* caution, as it will block indefinitely in case the queue never gets
|
||||
* empty. The purpose of this function is to let the main thread wait
|
||||
* until all task workers are finished.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef LASP_PARALLEL
|
||||
void JobQueue_wait_alldone(JobQueue*);
|
||||
#endif // LASP_PARALLEL
|
||||
|
||||
#endif // MQ_H
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,18 +0,0 @@
|
||||
#include "lasp_nprocs.h"
|
||||
#ifdef MS_WIN64
|
||||
#include <windows.h>
|
||||
#else
|
||||
// Used for obtaining the number of processors
|
||||
#include <sys/sysinfo.h>
|
||||
#endif
|
||||
|
||||
us getNumberOfProcs() {
|
||||
#if MS_WIN64
|
||||
// https://stackoverflow.com/questions/150355/programmatically-find-the-number-of-cores-on-a-machine
|
||||
SYSTEM_INFO sysinfo;
|
||||
GetSystemInfo(&sysinfo);
|
||||
return sysinfo.dwNumberOfProcessors;
|
||||
#else // Linux, easy
|
||||
return get_nprocs();
|
||||
#endif
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
// lasp_nprocs.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description: Implemententation of a function to determine the number
|
||||
// of processors.
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef LASP_NPROCS_H
|
||||
#define LASP_NPROCS_H
|
||||
#include "lasp_types.h"
|
||||
|
||||
/**
|
||||
* @return The number of SMP processors
|
||||
*/
|
||||
us getNumberOfProcs();
|
||||
|
||||
#endif // LASP_NPROCS_H
|
181
lasp/c/lasp_ps.c
181
lasp/c/lasp_ps.c
@ -1,181 +0,0 @@
|
||||
// lasp_ps.c
|
||||
//
|
||||
// Author: J.A. de Jong -ASCEE
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#define TRACERPLUS (-5)
|
||||
#include "lasp_ps.h"
|
||||
#include "lasp_fft.h"
|
||||
#include "lasp_alloc.h"
|
||||
#include "lasp_alg.h"
|
||||
#include "lasp_assert.h"
|
||||
|
||||
typedef struct PowerSpectra_s {
|
||||
|
||||
vd window;
|
||||
d win_pow; /**< The power of the window */
|
||||
Fft* fft; /**< Fft routines storage */
|
||||
} PowerSpectra;
|
||||
|
||||
PowerSpectra* PowerSpectra_alloc(const us nfft,
|
||||
const WindowType wt) {
|
||||
|
||||
fsTRACE(15);
|
||||
int rv;
|
||||
|
||||
/* Check nfft */
|
||||
if(nfft % 2 != 0) {
|
||||
WARN("nfft should be even");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ALlocate space */
|
||||
Fft* fft = Fft_create(nfft);
|
||||
if(fft == NULL) {
|
||||
WARN("Fft allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PowerSpectra* ps = a_malloc(sizeof(PowerSpectra));
|
||||
if(!ps) {
|
||||
WARN("Allocation of PowerSpectra memory failed");
|
||||
Fft_free(fft);
|
||||
return NULL;
|
||||
}
|
||||
ps->fft = fft;
|
||||
|
||||
/* Allocate vectors and matrices */
|
||||
ps->window = vd_alloc(nfft);
|
||||
|
||||
rv = window_create(wt,&ps->window,&ps->win_pow);
|
||||
check_overflow_vx(ps->window);
|
||||
if(rv!=0) {
|
||||
WARN("Error creating window function, continuing anyway");
|
||||
}
|
||||
feTRACE(15);
|
||||
return ps;
|
||||
}
|
||||
|
||||
void PowerSpectra_free(PowerSpectra* ps) {
|
||||
fsTRACE(15);
|
||||
Fft_free(ps->fft);
|
||||
vd_free(&ps->window);
|
||||
a_free(ps);
|
||||
feTRACE(15);
|
||||
}
|
||||
|
||||
|
||||
void PowerSpectra_compute(const PowerSpectra* ps,
|
||||
const dmat * timedata,
|
||||
cmat * result) {
|
||||
|
||||
fsTRACE(15);
|
||||
|
||||
dbgassert(ps && timedata && result,NULLPTRDEREF);
|
||||
|
||||
const us nchannels = timedata->n_cols;
|
||||
const us nfft = Fft_nfft(ps->fft);
|
||||
uVARTRACE(15,nchannels);
|
||||
const d win_pow = ps->win_pow;
|
||||
dVARTRACE(15,win_pow);
|
||||
|
||||
/* Sanity checks for the matrices */
|
||||
dbgassert(timedata->n_rows == nfft,"timedata n_rows "
|
||||
"should be equal to nfft");
|
||||
|
||||
dbgassert(result->n_rows == nfft/2+1,"result n_rows "
|
||||
"should be equal to nfft/2+1");
|
||||
|
||||
dbgassert(result->n_cols == nchannels*nchannels,"result n_cols "
|
||||
"should be equal to nchannels*nchannels");
|
||||
|
||||
|
||||
/* Multiply time data with the window and store result in
|
||||
* timedata_work. */
|
||||
dmat timedata_work = dmat_alloc(nfft,nchannels);
|
||||
for(us i=0;i<nchannels;i++) {
|
||||
vd column = dmat_column((dmat*) timedata,i);
|
||||
vd column_work = dmat_column(&timedata_work,i);
|
||||
|
||||
vd_elem_prod(&column_work,&column,&ps->window);
|
||||
|
||||
vd_free(&column);
|
||||
vd_free(&column_work);
|
||||
}
|
||||
check_overflow_xmat(timedata_work);
|
||||
/* print_dmat(&timedata_work); */
|
||||
|
||||
/* Compute fft of the time data */
|
||||
cmat fft_work = cmat_alloc(nfft/2+1,nchannels);
|
||||
Fft_fft(ps->fft,
|
||||
&timedata_work,
|
||||
&fft_work);
|
||||
|
||||
dmat_free(&timedata_work);
|
||||
|
||||
TRACE(15,"fft done");
|
||||
|
||||
/* Scale fft such that power is easily comxputed */
|
||||
const c scale_fac = d_sqrt(2/win_pow)/nfft;
|
||||
|
||||
/* POWER SPECTRAL DENSITY? Scale fft such that power is easily computed
|
||||
* - Multiply power spectral density by 2 except at f=0 and f=fNq
|
||||
* - Divide by energy of window function = nfft * window_power
|
||||
* - .. sqrt(factors) because it is applied to output fft instead of psd */
|
||||
// const c scale_fac = d_sqrt(2/(nfft*win_pow));
|
||||
|
||||
cmat_scale(&fft_work,scale_fac);
|
||||
TRACE(15,"scale done");
|
||||
|
||||
for(us i=0;i< nchannels;i++) {
|
||||
/* Multiply DC term by 1/sqrt(2) */
|
||||
*getcmatval(&fft_work,0,i) *= 1/d_sqrt(2.)+0*I;
|
||||
|
||||
/* Multiply Nyquist term by 1/sqrt(2) */
|
||||
*getcmatval(&fft_work,nfft/2,i) *= 1/d_sqrt(2.)+0*I;
|
||||
}
|
||||
check_overflow_xmat(fft_work);
|
||||
|
||||
/* print_cmat(&fft_work); */
|
||||
TRACE(15,"Nyquist and DC correction done");
|
||||
|
||||
vc j_vec_conj = vc_alloc(nfft/2+1);
|
||||
|
||||
/* Compute Cross-power spectra and store result */
|
||||
for(us i =0; i<nchannels;i++) {
|
||||
for(us j=0;j<nchannels;j++) {
|
||||
|
||||
/* The indices here are important. This is also how it
|
||||
* is documented */
|
||||
vc res = cmat_column(result,i+j*nchannels);
|
||||
|
||||
vc i_vec = cmat_column(&fft_work,i);
|
||||
vc j_vec = cmat_column(&fft_work,j);
|
||||
|
||||
/* Compute the conjugate of spectra j */
|
||||
cmat_conj(&j_vec_conj,&j_vec);
|
||||
|
||||
check_overflow_xmat(fft_work);
|
||||
|
||||
/* Compute the element-wise product of the two vectors and
|
||||
* store the result as the result */
|
||||
vc_hadamard(&res,&i_vec,&j_vec_conj);
|
||||
|
||||
vc_free(&i_vec);
|
||||
vc_free(&j_vec);
|
||||
vc_free(&res);
|
||||
|
||||
}
|
||||
}
|
||||
check_overflow_xmat(*result);
|
||||
check_overflow_xmat(*timedata);
|
||||
cmat_free(&fft_work);
|
||||
vc_free(&j_vec_conj);
|
||||
feTRACE(15);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,61 +0,0 @@
|
||||
// lasp_ps.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description: Single sided power and cross-power spectra computation
|
||||
// routines.
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef LASP_PS_H
|
||||
#define LASP_PS_H
|
||||
#include "lasp_window.h"
|
||||
typedef struct PowerSpectra_s PowerSpectra;
|
||||
|
||||
|
||||
/**
|
||||
* Allocate a PowerSpectra computer
|
||||
*
|
||||
* @param nfft fft length
|
||||
* @param nchannels Number of channels
|
||||
* @param wt Windowtype, as defined in window.h
|
||||
*
|
||||
* @return PowerSpectra handle, NULL on error
|
||||
*/
|
||||
PowerSpectra* PowerSpectra_alloc(const us nfft,
|
||||
const WindowType wt);
|
||||
|
||||
/**
|
||||
* Compute power spectra.
|
||||
*
|
||||
* @param ps[in] PowerSpectra handle
|
||||
* @param[in] timedata Time data. Should have size nfft*nchannels
|
||||
*
|
||||
* @param[out] result Here, the result will be stored. Should have
|
||||
* size (nfft/2+1)*nchannels^2, such that the Cij at frequency index f
|
||||
* can be obtained as result[f,i+j*nchannels]
|
||||
*
|
||||
*/
|
||||
void PowerSpectra_compute(const PowerSpectra* ps,
|
||||
const dmat* timedata,
|
||||
cmat *result);
|
||||
|
||||
/**
|
||||
* Return nfft
|
||||
*
|
||||
* @param ps[in] PowerSpectra handle
|
||||
*
|
||||
* @return nfft
|
||||
*/
|
||||
us PowerSpectra_getnfft(const PowerSpectra* ps);
|
||||
|
||||
/**
|
||||
* Free PowerSpectra
|
||||
*
|
||||
* @param[in] ps PowerSpectra handle
|
||||
*/
|
||||
void PowerSpectra_free(PowerSpectra* ps);
|
||||
|
||||
|
||||
|
||||
#endif // LASP_PS_H
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,108 +0,0 @@
|
||||
// ascee_python.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description:
|
||||
// Routine to generate a Numpy array from an arbitrary buffer. Careful, this
|
||||
// code should both be C and C++ compatible!!
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef LASP_PYARRAY_H
|
||||
#define LASP_PYARRAY_H
|
||||
#include "lasp_types.h"
|
||||
#include <assert.h>
|
||||
#include <numpy/ndarrayobject.h>
|
||||
|
||||
#if LASP_DOUBLE_PRECISION == 1
|
||||
#define LASP_NUMPY_FLOAT_TYPE NPY_FLOAT64
|
||||
#define LASP_NUMPY_COMPLEX_TYPE NPY_COMPLEX128
|
||||
#else
|
||||
#define LASP_NUMPY_FLOAT_TYPE NPY_FLOAT32
|
||||
#endif
|
||||
#include <malloc.h>
|
||||
|
||||
/**
|
||||
* Function passed to Python to use for cleanup of
|
||||
* foreignly obtained data.
|
||||
**/
|
||||
#define LASP_CAPSULE_NAME "pyarray_data_destructor"
|
||||
static inline void capsule_cleanup(PyObject *capsule) {
|
||||
void *memory = PyCapsule_GetPointer(capsule, LASP_CAPSULE_NAME);
|
||||
free(memory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a numpy array from a raw data pointer.
|
||||
*
|
||||
* @param data pointer to data, assumes d* for data
|
||||
* @param transfer_ownership If set to true, the created Numpy array will be
|
||||
* responsible for freeing the data.
|
||||
* @param F_contiguous It set to true, the data is assumed to be column-major
|
||||
* ordered in memory (which means we can find element d[r,c] by d[n_cols*r+c],
|
||||
* if set to false. Data is assumed to be row-major ordered (which means we
|
||||
* find element d[r,c] by d[n_rows*c+r]
|
||||
*
|
||||
* @return Numpy array
|
||||
*/
|
||||
static inline PyObject *data_to_ndarray(void *data, int n_rows, int n_cols,
|
||||
int typenum, bool transfer_ownership,
|
||||
bool F_contiguous) {
|
||||
|
||||
/* fprintf(stderr, "Enter data_to_ndarray\n"); */
|
||||
assert(data);
|
||||
|
||||
PyArray_Descr *descr = PyArray_DescrFromType(typenum);
|
||||
if (!descr)
|
||||
return NULL;
|
||||
|
||||
npy_intp dims[2] = {n_rows, n_cols};
|
||||
npy_intp strides[2];
|
||||
|
||||
int flags = 0;
|
||||
if (F_contiguous) {
|
||||
flags |= NPY_ARRAY_FARRAY;
|
||||
strides[0] = descr->elsize;
|
||||
strides[1] = descr->elsize * n_rows;
|
||||
} else {
|
||||
strides[0] = descr->elsize * n_rows;
|
||||
strides[1] = descr->elsize;
|
||||
}
|
||||
|
||||
PyArrayObject *arr =
|
||||
(PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type,
|
||||
descr, // Description
|
||||
2, // nd
|
||||
dims, // dimensions
|
||||
strides, // strides
|
||||
data, // Data pointer
|
||||
flags, // Flags
|
||||
NULL // obj
|
||||
);
|
||||
|
||||
if (!arr) {
|
||||
fprintf(stderr, "arr = 0!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (transfer_ownership) {
|
||||
// The default destructor of Python cannot free the data, as it is allocated
|
||||
// with malloc. Therefore, with this code, we tell Numpy/Python to use
|
||||
// the capsule_cleanup constructor. See:
|
||||
// https://stackoverflow.com/questions/54269956/crash-of-jupyter-due-to-the-use-of-pyarray-enableflags/54278170#54278170
|
||||
// Note that in general it was disadvised to build all C code with MinGW on
|
||||
// Windows. We do it anyway, see if we find any problems on the way.
|
||||
/* PyObject *capsule = PyCapsule_New(data, LASP_CAPSULE_NAME, capsule_cleanup); */
|
||||
/* int res = PyArray_SetBaseObject(arr, capsule); */
|
||||
/* if (res != 0) { */
|
||||
/* fprintf(stderr, "Failed to set base object of array!"); */
|
||||
/* return NULL; */
|
||||
/* } */
|
||||
|
||||
PyArray_ENABLEFLAGS(arr, NPY_OWNDATA);
|
||||
}
|
||||
|
||||
return (PyObject *)arr;
|
||||
}
|
||||
|
||||
#undef LASP_CAPSULE_NAME
|
||||
#endif // LASP_PYARRAY_H
|
@ -1,59 +0,0 @@
|
||||
// ascee_python.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE - Redu-Sone
|
||||
//
|
||||
// Description:
|
||||
// Some routines to generate numpy arrays from matrices and vectors.
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef LASP_PYTHON_H
|
||||
#define LASP_PYTHON_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
#error "Cannot compile this file with C++"
|
||||
#endif
|
||||
#include "lasp_types.h"
|
||||
#include "lasp_mat.h"
|
||||
#include "lasp_pyarray.h"
|
||||
|
||||
/**
|
||||
* @brief Create a Numpy array from a dmat structure
|
||||
*
|
||||
* @param mat Pointer to the dmat
|
||||
* @param transfer_ownership If set to true, the created Numpy array will
|
||||
* obtain ownership of the data and calls free() on destruction. The dmat will
|
||||
* have the foreign_data flag set, such that it won't free the data.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
static inline PyObject* dmat_to_ndarray(dmat* mat,bool transfer_ownership) {
|
||||
dbgassert(mat,NULLPTRDEREF);
|
||||
dbgassert(mat->_data,NULLPTRDEREF);
|
||||
/* fprintf(stderr, "Enter dmat_to_ndarray\n"); */
|
||||
|
||||
// Dimensions given in wrong order, as mat is
|
||||
// Fortran-contiguous. Later on we transpose the result. This is
|
||||
// more easy than using the PyArray_New syntax.
|
||||
PyObject* arr = data_to_ndarray(mat->_data,
|
||||
mat->n_rows,
|
||||
mat->n_cols,
|
||||
LASP_NUMPY_FLOAT_TYPE,
|
||||
transfer_ownership,
|
||||
true); // Fortran-contiguous
|
||||
|
||||
if(transfer_ownership) {
|
||||
mat->_foreign_data = true;
|
||||
}
|
||||
|
||||
if(!arr) {
|
||||
WARN("Array creation failure");
|
||||
feTRACE(15);
|
||||
return NULL;
|
||||
}
|
||||
/* fprintf(stderr, "Exit dmat_to_ndarray\n"); */
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
#endif // LASP_PYTHON_H
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,472 +0,0 @@
|
||||
// lasp_siggen.c
|
||||
//
|
||||
// Author: J.A. de Jong -ASCEE
|
||||
//
|
||||
// Description:
|
||||
// Signal generator implementation
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/* #define TRACERPLUS (-5) */
|
||||
#include "lasp_siggen.h"
|
||||
#include "lasp_alloc.h"
|
||||
#include "lasp_assert.h"
|
||||
#include "lasp_mat.h"
|
||||
|
||||
/** The fixed number of Newton iterations t.b.d. for tuning the sweep start and
|
||||
* stop frequency in logarithmic sweeps */
|
||||
#define NITER_NEWTON 20
|
||||
|
||||
/** The number of Bytes of space for the signal-specific data in the Siggen
|
||||
* structure */
|
||||
#define PRIVATE_SIZE 64
|
||||
|
||||
typedef enum {
|
||||
SINEWAVE = 0,
|
||||
NOISE,
|
||||
SWEEP,
|
||||
} SignalType;
|
||||
|
||||
typedef struct Siggen {
|
||||
SignalType signaltype;
|
||||
d fs; // Sampling frequency [Hz]
|
||||
d level_amp;
|
||||
char private_data[PRIVATE_SIZE];
|
||||
} Siggen;
|
||||
|
||||
typedef struct {
|
||||
d curtime;
|
||||
d omg;
|
||||
} SinewaveSpecific;
|
||||
|
||||
typedef struct {
|
||||
us N;
|
||||
vd data;
|
||||
us index;
|
||||
} PeriodicSpecific;
|
||||
|
||||
typedef struct {
|
||||
d V1, V2, S;
|
||||
int phase;
|
||||
Sosfilterbank* colorfilter;
|
||||
} NoiseSpecific;
|
||||
|
||||
static d level_amp(d level_dB){
|
||||
return pow(10, level_dB/20);
|
||||
}
|
||||
|
||||
Siggen* Siggen_create(SignalType type, const d fs,const d level_dB) {
|
||||
|
||||
fsTRACE(15);
|
||||
|
||||
Siggen* siggen = a_malloc(sizeof(Siggen));
|
||||
siggen->signaltype = type;
|
||||
siggen->fs = fs;
|
||||
siggen->level_amp = level_amp(level_dB);
|
||||
|
||||
feTRACE(15);
|
||||
return siggen;
|
||||
}
|
||||
|
||||
Siggen* Siggen_Sinewave_create(const d fs, const d freq,const d level_dB) {
|
||||
fsTRACE(15);
|
||||
|
||||
Siggen* sine = Siggen_create(SINEWAVE, fs, level_dB);
|
||||
dbgassert(sizeof(SinewaveSpecific) <= sizeof(sine->private_data),
|
||||
"Allocated memory too small");
|
||||
SinewaveSpecific* sp = (SinewaveSpecific*) sine->private_data;
|
||||
sp->curtime = 0;
|
||||
sp->omg = 2*number_pi*freq;
|
||||
|
||||
feTRACE(15);
|
||||
return sine;
|
||||
}
|
||||
|
||||
Siggen* Siggen_Noise_create(const d fs, const d level_dB, Sosfilterbank* colorfilter) {
|
||||
fsTRACE(15);
|
||||
|
||||
Siggen* noise = Siggen_create(NOISE, fs, level_dB);
|
||||
dbgassert(sizeof(NoiseSpecific) <= sizeof(noise->private_data),
|
||||
"Allocated memory too small");
|
||||
NoiseSpecific* wn = (NoiseSpecific*) noise->private_data;
|
||||
wn->phase = 0;
|
||||
wn->V1 = 0;
|
||||
wn->V2 = 0;
|
||||
wn->S = 0;
|
||||
wn->colorfilter = colorfilter;
|
||||
|
||||
feTRACE(15);
|
||||
return noise;
|
||||
}
|
||||
|
||||
Siggen* Siggen_Sweep_create(const d fs,const d fl_,const d fu_,
|
||||
const d Ts,const d Tq, const us flags, const d level_dB) {
|
||||
fsTRACE(15);
|
||||
|
||||
Siggen* sweep = Siggen_create(SWEEP, fs, level_dB);
|
||||
dbgassert(sizeof(PeriodicSpecific) <= sizeof(sweep->private_data),
|
||||
"Allocated memory too small");
|
||||
|
||||
bool forward_sweep = flags & SWEEP_FLAG_FORWARD;
|
||||
bool backward_sweep = flags & SWEEP_FLAG_BACKWARD;
|
||||
|
||||
// Set pointer to inplace data storage
|
||||
dbgassert(!(forward_sweep && backward_sweep), "Both forward and backward flag set");
|
||||
|
||||
PeriodicSpecific* sp = (PeriodicSpecific*) sweep->private_data;
|
||||
if(fl_ < 0 || fu_ < 0 || Ts <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
const d Dt = 1/fs; // Deltat
|
||||
|
||||
// Estimate N, the number of samples in the sweep part (non-quiescent part):
|
||||
const us N = (us) (Ts*fs);
|
||||
const us Nq = (us) (Tq*fs);
|
||||
iVARTRACE(15, N);
|
||||
sp->data = vd_alloc(N+Nq);
|
||||
vd* data = &(sp->data);
|
||||
/* Set the last part, the quiescent tail to zero */
|
||||
dmat_set(data,0.0);
|
||||
|
||||
sp->N = N+Nq;
|
||||
sp->index = 0;
|
||||
|
||||
// Obtain flags and expand
|
||||
d phase = 0;
|
||||
d fl, fu;
|
||||
|
||||
/* Swap fl and fu for a backward sweep */
|
||||
if(backward_sweep) {
|
||||
fu = fl_;
|
||||
fl = fu_;
|
||||
}
|
||||
else {
|
||||
/* Case of continuous sweep, or forward sweep */
|
||||
fl = fl_;
|
||||
fu = fu_;
|
||||
}
|
||||
|
||||
/* Linear sweep */
|
||||
if(flags & SWEEP_FLAG_LINEAR) {
|
||||
TRACE(15, "linear sweep");
|
||||
|
||||
if(forward_sweep || backward_sweep) {
|
||||
/* Forward or backward sweep */
|
||||
TRACE(15, "Forward or backward sweep");
|
||||
us K = (us) (Dt*(fl*N+0.5*(N-1)*(fu-fl)));
|
||||
d eps_num = ((d) K)/Dt - fl*N-0.5*(N-1)*(fu-fl);
|
||||
d eps = eps_num/(0.5*(N-1));
|
||||
iVARTRACE(15, K);
|
||||
dVARTRACE(15, eps);
|
||||
|
||||
for(us n = 0; n<N; n++) {
|
||||
setvecval(data, n, d_sin(phase));
|
||||
d fn = fl + ((d) n)/N*(fu + eps -fl);
|
||||
phase += 2*number_pi*Dt*fn;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Continous sweep */
|
||||
TRACE(15, "continuous sweep");
|
||||
|
||||
iVARTRACE(17, N);
|
||||
dVARTRACE(17, fl);
|
||||
dVARTRACE(17, fu);
|
||||
|
||||
const us Nf = N/2;
|
||||
const us Nb = N-Nf;
|
||||
|
||||
/* Phi halfway */
|
||||
d phih = 2*number_pi*Dt*(fl*Nf+0.5*(Nf-1)*(fu-fl));
|
||||
|
||||
us K = (us) (phih/(2*number_pi) + Dt*(fu*Nb - (Nb-1)*(fu-fl)));
|
||||
|
||||
d eps_num1 = (K- phih/(2*number_pi))/Dt;
|
||||
d eps_num2 = -fu*Nb + (Nb-1)*(fu-fl);
|
||||
d eps = (eps_num1+eps_num2)/(0.5*(Nb+1));
|
||||
|
||||
iVARTRACE(15, K);
|
||||
dVARTRACE(15, eps);
|
||||
|
||||
for(us n = 0; n<=N; n++) {
|
||||
/* iVARTRACE(17, n); */
|
||||
if(n<N) {
|
||||
setvecval(data, n, d_sin(phase));
|
||||
}
|
||||
|
||||
d fn;
|
||||
if(n <= Nf) {
|
||||
fn = fl + ((d) n)/Nf*(fu -fl);
|
||||
} else {
|
||||
fn = fu - ((d) n - Nf)/Nb*(fu + eps - fl);
|
||||
}
|
||||
dbgassert(fn >= 0, "BUG");
|
||||
|
||||
phase += 2*number_pi*Dt*fn;
|
||||
/* dVARTRACE(17, phase); */
|
||||
/* setvecval(data, n, fn); */
|
||||
/* setvecval(data, n, phase); */
|
||||
}
|
||||
/* This should be a very small number!! */
|
||||
dVARTRACE(15, phase);
|
||||
}
|
||||
|
||||
}
|
||||
else if(flags & SWEEP_FLAG_EXPONENTIAL) {
|
||||
|
||||
TRACE(15, "exponential sweep");
|
||||
if(forward_sweep || backward_sweep) {
|
||||
/* Forward or backward sweep */
|
||||
TRACE(15, "Forward or backward sweep");
|
||||
d k1 = (fu/fl);
|
||||
us K = (us) (Dt*fl*(k1-1)/(d_pow(k1,1.0/N)-1));
|
||||
d k = k1;
|
||||
|
||||
/* Iterate k to the right solution */
|
||||
d E;
|
||||
for(us iter=0;iter< 10; iter++) {
|
||||
E = 1 + K/(Dt*fl)*(d_pow(k,1.0/N)-1) - k;
|
||||
d dEdk = K/(Dt*fl)*d_pow(k,1.0/N)/(N*k)-1;
|
||||
k -= E/dEdk;
|
||||
}
|
||||
|
||||
iVARTRACE(15, K);
|
||||
dVARTRACE(15, k1);
|
||||
dVARTRACE(15, k);
|
||||
dVARTRACE(15, E);
|
||||
|
||||
for(us n = 0; n<N; n++) {
|
||||
setvecval(data, n, d_sin(phase));
|
||||
d fn = fl*d_pow(k,((d) n)/N);
|
||||
phase += 2*number_pi*Dt*fn;
|
||||
}
|
||||
} else {
|
||||
TRACE(15, "Continuous sweep");
|
||||
|
||||
const us Nf = N/2;
|
||||
const us Nb = N-Nf;
|
||||
const d k1 = (fu/fl);
|
||||
const d phif1 = 2*number_pi*Dt*fl*(k1-1)/(d_pow(k1,1.0/Nf)-1);
|
||||
const us K = (us) (phif1/(2*number_pi) + Dt*fu*(1/k1-1)/(d_pow(1/k1,1.0/Nb)-1));
|
||||
|
||||
d E;
|
||||
d k = k1;
|
||||
|
||||
/* Newton iterations to converge k to the value such that the sweep is
|
||||
* continuous */
|
||||
for(us iter=0;iter<NITER_NEWTON; iter++) {
|
||||
E = (k-1)/(d_pow(k,1.0/Nf)-1) + (k-1)/(1-d_pow(k,-1.0/Nb)) - K/Dt/fl;
|
||||
dVARTRACE(15, E);
|
||||
|
||||
/* All parts of the derivative of above error E to k */
|
||||
d dEdk1 = 1/(d_pow(k,1.0/Nf)-1);
|
||||
d dEdk2 = (1/k -1)/(d_pow(k,-1.0/Nb)-1);
|
||||
d dEdk3 = -1/(k*(d_pow(k,-1.0/Nb)-1));
|
||||
d dEdk4 = d_pow(k,-1.0/Nb)*(1/k-1)/(Nb*d_pow(d_pow(k, -1.0/Nb)-1,2));
|
||||
d dEdk5 = -d_pow(k,1.0/Nf)*(k-1)/(Nf*k*d_pow(d_pow(k,1.0/Nf)-1,2));
|
||||
d dEdk = dEdk1+dEdk2+dEdk3+dEdk4+dEdk5;
|
||||
|
||||
/* Iterate! */
|
||||
k -= E/dEdk;
|
||||
dVARTRACE(15, k);
|
||||
}
|
||||
|
||||
iVARTRACE(15, K);
|
||||
dVARTRACE(15, k1);
|
||||
dVARTRACE(15, k);
|
||||
dVARTRACE(15, E);
|
||||
|
||||
for(us n = 0; n<=N; n++) {
|
||||
/* iVARTRACE(17, n); */
|
||||
if(n<N) {
|
||||
setvecval(data, n, d_sin(phase));
|
||||
}
|
||||
|
||||
d fn;
|
||||
if(n <= Nf) {
|
||||
fn = fl * d_pow(k, ((d) n)/Nf);
|
||||
} else {
|
||||
fn = fl*k * d_pow(1/k, ((d) n - Nf)/Nb);
|
||||
}
|
||||
dbgassert(fn >= 0, "BUG");
|
||||
|
||||
phase += 2*number_pi*Dt*fn;
|
||||
while(phase > 2*number_pi) phase -= 2*number_pi;
|
||||
/* dVARTRACE(17, phase); */
|
||||
/* setvecval(data, n, fn); */
|
||||
/* setvecval(data, n, phase); */
|
||||
}
|
||||
/* This should be a very small number!! */
|
||||
dVARTRACE(15, phase);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
feTRACE(15);
|
||||
return sweep;
|
||||
}
|
||||
|
||||
static void Siggen_periodic_free(PeriodicSpecific* ps) {
|
||||
assertvalidptr(ps);
|
||||
fsTRACE(15);
|
||||
vd_free(&(ps->data));
|
||||
feTRACE(15);
|
||||
}
|
||||
us Siggen_getN(const Siggen* siggen) {
|
||||
fsTRACE(15);
|
||||
assertvalidptr(siggen);
|
||||
|
||||
switch(siggen->signaltype) {
|
||||
case SINEWAVE:
|
||||
break;
|
||||
case NOISE:
|
||||
break;
|
||||
case SWEEP:
|
||||
return ((PeriodicSpecific*) siggen->private_data)->N;
|
||||
break;
|
||||
default:
|
||||
dbgassert(false, "Not implementend signal type");
|
||||
|
||||
}
|
||||
|
||||
feTRACE(15);
|
||||
return 0;
|
||||
}
|
||||
void Siggen_setLevel(Siggen* siggen, const d new_level_dB) {
|
||||
fsTRACE(15);
|
||||
|
||||
siggen->level_amp = d_pow(10, new_level_dB/20);
|
||||
|
||||
feTRACE(15);
|
||||
}
|
||||
|
||||
void Siggen_free(Siggen* siggen) {
|
||||
fsTRACE(15);
|
||||
assertvalidptr(siggen);
|
||||
NoiseSpecific* sp;
|
||||
|
||||
switch(siggen->signaltype) {
|
||||
case SWEEP:
|
||||
/* Sweep specific stuff here */
|
||||
Siggen_periodic_free((PeriodicSpecific*) siggen->private_data);
|
||||
break;
|
||||
case SINEWAVE:
|
||||
/* Sweep specific stuff here */
|
||||
break;
|
||||
case NOISE:
|
||||
sp = (NoiseSpecific*) siggen->private_data;
|
||||
if(sp->colorfilter) {
|
||||
Sosfilterbank_free(sp->colorfilter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
a_free(siggen);
|
||||
feTRACE(15);
|
||||
}
|
||||
|
||||
static void Sinewave_genSignal(Siggen* siggen, SinewaveSpecific* sine, vd* samples) {
|
||||
fsTRACE(10);
|
||||
assertvalidptr(sine);
|
||||
d ts = 1/siggen->fs;
|
||||
d omg = sine->omg;
|
||||
|
||||
d curtime = sine->curtime;
|
||||
for(us i =0; i< samples->n_rows; i++) {
|
||||
setvecval(samples, i, siggen->level_amp*sin(omg*curtime));
|
||||
curtime = curtime + ts;
|
||||
}
|
||||
sine->curtime = curtime;
|
||||
feTRACE(10);
|
||||
}
|
||||
|
||||
static void Periodic_genSignal(Siggen* siggen, PeriodicSpecific* sweep, vd* samples) {
|
||||
fsTRACE(10);
|
||||
|
||||
for(us i=0; i<samples->n_rows; i++) {
|
||||
d* data = getvdval(&(sweep->data), sweep->index);
|
||||
setvecval(samples, i, siggen->level_amp*(*data));
|
||||
sweep->index++;
|
||||
sweep->index %= sweep->N;
|
||||
}
|
||||
|
||||
feTRACE(10);
|
||||
}
|
||||
|
||||
|
||||
static void noise_genSignal(Siggen* siggen, NoiseSpecific* wn, vd* samples) {
|
||||
fsTRACE(10);
|
||||
d X;
|
||||
d S = wn->S;
|
||||
d V1 = wn->V1;
|
||||
d V2 = wn->V2;
|
||||
|
||||
int phase = wn->phase;
|
||||
|
||||
for(us i =0; i< samples->n_rows; i++) {
|
||||
|
||||
if(wn->phase == 0) {
|
||||
do {
|
||||
d U1 = (d)rand() / RAND_MAX;
|
||||
d U2 = (d)rand() / RAND_MAX;
|
||||
|
||||
V1 = 2 * U1 - 1;
|
||||
V2 = 2 * U2 - 1;
|
||||
S = V1 * V1 + V2 * V2;
|
||||
} while(S >= 1 || S == 0);
|
||||
|
||||
X = V1 * sqrt(-2 * d_ln(S) / S);
|
||||
} else
|
||||
X = V2 * sqrt(-2 * d_ln(S) / S);
|
||||
|
||||
phase = 1 - phase;
|
||||
|
||||
setvecval(samples, i, siggen->level_amp*X);
|
||||
}
|
||||
if(wn->colorfilter){
|
||||
vd filtered = Sosfilterbank_filter(wn->colorfilter,
|
||||
samples);
|
||||
dmat_copy(samples, &filtered);
|
||||
vd_free(&filtered);
|
||||
}
|
||||
wn->S = S;
|
||||
wn->V1 = V1;
|
||||
wn->V2 = V2;
|
||||
wn->phase = phase;
|
||||
feTRACE(10);
|
||||
}
|
||||
|
||||
void Siggen_genSignal(Siggen* siggen,vd* samples) {
|
||||
|
||||
fsTRACE(10);
|
||||
assertvalidptr(siggen);
|
||||
assert_vx(samples);
|
||||
|
||||
switch(siggen->signaltype) {
|
||||
case SINEWAVE:
|
||||
Sinewave_genSignal(siggen,
|
||||
(SinewaveSpecific*) siggen->private_data,
|
||||
samples);
|
||||
|
||||
break;
|
||||
case NOISE:
|
||||
noise_genSignal(siggen,
|
||||
(NoiseSpecific*) siggen->private_data,
|
||||
samples);
|
||||
break;
|
||||
case SWEEP:
|
||||
Periodic_genSignal(siggen,
|
||||
(PeriodicSpecific*) siggen->private_data,
|
||||
samples);
|
||||
break;
|
||||
default:
|
||||
dbgassert(false, "Not implementend signal type");
|
||||
|
||||
}
|
||||
|
||||
feTRACE(10);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,100 +0,0 @@
|
||||
// lasp_siggen.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description:
|
||||
// Header file for signal generation routines
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef LASP_SIGGEN_H
|
||||
#define LASP_SIGGEN_H
|
||||
#include "lasp_mat.h"
|
||||
#include "lasp_sosfilterbank.h"
|
||||
|
||||
// Define this flag to repeat a forward sweep only, or backward only. If not
|
||||
// set, we do a continuous sweep
|
||||
#define SWEEP_FLAG_FORWARD 1
|
||||
#define SWEEP_FLAG_BACKWARD 2
|
||||
|
||||
// Types of sweeps
|
||||
#define SWEEP_FLAG_LINEAR 4
|
||||
#define SWEEP_FLAG_EXPONENTIAL 8
|
||||
|
||||
typedef struct Siggen Siggen;
|
||||
|
||||
/**
|
||||
* Create a sine wave signal generator
|
||||
*
|
||||
* @param[in] fs: Sampling frequency [Hz]
|
||||
* @param[in] level: Relative level in [dB], should be between -inf and 0
|
||||
* @param[freq] Sine wave frequency [Hz]
|
||||
*/
|
||||
Siggen* Siggen_Sinewave_create(const d fs,const d freq,const d level_dB);
|
||||
|
||||
/**
|
||||
* Create a Noise signal generator. If no Sosfilterbank is provided, it will
|
||||
* create white noise. Otherwise, the noise is 'colored' using the filterbank
|
||||
* given in the constructor. Note that the pointer to this filterbank is
|
||||
* *STOLEN*!.
|
||||
*
|
||||
* @param[in] fs: Sampling frequency [Hz]
|
||||
* @param[in] level_dB: Relative level [dB]
|
||||
* @param[in]
|
||||
*
|
||||
* @return Siggen* handle
|
||||
*/
|
||||
Siggen* Siggen_Noise_create(const d fs, const d level_dB, Sosfilterbank* colorfilter);
|
||||
|
||||
/**
|
||||
* Set the level of the signal generator
|
||||
* @param[in] Siggen* Signal generator handle
|
||||
*
|
||||
* @param[in] new_level_dB The new level, in dBFS
|
||||
*/
|
||||
void Siggen_setLevel(Siggen*, const d new_level_dB);
|
||||
|
||||
|
||||
/**
|
||||
* Obtain the repetition period for a periodic excitation.
|
||||
* @param[in] Siggen* Signal generator handle
|
||||
*
|
||||
* @param[out] N The amount of samples in one period, returns 0 if the signal
|
||||
* does not repeat.
|
||||
*/
|
||||
us Siggen_getN(const Siggen*);
|
||||
|
||||
/**
|
||||
* Create a forward sweep
|
||||
*
|
||||
* @param[in] fs: Sampling frequency [Hz]
|
||||
* @param[in] fl: Lower frequency [Hz]
|
||||
* @param[in] fl: Upper frequency [Hz]
|
||||
* @param[in] Ts: Sweep time [s]
|
||||
* @param[in] Tq: Quescent tail time [s]. Choose this value long enough to
|
||||
* avoid temporal aliasing in case of measuring impulse responses.
|
||||
* @param[in] sweep_flags: Sweep period [s]
|
||||
* @param[in] level: Relative level in [dB], should be between -inf and 0
|
||||
* @return Siggen* handle
|
||||
*/
|
||||
Siggen* Siggen_Sweep_create(const d fs,const d fl,const d fu,
|
||||
const d Ts, const d Tq, const us sweep_flags,
|
||||
const d level);
|
||||
|
||||
/**
|
||||
* Obtain a new piece of signal
|
||||
*
|
||||
* @param[in] Siggen* Signal generator handle
|
||||
* @param[out] samples Samples to fill. Vector should be pre-allocated, but
|
||||
* values will be overwritten.
|
||||
*/
|
||||
void Siggen_genSignal(Siggen*,vd* samples);
|
||||
/**
|
||||
* Free Siggen data
|
||||
*
|
||||
* @param[in] Siggen* Signal generator private data
|
||||
*/
|
||||
void Siggen_free(Siggen*);
|
||||
|
||||
#endif //LASP_SIGGEN_H
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,32 +0,0 @@
|
||||
// lasp_signals.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description:
|
||||
// Several signal functions
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef LASP_SIGNALS_H
|
||||
#define LASP_SIGNALS_H
|
||||
#include "lasp_mat.h"
|
||||
|
||||
/**
|
||||
* Compute the signal power, that is \f$ \frac{1}{N} \sum_{i=0}^{N-1}
|
||||
* v_i^2 \f$
|
||||
*
|
||||
* @param[in] signal Signal to compute the power of.
|
||||
* @return the signal power
|
||||
*/
|
||||
static inline d signal_power(vd* signal) {
|
||||
d res = 0;
|
||||
for(us i=0;i<signal->n_rows;i++) {
|
||||
res+= d_pow(*getvdval(signal,i),2);
|
||||
}
|
||||
res /= signal->n_rows;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // LASP_SIGNALS_H
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,293 +0,0 @@
|
||||
#define TRACERPLUS (-5)
|
||||
#include "lasp_slm.h"
|
||||
#include "lasp_assert.h"
|
||||
#include "lasp_tracer.h"
|
||||
|
||||
typedef struct Slm {
|
||||
Sosfilterbank *prefilter; /// Pre-filter, A, or C. If NULL, not used.
|
||||
Sosfilterbank *bandpass; /// Filterbank. If NULL, not used
|
||||
Sosfilterbank **splowpass; /// Used for time-weighting of the squared signal
|
||||
d ref_level; /// Reference value for computing decibels
|
||||
us downsampling_fac; /// Every x'th sample is returned.
|
||||
us cur_offset; /// Storage for offset point in input arrays
|
||||
vd Pm; /// Storage for the computing the mean of the square of the signal.
|
||||
vd Pmax; /// Storage for maximum computed signal power so far.
|
||||
vd Ppeak; /// Storage for computing peak powers so far.
|
||||
us N; /// Counter for the number of time samples counted that came in
|
||||
|
||||
} Slm;
|
||||
|
||||
Slm *Slm_create(Sosfilterbank *prefilter, Sosfilterbank *bandpass, const d fs,
|
||||
const d tau, const d ref_level, us *downsampling_fac) {
|
||||
fsTRACE(15);
|
||||
assertvalidptr(downsampling_fac);
|
||||
|
||||
Slm *slm = NULL;
|
||||
if (ref_level <= 0) {
|
||||
WARN("Invalid reference level");
|
||||
return NULL;
|
||||
} else if (fs <= 0) {
|
||||
WARN("Invalid sampling frequency");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
slm = (Slm *)a_malloc(sizeof(Slm));
|
||||
slm->ref_level = ref_level;
|
||||
slm->prefilter = prefilter;
|
||||
slm->bandpass = bandpass;
|
||||
|
||||
/// Compute the downsampling factor. This one is chosen based on the
|
||||
/// lowpass filter. Which has a -3 dB point of f = 1/(tau*2*pi). See LASP
|
||||
/// documentation for the computation of its minus 20 dB point. We set the
|
||||
/// reduction in its 'sampling frequency' such that its noise is at a level
|
||||
/// of 20 dB less than its 'signal'.
|
||||
us ds_fac;
|
||||
if (tau > 0) {
|
||||
// A reasonable 'framerate' for the sound level meter, based on the
|
||||
// filtering time constant.
|
||||
d fs_slm = 10 / tau;
|
||||
dVARTRACE(15, fs_slm);
|
||||
if(fs_slm < 30) {
|
||||
fs_slm = 30;
|
||||
}
|
||||
ds_fac = (us)(fs / fs_slm);
|
||||
if (ds_fac == 0) {
|
||||
// If we get 0, it should be 1
|
||||
ds_fac++;
|
||||
}
|
||||
} else {
|
||||
ds_fac = 1;
|
||||
}
|
||||
slm->downsampling_fac = ds_fac;
|
||||
*downsampling_fac = ds_fac;
|
||||
slm->cur_offset = 0;
|
||||
|
||||
/// Create the single pole lowpass
|
||||
us filterbank_size;
|
||||
if (bandpass) {
|
||||
filterbank_size = Sosfilterbank_getFilterbankSize(bandpass);
|
||||
} else {
|
||||
filterbank_size = 1;
|
||||
}
|
||||
|
||||
if (tau > 0) {
|
||||
|
||||
vd lowpass_sos = vd_alloc(6);
|
||||
d b0 = 1.0 / (1 + 2 * tau * fs);
|
||||
*getvdval(&lowpass_sos, 0) = b0;
|
||||
*getvdval(&lowpass_sos, 1) = b0;
|
||||
*getvdval(&lowpass_sos, 2) = 0;
|
||||
*getvdval(&lowpass_sos, 3) = 1;
|
||||
*getvdval(&lowpass_sos, 4) = (1 - 2 * tau * fs) * b0;
|
||||
*getvdval(&lowpass_sos, 5) = 0;
|
||||
|
||||
slm->splowpass = a_malloc(filterbank_size * sizeof(Sosfilterbank *));
|
||||
for (us ch = 0; ch < filterbank_size; ch++) {
|
||||
/// Allocate a filterbank with one channel and one section.
|
||||
slm->splowpass[ch] = Sosfilterbank_create(0, 1, 1);
|
||||
Sosfilterbank_setFilter(slm->splowpass[ch], 0, lowpass_sos);
|
||||
}
|
||||
vd_free(&lowpass_sos);
|
||||
} else {
|
||||
/// No low-pass filtering. Tau set to zero
|
||||
slm->splowpass = NULL;
|
||||
}
|
||||
|
||||
/// Initialize statistics gatherers
|
||||
slm->Ppeak = vd_alloc(filterbank_size);
|
||||
slm->Pmax = vd_alloc(filterbank_size);
|
||||
slm->Pm = vd_alloc(filterbank_size);
|
||||
slm->N = 0;
|
||||
vd_set(&(slm->Ppeak), 0);
|
||||
vd_set(&(slm->Pmax), 0);
|
||||
vd_set(&(slm->Pm), 0);
|
||||
feTRACE(15);
|
||||
return slm;
|
||||
}
|
||||
|
||||
dmat Slm_run(Slm *slm, vd *input_data) {
|
||||
fsTRACE(15);
|
||||
assertvalidptr(slm);
|
||||
assert_vx(input_data);
|
||||
|
||||
/// First step: run the input data through the pre-filter
|
||||
vd prefiltered;
|
||||
if (slm->prefilter)
|
||||
prefiltered = Sosfilterbank_filter(slm->prefilter, input_data);
|
||||
else {
|
||||
prefiltered = dmat_foreign(input_data);
|
||||
}
|
||||
dmat bandpassed;
|
||||
if (slm->bandpass) {
|
||||
bandpassed = Sosfilterbank_filter(slm->bandpass, &prefiltered);
|
||||
} else {
|
||||
bandpassed = dmat_foreign(&prefiltered);
|
||||
}
|
||||
us filterbank_size = bandpassed.n_cols;
|
||||
|
||||
/// Next step: square all values. We do this in-place. Then we filter for
|
||||
/// each channel.
|
||||
d ref_level = slm->ref_level;
|
||||
d *tmp;
|
||||
|
||||
/// Pre-calculate the size of the output data
|
||||
us downsampling_fac = slm->downsampling_fac;
|
||||
us samples_bandpassed = bandpassed.n_rows;
|
||||
iVARTRACE(15, samples_bandpassed);
|
||||
iVARTRACE(15, downsampling_fac);
|
||||
us cur_offset = slm->cur_offset;
|
||||
|
||||
/// Compute the number of samples output
|
||||
us nsamples_output = samples_bandpassed;
|
||||
if (downsampling_fac > 1) {
|
||||
nsamples_output = (samples_bandpassed - cur_offset) / downsampling_fac;
|
||||
if(nsamples_output > samples_bandpassed) {
|
||||
// This means overflow of unsigned number calculations
|
||||
nsamples_output = 0;
|
||||
}
|
||||
while(nsamples_output * downsampling_fac + cur_offset < samples_bandpassed) {
|
||||
nsamples_output++;
|
||||
}
|
||||
}
|
||||
|
||||
iVARTRACE(15, nsamples_output);
|
||||
iVARTRACE(15, cur_offset);
|
||||
dmat levels = dmat_alloc(nsamples_output, filterbank_size);
|
||||
us N, ch;
|
||||
|
||||
for (ch = 0; ch < bandpassed.n_cols; ch++) {
|
||||
iVARTRACE(15, ch);
|
||||
vd chan = dmat_column(&bandpassed, ch);
|
||||
/// Inplace squaring of the signal
|
||||
for (us sample = 0; sample < bandpassed.n_rows; sample++) {
|
||||
tmp = getdmatval(&bandpassed, sample, ch);
|
||||
*tmp = *tmp * *tmp;
|
||||
*getvdval(&(slm->Ppeak), ch) = d_max(*getvdval(&(slm->Ppeak), ch), *tmp);
|
||||
}
|
||||
|
||||
// Now that all data for the channel is squared, we can run it through
|
||||
// the low-pass filter
|
||||
cur_offset = slm->cur_offset;
|
||||
|
||||
/// Apply single-pole lowpass filter for current filterbank channel
|
||||
TRACE(15, "Start filtering");
|
||||
vd power_filtered;
|
||||
if (slm->splowpass) {
|
||||
power_filtered = Sosfilterbank_filter(slm->splowpass[ch], &chan);
|
||||
} else {
|
||||
power_filtered = dmat_foreign(&chan);
|
||||
}
|
||||
TRACE(15, "Filtering done");
|
||||
dbgassert(chan.n_rows == power_filtered.n_rows, "BUG");
|
||||
|
||||
/// Output resulting levels at a lower interval
|
||||
us i = 0;
|
||||
N = slm->N;
|
||||
d *Pm = getvdval(&(slm->Pm), ch);
|
||||
while (cur_offset < samples_bandpassed) {
|
||||
iVARTRACE(10, i);
|
||||
iVARTRACE(10, cur_offset);
|
||||
|
||||
/// Filtered power.
|
||||
const d P = *getvdval(&power_filtered, cur_offset);
|
||||
dVARTRACE(15, P);
|
||||
|
||||
/// Compute maximum, compare to current maximum
|
||||
*getvdval(&(slm->Pmax), ch) = d_max(*getvdval(&(slm->Pmax), ch), P);
|
||||
|
||||
/// Update mean power
|
||||
d Nd = (d) N;
|
||||
*Pm = (*Pm*Nd + P ) / (Nd+1);
|
||||
N++;
|
||||
dVARTRACE(15, *Pm);
|
||||
|
||||
/// Compute level
|
||||
d level = 10 * d_log10((P + d_epsilon ) / ref_level / ref_level);
|
||||
|
||||
*getdmatval(&levels, i++, ch) = level;
|
||||
cur_offset = cur_offset + downsampling_fac;
|
||||
}
|
||||
iVARTRACE(15, cur_offset);
|
||||
iVARTRACE(15, i);
|
||||
dbgassert(i == nsamples_output, "BUG");
|
||||
|
||||
vd_free(&power_filtered);
|
||||
vd_free(&chan);
|
||||
}
|
||||
/// Update sample counter
|
||||
dbgassert(ch >0, "BUG");
|
||||
slm->N = N;
|
||||
slm->cur_offset = cur_offset - samples_bandpassed;
|
||||
|
||||
vd_free(&prefiltered);
|
||||
dmat_free(&bandpassed);
|
||||
feTRACE(15);
|
||||
return levels;
|
||||
}
|
||||
|
||||
|
||||
static inline vd levels_from_power(const vd* power,const d ref_level){
|
||||
fsTRACE(15);
|
||||
|
||||
vd levels = dmat_alloc_from_dmat(power);
|
||||
for(us i=0; i< levels.n_rows; i++) {
|
||||
*getvdval(&levels, i) = 10 * d_log10(
|
||||
(*getvdval(power, i) + d_epsilon) / ref_level / ref_level);
|
||||
|
||||
}
|
||||
feTRACE(15);
|
||||
return levels;
|
||||
}
|
||||
|
||||
vd Slm_Lpeak(Slm* slm) {
|
||||
fsTRACE(15);
|
||||
assertvalidptr(slm);
|
||||
vd Lpeak = levels_from_power(&(slm->Ppeak), slm->ref_level);
|
||||
feTRACE(15);
|
||||
return Lpeak;
|
||||
}
|
||||
|
||||
vd Slm_Lmax(Slm* slm) {
|
||||
fsTRACE(15);
|
||||
assertvalidptr(slm);
|
||||
vd Lmax = levels_from_power(&(slm->Pmax), slm->ref_level);
|
||||
feTRACE(15);
|
||||
return Lmax;
|
||||
}
|
||||
|
||||
vd Slm_Leq(Slm* slm) {
|
||||
fsTRACE(15);
|
||||
assertvalidptr(slm);
|
||||
print_vd(&(slm->Pm));
|
||||
vd Leq = levels_from_power(&(slm->Pm), slm->ref_level);
|
||||
feTRACE(15);
|
||||
return Leq;
|
||||
}
|
||||
|
||||
void Slm_free(Slm *slm) {
|
||||
fsTRACE(15);
|
||||
assertvalidptr(slm);
|
||||
if (slm->prefilter) {
|
||||
Sosfilterbank_free(slm->prefilter);
|
||||
}
|
||||
|
||||
us filterbank_size;
|
||||
if (slm->bandpass) {
|
||||
filterbank_size = Sosfilterbank_getFilterbankSize(slm->bandpass);
|
||||
Sosfilterbank_free(slm->bandpass);
|
||||
} else {
|
||||
filterbank_size = 1;
|
||||
}
|
||||
if (slm->splowpass) {
|
||||
for (us ch = 0; ch < filterbank_size; ch++) {
|
||||
Sosfilterbank_free(slm->splowpass[ch]);
|
||||
}
|
||||
a_free(slm->splowpass);
|
||||
}
|
||||
vd_free(&(slm->Ppeak));
|
||||
vd_free(&(slm->Pmax));
|
||||
vd_free(&(slm->Pm));
|
||||
a_free(slm);
|
||||
|
||||
feTRACE(15);
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
// lasp_slm.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description: Multi-purpose implementation of a real time Sound Level Meter,
|
||||
// can be used for full-signal filtering, (fractional) octave band filtering,
|
||||
// etc.
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef LASP_SLM_H
|
||||
#define LASP_SLM_H
|
||||
#include "lasp_types.h"
|
||||
#include "lasp_mat.h"
|
||||
#include "lasp_sosfilterbank.h"
|
||||
|
||||
typedef struct Slm Slm;
|
||||
|
||||
#define TAU_FAST (1.0/8.0)
|
||||
#define TAU_SLOW (1.0)
|
||||
#define TAU_IMPULSE (35e-3)
|
||||
|
||||
/**
|
||||
* Initializes a Sound level meter. NOTE: Sound level meter takes over
|
||||
* ownership of pointers to the filterbanks for prefiltering and band-pass
|
||||
* filtering! After passing them to the constructor of the Slm, they should not
|
||||
* be touched anymore.
|
||||
*
|
||||
* @param[in] weighting: Sosfiterbank handle, used to pre-filter data. This is
|
||||
* in most cases an A-weighting filter, or C-weighting. If NULL, no
|
||||
* pre-filtering is done. That can be the case in situations of Z-weighting.
|
||||
* @param[in] fb: Sosfiterbank handle, bandpass filters.
|
||||
* @param[in] fs: Sampling frequency in [Hz], used for computing the
|
||||
* downsampling factor and size of output arrays.
|
||||
* @param[in] tau: Time constant of single pole low pass filter for squared data.
|
||||
* Three pre-defined values can be used: TAU_FAST, for fast filtering,
|
||||
* TAU_SLOW, for slow filtering, TAU_IMPULSE for impulse filtering
|
||||
* @param[in] ref_level: Reference level when computing dB's. I.e. P_REF_AIR for
|
||||
* sound pressure levels in air
|
||||
* @param[out] downsampling_fac: Here, the used downsampling factor is stored
|
||||
* which is used on returning data. If the value is for example 10, the
|
||||
* 'sampling' frequency of output data from `Slm_run` is 4800 is fs is set to
|
||||
* 48000. This downsampling factor is a function of the used time weighting.
|
||||
*
|
||||
* @return Slm: Handle of the sound level meter, NULL on error.
|
||||
*/
|
||||
Slm* Slm_create(Sosfilterbank* weighting,Sosfilterbank* bandpass,
|
||||
const d fs,
|
||||
const d tau,
|
||||
const d ref_level,
|
||||
us* downsampling_fac);
|
||||
|
||||
/**
|
||||
* Run the sound level meter on a piece of time data.
|
||||
*
|
||||
* @param[in] slm: Slm handle
|
||||
* @param[in] input_data: Vector of input data samples.
|
||||
*
|
||||
* @return Output result of Sound Level values in [dB], for each bank in the filter
|
||||
* bank.
|
||||
*/
|
||||
dmat Slm_run(Slm* slm,
|
||||
vd* input_data);
|
||||
/**
|
||||
* Cleans up an existing Slm
|
||||
*
|
||||
* @param f slm handle
|
||||
*/
|
||||
void Slm_free(Slm* f);
|
||||
|
||||
/**
|
||||
* Returns the (raw) peak level computed so far.
|
||||
*
|
||||
* @param[in] slm: Slm handle
|
||||
* @return Vector of peak level for each channel in the filterbank.
|
||||
*/
|
||||
vd Slm_Lpeak(Slm* slm);
|
||||
|
||||
/**
|
||||
* Returns the equivalent level computed so far.
|
||||
*
|
||||
* @param[in] slm: Slm handle
|
||||
* @return Vector of equivalent levels for each channel in the filterbank.
|
||||
*/
|
||||
vd Slm_Leq(Slm* slm);
|
||||
|
||||
/**
|
||||
* Returns the maximum level computed so far.
|
||||
*
|
||||
* @param[in] slm: Slm handle
|
||||
* @return Vector of maximum levels for each channel in the filterbank.
|
||||
*/
|
||||
vd Slm_Lmax(Slm* slm);
|
||||
|
||||
|
||||
#endif // LASP_SLM_H
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,264 +0,0 @@
|
||||
#define TRACERPLUS (-5)
|
||||
#include "lasp_sosfilterbank.h"
|
||||
#include "lasp_mq.h"
|
||||
#include "lasp_worker.h"
|
||||
#include "lasp_nprocs.h"
|
||||
|
||||
|
||||
|
||||
typedef struct Sosfilterbank {
|
||||
/// The filter_coefs matrix contains filter coefficients for a SOS filter.
|
||||
us filterbank_size;
|
||||
us nsections;
|
||||
|
||||
/// The filter coefficients for each of the filters in the Filterbank
|
||||
/// The *first* axis is the filter no, the second axis contains the
|
||||
/// filter coefficients, in the order, b_0, b_1, b_2, a_0, a_1, a_2, which
|
||||
/// corresponds to the transfer function
|
||||
/// b_0 + b_1 z^-1 + b_2 z^-2
|
||||
/// H[z] = -------------------------
|
||||
/// a_0 + a_1 z^-1 + a_2 z^-2
|
||||
dmat sos; /// sos[filter_no, coeff]
|
||||
|
||||
/// Storage for the current state of the output, first axis correspond to
|
||||
/// the filter number axis, the second axis contains state coefficients
|
||||
dmat state;
|
||||
#ifdef LASP_PARALLEL
|
||||
JobQueue* jq;
|
||||
Workers* workers;
|
||||
#endif // LASP_PARALLEL
|
||||
|
||||
|
||||
} Sosfilterbank;
|
||||
|
||||
us Sosfilterbank_getFilterbankSize(const Sosfilterbank* fb) {
|
||||
fsTRACE(15);
|
||||
assertvalidptr(fb);
|
||||
return fb->filterbank_size;
|
||||
feTRACE(15);
|
||||
|
||||
}
|
||||
|
||||
int filter_single(void* worker_data,void* job);
|
||||
|
||||
static inline us min(us a, us b){
|
||||
return a<b?a:b;
|
||||
}
|
||||
static inline us max(us a, us b){
|
||||
return a>b?a:b;
|
||||
}
|
||||
|
||||
Sosfilterbank* Sosfilterbank_create(
|
||||
const us nthreads_,
|
||||
const us filterbank_size,
|
||||
const us nsections) {
|
||||
fsTRACE(15);
|
||||
|
||||
dbgassert(filterbank_size <= MAX_SOS_FILTER_BANK_SIZE,
|
||||
"Illegal filterbank size. Max size is "
|
||||
annestr(MAX_SOS_FILTER_BANK_SIZE));
|
||||
|
||||
Sosfilterbank* fb = (Sosfilterbank*) a_malloc(sizeof(Sosfilterbank));
|
||||
|
||||
fb->filterbank_size = filterbank_size;
|
||||
dbgassert(nsections < MAX_SOS_FILTER_BANK_NSECTIONS,"Illegal number of sections");
|
||||
fb->nsections = nsections;
|
||||
|
||||
/// Allocate filter coefficients matrix
|
||||
fb->sos = dmat_alloc(filterbank_size, nsections*6);
|
||||
fb->state = dmat_alloc(filterbank_size, nsections*2);
|
||||
dmat_set(&(fb->state), 0);
|
||||
|
||||
/// Set all filter coefficients to unit impulse response
|
||||
vd imp_response = vd_alloc(6*nsections);
|
||||
vd_set(&imp_response,0);
|
||||
for(us section = 0;section < nsections; section++) {
|
||||
// Set b0 coefficient to 1
|
||||
setvecval(&imp_response, 0 + 6*section, 1);
|
||||
// Set a0 coefficient to 1
|
||||
setvecval(&imp_response, 3 + 6*section, 1);
|
||||
}
|
||||
|
||||
// Initialize all filters with a simple impulse response, single pass
|
||||
for(us filter_no = 0; filter_no < filterbank_size; filter_no++) {
|
||||
Sosfilterbank_setFilter(fb,filter_no,imp_response);
|
||||
}
|
||||
// Check if coefficients are properly initialized
|
||||
// print_dmat(&(fb->sos));
|
||||
vd_free(&imp_response);
|
||||
|
||||
#ifdef LASP_PARALLEL
|
||||
fb->jq = NULL;
|
||||
fb->workers = NULL;
|
||||
dbgassert(nthreads_ <= LASP_MAX_NUM_THREADS, "Illegal number of threads");
|
||||
us nthreads;
|
||||
us nprocs = getNumberOfProcs();
|
||||
|
||||
if(nthreads_ == 0) {
|
||||
nthreads = min(max(nprocs/2,1), filterbank_size);
|
||||
} else {
|
||||
nthreads = nthreads_;
|
||||
}
|
||||
iVARTRACE(15, nthreads);
|
||||
|
||||
if(nthreads > 1) {
|
||||
if(!(fb->jq = JobQueue_alloc(filterbank_size))) {
|
||||
Sosfilterbank_free(fb);
|
||||
feTRACE(15);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!(fb->workers = Workers_create(nthreads,
|
||||
fb->jq,
|
||||
NULL,
|
||||
&filter_single,
|
||||
NULL,
|
||||
NULL))) {
|
||||
Sosfilterbank_free(fb);
|
||||
feTRACE(15);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // LASP_PARALLEL
|
||||
feTRACE(15);
|
||||
return fb;
|
||||
}
|
||||
|
||||
void Sosfilterbank_setFilter(Sosfilterbank* fb,const us filter_no,
|
||||
const vd filter_coefs) {
|
||||
fsTRACE(15);
|
||||
assertvalidptr(fb);
|
||||
assert_vx(&filter_coefs);
|
||||
iVARTRACE(15, filter_coefs.n_rows);
|
||||
iVARTRACE(15, filter_no);
|
||||
dbgassert(filter_no < fb->filterbank_size, "Illegal filter number");
|
||||
dbgassert(filter_coefs.n_rows == fb->nsections * 6,
|
||||
"Illegal filter coefficient length");
|
||||
|
||||
dmat *sos = &fb->sos;
|
||||
/* dmat *state = &fb->state; */
|
||||
us nsections = fb->nsections;
|
||||
|
||||
for(us index=0;index<nsections*6;index++){
|
||||
// Copy contents to position in sos matrix
|
||||
*getdmatval(sos,filter_no,index) = *getvdval(&filter_coefs,index);
|
||||
}
|
||||
|
||||
feTRACE(15);
|
||||
}
|
||||
|
||||
void Sosfilterbank_free(Sosfilterbank* fb) {
|
||||
fsTRACE(15);
|
||||
assertvalidptr(fb);
|
||||
|
||||
dmat_free(&(fb->sos));
|
||||
dmat_free(&(fb->state));
|
||||
|
||||
#ifdef LASP_PARALLEL
|
||||
if(fb->workers) Workers_free(fb->workers);
|
||||
if(fb->jq) JobQueue_free(fb->jq);
|
||||
#endif // LASP_PARALLEL
|
||||
|
||||
a_free(fb);
|
||||
feTRACE(15);
|
||||
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
us filter_no;
|
||||
us nsections;
|
||||
us nsamples;
|
||||
dmat sos;
|
||||
dmat state;
|
||||
dmat ys;
|
||||
} Job;
|
||||
|
||||
int filter_single(void* worker_data, void* job_) {
|
||||
fsTRACE(15);
|
||||
Job* job = (Job*) job_;
|
||||
|
||||
us nsections = job->nsections;
|
||||
us nsamples = job->nsamples;
|
||||
dmat sos = job->sos;
|
||||
|
||||
|
||||
for(us section=0;section<nsections;section++) {
|
||||
d w1 = *getdmatval(&(job->state),job->filter_no,section*2);
|
||||
d w2 = *getdmatval(&(job->state),job->filter_no,section*2+1);
|
||||
|
||||
d b0 = *getdmatval(&sos,job->filter_no,section*6+0);
|
||||
d b1 = *getdmatval(&sos,job->filter_no,section*6+1);
|
||||
d b2 = *getdmatval(&sos,job->filter_no,section*6+2);
|
||||
/* d a0 = *getdmatval(&sos,job->filter_no,section*6+3); */
|
||||
d a1 = *getdmatval(&sos,job->filter_no,section*6+4);
|
||||
d a2 = *getdmatval(&sos,job->filter_no,section*6+5);
|
||||
|
||||
d* y = getdmatval(&(job->ys), 0, job->filter_no);
|
||||
|
||||
for(us sample=0;sample<nsamples;sample++){
|
||||
d w0 = *y - a1*w1 - a2*w2;
|
||||
d yn = b0*w0 + b1*w1 + b2*w2;
|
||||
w2 = w1;
|
||||
w1 = w0;
|
||||
*y++ = yn;
|
||||
}
|
||||
*getdmatval(&(job->state),job->filter_no,section*2) = w1;
|
||||
*getdmatval(&(job->state),job->filter_no,section*2+1) = w2;
|
||||
|
||||
}
|
||||
|
||||
feTRACE(15);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
dmat Sosfilterbank_filter(Sosfilterbank* fb,const vd* xs) {
|
||||
|
||||
fsTRACE(15);
|
||||
assertvalidptr(fb);
|
||||
assert_vx(xs);
|
||||
dmat state = fb->state;
|
||||
dmat sos = fb->sos;
|
||||
|
||||
us nsections = fb->nsections;
|
||||
us filterbank_size = fb->filterbank_size;
|
||||
us nsamples = xs->n_rows;
|
||||
|
||||
dmat ys = dmat_alloc(nsamples, filterbank_size);
|
||||
/// Copy input signal to output array
|
||||
for(us filter=0;filter<filterbank_size;filter++) {
|
||||
d_copy(getdmatval(&ys,0,filter),getvdval(xs,0),nsamples,1,1);
|
||||
}
|
||||
|
||||
Job jobs[MAX_SOS_FILTER_BANK_SIZE];
|
||||
/// Implementation is based on Proakis & Manolakis - Digital Signal
|
||||
/// Processing, Fourth Edition, p. 550
|
||||
Job job_template = {0, nsections, nsamples, sos, state, ys};
|
||||
|
||||
for(us filter=0;filter<filterbank_size;filter++) {
|
||||
/// Obtain state information for current section, and all filters
|
||||
jobs[filter] = job_template;
|
||||
jobs[filter].filter_no = filter;
|
||||
|
||||
#ifdef LASP_PARALLEL
|
||||
if(fb->workers) {
|
||||
assertvalidptr(fb->jq);
|
||||
JobQueue_push(fb->jq, &(jobs[filter]));
|
||||
} else {
|
||||
|
||||
#endif // LASP_PARALLEL
|
||||
/* No workers, we have to do it ourselves */
|
||||
filter_single(NULL,(void*) &(jobs[filter]));
|
||||
#ifdef LASP_PARALLEL
|
||||
}
|
||||
#endif // LASP_PARALLEL
|
||||
}
|
||||
#ifdef LASP_PARALLEL
|
||||
if(fb->workers) {
|
||||
JobQueue_wait_alldone(fb->jq);
|
||||
}
|
||||
#endif // LASP_PARALLEL
|
||||
feTRACE(15);
|
||||
return ys;
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
// lasp_sosfilterbank.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description: Implemententation of a discrete parallel filterbank using
|
||||
// cascaded second order sections (sos) for each filter in the filterbank. In
|
||||
// parallel mode, the filters are allocated over a set of Worker threads that
|
||||
// actually perform the computation. In serial mode, all filters in the bank
|
||||
// are computed in series.
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef LASP_FILTERBANK_H
|
||||
#define LASP_FILTERBANK_H
|
||||
#include "lasp_config.h"
|
||||
#include "lasp_types.h"
|
||||
#include "lasp_mat.h"
|
||||
|
||||
#define MAX_SOS_FILTER_BANK_SIZE 40
|
||||
#define MAX_SOS_FILTER_BANK_NSECTIONS 10
|
||||
|
||||
typedef struct Sosfilterbank Sosfilterbank;
|
||||
|
||||
/**
|
||||
* Initializes a Sosfilterbank. Sets all coefficients in such a way that the
|
||||
* filter effectively does nothing (unit impulse response).
|
||||
* @param[in] nthreads: The number of threads used in computation should be 1
|
||||
* <= nthreads <= LASP_MAX_NUM_THREADS. If set to 0, a sensible value is
|
||||
* computed based on the number of filterbanks.
|
||||
* @param[in] filterbank_size: The number of parallel filters in the bank
|
||||
*
|
||||
* @return Sosfilterbank handle
|
||||
*/
|
||||
Sosfilterbank* Sosfilterbank_create(const us nthreads,
|
||||
const us filterbank_size,
|
||||
const us nsections);
|
||||
|
||||
/**
|
||||
* Returns the number of channels in the filterbank (the filberbank size).
|
||||
*
|
||||
* @param[in] fb: Filterbank handle
|
||||
* @return The number of filters in the bank
|
||||
* */
|
||||
us Sosfilterbank_getFilterbankSize(const Sosfilterbank* fb);
|
||||
|
||||
/**
|
||||
* Initialize the filter coeficients in the filterbank
|
||||
*
|
||||
* @param fb: Filterbank handle
|
||||
* @param filter_no: Filter number in the bank
|
||||
* @param coefss: Array of filter coefficients. Should have a length of
|
||||
* nsections x 6, for each of the sections, it contains (b0, b1, b2, a0,
|
||||
* a1, a2), where b are the numerator coefficients and a are the denominator
|
||||
* coefficients.
|
||||
*
|
||||
*/
|
||||
void Sosfilterbank_setFilter(Sosfilterbank* fb,const us filter_no,
|
||||
const vd coefs);
|
||||
|
||||
/**
|
||||
* Filters x using h, returns y
|
||||
*
|
||||
* @param x Input time sequence block. Should have at least one sample.
|
||||
|
||||
* @return Filtered output in an allocated array. The number of
|
||||
* columns in this array equals the number of filters in the
|
||||
* filterbank. The number of output samples is equal to the number of
|
||||
* input samples in x (and is equal to the number of rows in the output).
|
||||
*/
|
||||
dmat Sosfilterbank_filter(Sosfilterbank* fb,
|
||||
const vd* x);
|
||||
|
||||
/**
|
||||
* Cleans up an existing filter bank.
|
||||
*
|
||||
* @param f Filterbank handle
|
||||
*/
|
||||
void Sosfilterbank_free(Sosfilterbank* f);
|
||||
|
||||
|
||||
#endif // LASP_FILTERBANK_H
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,78 +0,0 @@
|
||||
// lasp_tracer.c
|
||||
//
|
||||
// last-edit-by: J.A. de Jong
|
||||
//
|
||||
// Description:
|
||||
// Debug tracing code implementation
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#include "lasp_config.h"
|
||||
#if TRACER == 1
|
||||
#include <stdio.h>
|
||||
#include "lasp_tracer.h"
|
||||
#include "lasp_types.h"
|
||||
|
||||
#ifdef _REENTRANT
|
||||
|
||||
static __thread us ASCEE_FN_LEVEL = 0;
|
||||
|
||||
static int TRACERNAME = DEFAULTTRACERLEVEL;
|
||||
|
||||
|
||||
void setTracerLevel(int level) {
|
||||
__sync_lock_test_and_set(&TRACERNAME, level);
|
||||
}
|
||||
int getTracerLevel() {
|
||||
return __sync_fetch_and_add(&TRACERNAME, 0);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int TRACERNAME;
|
||||
static us ASCEE_FN_LEVEL = 0;
|
||||
|
||||
/* setTracerLevel and getTracerLevel are defined as macros in
|
||||
* tracer.h */
|
||||
#endif
|
||||
|
||||
void indent_trace() {
|
||||
for(us i=0;i<ASCEE_FN_LEVEL;i++) {
|
||||
printf("--");
|
||||
}
|
||||
printf("* ");
|
||||
}
|
||||
|
||||
|
||||
|
||||
void trace_impl(const char* file,int pos, const char * string){
|
||||
indent_trace();
|
||||
printf(annestr(TRACERNAME) ":%s:%i: %s\n",file,pos,string);
|
||||
}
|
||||
void fstrace_impl(const char* file,int pos,const char* fn){
|
||||
ASCEE_FN_LEVEL++;
|
||||
indent_trace();
|
||||
printf(annestr(TRACERNAME) ":%s:%i: start function: %s()\n",file,pos,fn);
|
||||
}
|
||||
void fetrace_impl(const char* file,int pos,const char* fn){
|
||||
indent_trace();
|
||||
printf(annestr(TRACERNAME) ":%s:%i: end function: %s()\n",file,pos,fn);
|
||||
ASCEE_FN_LEVEL--;
|
||||
}
|
||||
void ivartrace_impl(const char* pos,int line,const char* varname, int var){
|
||||
indent_trace();
|
||||
printf(annestr(TRACERNAME) ":%s:%i: %s = %i\n",pos,line,varname,var);
|
||||
}
|
||||
void uvartrace_impl(const char* pos,int line,const char* varname,size_t var){
|
||||
indent_trace();
|
||||
printf(annestr(TRACERNAME) ":%s:%i: %s = %zu\n",pos,line,varname,var);
|
||||
}
|
||||
void dvartrace_impl(const char* pos,int line,const char* varname, d var){
|
||||
indent_trace();
|
||||
printf(annestr(TRACERNAME) ":%s:%i: %s = %0.5e\n",pos,line,varname,var);
|
||||
}
|
||||
void cvartrace_impl(const char* pos,int line,const char* varname, c var){
|
||||
indent_trace();
|
||||
printf(annestr(TRACERNAME) ":%s:%i: %s = %0.5e+%0.5ei\n",pos,line,varname,creal(var),cimag(var));
|
||||
}
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,238 +0,0 @@
|
||||
// ascee_tracer.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description:
|
||||
// Basic tracing code for debugging.
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef LASP_TRACER_H
|
||||
#define LASP_TRACER_H
|
||||
#include "lasp_config.h"
|
||||
#include "lasp_types.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static inline void clearScreen() {
|
||||
printf("\033c\n");
|
||||
}
|
||||
|
||||
// Some console colors
|
||||
#define RESET "\033[0m"
|
||||
#define BLACK "\033[30m" /* Black */
|
||||
#define RED "\033[31m" /* Red */
|
||||
#define GREEN "\033[32m" /* Green */
|
||||
#define YELLOW "\033[33m" /* Yellow */
|
||||
#define BLUE "\033[34m" /* Blue */
|
||||
#define MAGENTA "\033[35m" /* Magenta */
|
||||
#define CYAN "\033[36m" /* Cyan */
|
||||
#define WHITE "\033[37m" /* White */
|
||||
#define BOLDBLACK "\033[1m\033[30m" /* Bold Black */
|
||||
#define BOLDRED "\033[1m\033[31m" /* Bold Red */
|
||||
#define BOLDGREEN "\033[1m\033[32m" /* Bold Green */
|
||||
#define BOLDYELLOW "\033[1m\033[33m" /* Bold Yellow */
|
||||
#define BOLDBLUE "\033[1m\033[34m" /* Bold Blue */
|
||||
#define BOLDMAGENTA "\033[1m\033[35m" /* Bold Magenta */
|
||||
#define BOLDCYAN "\033[1m\033[36m" /* Bold Cyan */
|
||||
#define BOLDWHITE "\033[1m\033[37m" /* Bold White */
|
||||
|
||||
// Not so interesting part
|
||||
#define rawstr(x) #x
|
||||
#define namestr(x) rawstr(x)
|
||||
#define annestr(x) namestr(x)
|
||||
#define FILEWITHOUTPATH ( strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__ )
|
||||
// #define POS annestr(FILEWITHOUTPATH) ":" # __LINE__ << ": "
|
||||
// End not so interesting part
|
||||
|
||||
/**
|
||||
* Produce a debug warning
|
||||
*/
|
||||
#define DBGWARN(a) \
|
||||
printf(RED); \
|
||||
printf("%s(%d): ", \
|
||||
__FILE__, \
|
||||
__LINE__ \
|
||||
); \
|
||||
printf(a); \
|
||||
printf(RESET "\n");
|
||||
|
||||
/**
|
||||
* Produce a runtime warning
|
||||
*/
|
||||
#define WARN(a) \
|
||||
printf(RED); \
|
||||
printf("WARNING: "); \
|
||||
printf(a); \
|
||||
printf(RESET "\n");
|
||||
|
||||
/**
|
||||
* Fatal error, abort execution
|
||||
*/
|
||||
#define FATAL(a) \
|
||||
WARN(a); \
|
||||
abort();
|
||||
|
||||
|
||||
|
||||
|
||||
// **************************************** Tracer code
|
||||
#ifndef TRACERPLUS
|
||||
#define TRACERPLUS (0)
|
||||
#endif
|
||||
|
||||
// If PP variable TRACER is not defined, we automatically set it on.
|
||||
#ifndef TRACER
|
||||
#define TRACER 1
|
||||
#endif
|
||||
|
||||
|
||||
#if TRACER == 1
|
||||
#ifndef TRACERNAME
|
||||
|
||||
#ifdef __GNUC__
|
||||
#warning TRACERNAME name not set, sol TRACERNAME set to 'defaulttracer'
|
||||
#else
|
||||
#pragma message("TRACERNAME name not set, sol TRACERNAME set to defaulttracer")
|
||||
#endif
|
||||
|
||||
#define TRACERNAME defaulttracer
|
||||
#endif // ifndef TRACERNAME
|
||||
|
||||
/**
|
||||
* Indent the rule for tracing visibility.
|
||||
*/
|
||||
void indent_trace();
|
||||
|
||||
// Define this preprocessor definition to overwrite
|
||||
// Use -O flag for compiler to remove the dead functions!
|
||||
// In that case all cout's for TRACE() are removed from code
|
||||
#ifndef DEFAULTTRACERLEVEL
|
||||
#define DEFAULTTRACERLEVEL (15)
|
||||
#endif
|
||||
|
||||
#ifdef _REENTRANT
|
||||
/**
|
||||
* Set the tracer level at runtime
|
||||
*
|
||||
* @param level
|
||||
*/
|
||||
void setTracerLevel(int level);
|
||||
|
||||
/**
|
||||
* Obtain the tracer level
|
||||
*
|
||||
* @return level
|
||||
*/
|
||||
int getTracerLevel();
|
||||
|
||||
#else // Not reentrant
|
||||
extern int TRACERNAME;
|
||||
#define setTracerLevel(a) TRACERNAME = a;
|
||||
static inline int getTracerLevel() { return TRACERNAME;}
|
||||
#endif
|
||||
|
||||
#include "lasp_types.h"
|
||||
|
||||
// Use this preprocessor command to introduce one TRACERNAME integer per unit
|
||||
/* Introduce one static logger */
|
||||
// We trust that the compiler will eliminate 'dead code', which means
|
||||
// that if variable BUILDINTRACERLEVEL is set, the inner if statement
|
||||
// will not be reached.
|
||||
void trace_impl(const char* pos,int line,const char * string);
|
||||
void fstrace_impl(const char* file,int pos,const char* fn);
|
||||
void fetrace_impl(const char* file,int pos,const char* fn);
|
||||
void dvartrace_impl(const char* pos,int line,const char* varname,d var);
|
||||
void cvartrace_impl(const char* pos,int line,const char* varname,c var);
|
||||
void ivartrace_impl(const char* pos,int line,const char* varname,int var);
|
||||
void uvartrace_impl(const char* pos,int line,const char* varname,size_t var);
|
||||
|
||||
/**
|
||||
* Print a trace string
|
||||
*/
|
||||
#define TRACE(level,trace_string) \
|
||||
if (level+TRACERPLUS>=getTracerLevel()) \
|
||||
{ \
|
||||
trace_impl(FILEWITHOUTPATH,__LINE__,trace_string ); \
|
||||
}
|
||||
|
||||
#define SFSG TRACE(100,"SFSG")
|
||||
|
||||
/**
|
||||
* Print start of function string
|
||||
*/
|
||||
#define fsTRACE(level) \
|
||||
if (level+TRACERPLUS>=getTracerLevel()) \
|
||||
{ \
|
||||
fstrace_impl(FILEWITHOUTPATH,__LINE__, __FUNCTION__ ); \
|
||||
}
|
||||
|
||||
/**
|
||||
* Print end of function string
|
||||
*/
|
||||
#define feTRACE(level) \
|
||||
if (level+TRACERPLUS>=getTracerLevel()) \
|
||||
{ \
|
||||
fetrace_impl(FILEWITHOUTPATH,__LINE__, __FUNCTION__ ); \
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Trace an int variable
|
||||
*/
|
||||
#define iVARTRACE(level,trace_var) \
|
||||
if (level+TRACERPLUS>=getTracerLevel()) \
|
||||
{ \
|
||||
ivartrace_impl(FILEWITHOUTPATH,__LINE__,#trace_var,trace_var); \
|
||||
}
|
||||
|
||||
/**
|
||||
* Trace an unsigned int variable
|
||||
*/
|
||||
#define uVARTRACE(level,trace_var) \
|
||||
if (level+TRACERPLUS>=getTracerLevel()) \
|
||||
{ \
|
||||
uvartrace_impl(FILEWITHOUTPATH,__LINE__,#trace_var,trace_var); \
|
||||
}
|
||||
/**
|
||||
* Trace a floating point value
|
||||
*/
|
||||
#define dVARTRACE(level,trace_var) \
|
||||
if (level+TRACERPLUS>=getTracerLevel()) \
|
||||
{ \
|
||||
dvartrace_impl(FILEWITHOUTPATH,__LINE__,#trace_var,trace_var); \
|
||||
}
|
||||
/**
|
||||
* Trace a complex floating point value
|
||||
*/
|
||||
#define cVARTRACE(level,trace_var) \
|
||||
if (level+TRACERPLUS>=getTracerLevel()) \
|
||||
{ \
|
||||
cvartrace_impl(FILEWITHOUTPATH,__LINE__,#trace_var,trace_var); \
|
||||
}
|
||||
|
||||
|
||||
#else // TRACER !=1
|
||||
#define TRACE(l,a)
|
||||
#define fsTRACE(l)
|
||||
#define feTRACE(l)
|
||||
#define setTracerLevel(a)
|
||||
#define getTracerLevel()
|
||||
|
||||
#define iVARTRACE(level,trace_var)
|
||||
#define uVARTRACE(level,trace_var)
|
||||
#define dVARTRACE(level,trace_var)
|
||||
#define cVARTRACE(level,trace_var)
|
||||
|
||||
|
||||
#endif // ######################################## TRACER ==1
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // LASP_TRACER_H
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,111 +0,0 @@
|
||||
// window.c
|
||||
//
|
||||
// Author: J.A. de Jong -ASCEE
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "lasp_window.h"
|
||||
#include "lasp_signals.h"
|
||||
#include <stdlib.h>
|
||||
/**
|
||||
* Compute the Hann window
|
||||
*
|
||||
* @param i index
|
||||
* @param N Number of indices
|
||||
*/
|
||||
static d hann(us i,us N) {
|
||||
dbgassert(i<N,"Invalid index for window function Hann");
|
||||
return d_pow(d_sin(number_pi*i/(N-1)),2);
|
||||
}
|
||||
/**
|
||||
* Compute the Hamming window
|
||||
*
|
||||
* @param i index
|
||||
* @param N Number of indices
|
||||
*/
|
||||
static d hamming(us i,us N) {
|
||||
dbgassert(i<N,"Invalid index for window function Hamming");
|
||||
d alpha = 25.0/46.0;
|
||||
return alpha-(1-alpha)*d_cos(2*number_pi*i/(N-1));
|
||||
}
|
||||
/**
|
||||
* Compute the Blackman window
|
||||
*
|
||||
* @param i index
|
||||
* @param N Number of indices
|
||||
*/
|
||||
static d blackman(us i,us N) {
|
||||
dbgassert(i<N,"Invalid index for window function Blackman");
|
||||
d a0 = 7938./18608.;
|
||||
d a1 = 9240./18608.;
|
||||
d a2 = 1430./18608.;
|
||||
return a0-a1*d_cos(2*number_pi*i/(N-1))+a2*d_cos(4*number_pi*i/(N-1));
|
||||
}
|
||||
/**
|
||||
* Compute the Rectangular window
|
||||
*
|
||||
* @param i index
|
||||
* @param N Number of indices
|
||||
*/
|
||||
static d rectangle(us i,us N) {
|
||||
dbgassert(i<N,"Invalid index for window function Hann");
|
||||
return 1.0;
|
||||
}
|
||||
static d bartlett(us n,us N) {
|
||||
dbgassert(n<N,"Invalid index for window function Bartlett");
|
||||
return 1 - d_abs(2*(n - (N-1)/2.)/(N-1));
|
||||
}
|
||||
int window_create(const WindowType wintype,vd* result,d* win_pow) {
|
||||
fsTRACE(15);
|
||||
dbgassert(result && win_pow,NULLPTRDEREF);
|
||||
assert_vx(result);
|
||||
|
||||
us nfft = result->n_rows;
|
||||
d (*win_fun)(us,us);
|
||||
switch (wintype) {
|
||||
case Hann: {
|
||||
win_fun = hann;
|
||||
break;
|
||||
}
|
||||
case Hamming: {
|
||||
win_fun = hamming;
|
||||
break;
|
||||
}
|
||||
case Rectangular: {
|
||||
win_fun = rectangle;
|
||||
break;
|
||||
}
|
||||
case Bartlett: {
|
||||
win_fun = bartlett;
|
||||
break;
|
||||
}
|
||||
case Blackman: {
|
||||
win_fun = blackman;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
DBGWARN("BUG: Unknown window function");
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
us index;
|
||||
for(index=0;index<nfft;index++) {
|
||||
|
||||
/* Compute the window function value */
|
||||
d val = win_fun(index,nfft);
|
||||
|
||||
/* Set the value in the vector */
|
||||
setvecval(result,index,val);
|
||||
}
|
||||
|
||||
/* Store window power in result */
|
||||
*win_pow = signal_power(result);
|
||||
|
||||
feTRACE(15);
|
||||
return LASP_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,37 +0,0 @@
|
||||
// window.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef LASP_WINDOW_H
|
||||
#define LASP_WINDOW_H
|
||||
#include "lasp_mat.h"
|
||||
|
||||
typedef enum {
|
||||
Hann = 0,
|
||||
Hamming = 1,
|
||||
Rectangular = 2,
|
||||
Bartlett = 3,
|
||||
Blackman = 4,
|
||||
|
||||
} WindowType;
|
||||
|
||||
/**
|
||||
* Create a Window function, store it in the result
|
||||
*
|
||||
* @param[in] wintype Enumerated type, corresponding to the window
|
||||
* function.
|
||||
* @param[out] result Vector where the window values are stored
|
||||
* @param[out] win_power Here, the overall power of the window will be
|
||||
* returned.
|
||||
*
|
||||
* @return status code, SUCCESS on success.
|
||||
*/
|
||||
int window_create(const WindowType wintype,vd* result,d* win_power);
|
||||
|
||||
|
||||
#endif // LASP_WINDOW_H
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,206 +0,0 @@
|
||||
// lasp_worker.c
|
||||
//
|
||||
// Author: J.A. de Jong -ASCEE
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#define TRACERPLUS (-5)
|
||||
|
||||
/* The code in this file is not of any use for systems that are not able to do
|
||||
* simultaneous multithreading, or should be adjusted for it. It is therefore
|
||||
* only compiled in case LASP_PARALLEL flag is set */
|
||||
#include "lasp_worker.h"
|
||||
#ifdef LASP_PARALLEL
|
||||
#include "lasp_mq.h"
|
||||
#include "lasp_alloc.h"
|
||||
#include <pthread.h>
|
||||
#include "lasp_assert.h"
|
||||
#include "lasp_tracer.h"
|
||||
|
||||
typedef struct Workers_s {
|
||||
JobQueue* jq;
|
||||
worker_alloc_function w_alloc_fcn;
|
||||
worker_function fn;
|
||||
worker_free_function w_free_fcn;
|
||||
|
||||
pthread_mutex_t global_data_mutex;
|
||||
void* global_data;
|
||||
|
||||
pthread_t worker_threads[LASP_MAX_NUM_THREADS];
|
||||
us num_workers;
|
||||
} Workers;
|
||||
|
||||
static void* threadfcn(void* data);
|
||||
|
||||
Workers* Workers_create(const us num_workers,
|
||||
JobQueue* jq,
|
||||
worker_alloc_function init_fn,
|
||||
worker_function fn,
|
||||
worker_free_function free_fn,
|
||||
void* thread_global_data) {
|
||||
|
||||
TRACE(15,"Workers_create");
|
||||
|
||||
if(num_workers > LASP_MAX_NUM_THREADS) {
|
||||
WARN("Number of workers too high in Workers_create");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* dbgassert(init_fn,NULLPTRDEREF "init_fn"); */
|
||||
dbgassert(fn,NULLPTRDEREF "fn");
|
||||
/* dbgassert(free_fn,NULLPTRDEREF "free_fn"); */
|
||||
|
||||
Workers* w = a_malloc(sizeof(Workers));
|
||||
if(!w){
|
||||
WARN(ALLOCFAILED "Workers_create");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
w->jq = jq;
|
||||
w->w_alloc_fcn = init_fn;
|
||||
w->fn = fn;
|
||||
w->w_free_fcn = free_fn;
|
||||
w->global_data = thread_global_data;
|
||||
w->num_workers = num_workers;
|
||||
|
||||
/* Initialize thread mutex */
|
||||
int rv = pthread_mutex_init(&w->global_data_mutex,NULL);
|
||||
if(rv !=0) {
|
||||
WARN("Mutex initialization failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Create the threads */
|
||||
pthread_t* thread = w->worker_threads;
|
||||
for(us i = 0; i < num_workers; i++) {
|
||||
TRACE(15,"Creating thread");
|
||||
int rv = pthread_create(thread,
|
||||
NULL, /* Thread attributes */
|
||||
threadfcn, /* Function */
|
||||
w); /* Data */
|
||||
if(rv!=0) {
|
||||
WARN("Thread creation failed");
|
||||
return NULL;
|
||||
}
|
||||
thread++;
|
||||
}
|
||||
|
||||
return w;
|
||||
|
||||
}
|
||||
|
||||
void Workers_free(Workers* w) {
|
||||
TRACE(15,"Workers_free");
|
||||
dbgassert(w,NULLPTRDEREF "w in Workers_free");
|
||||
dbgassert(w->jq,NULLPTRDEREF "w->jq in Workers_free");
|
||||
|
||||
for(us i=0;i<w->num_workers;i++) {
|
||||
/* Push the special NULL job. This will make the worker
|
||||
* threads stop their execution. */
|
||||
JobQueue_push(w->jq,NULL);
|
||||
}
|
||||
|
||||
JobQueue_wait_alldone(w->jq);
|
||||
|
||||
/* Join the threads */
|
||||
pthread_t* thread = w->worker_threads;
|
||||
for(us i=0;i<w->num_workers;i++) {
|
||||
void* retval;
|
||||
if(pthread_join(*thread,&retval)!=0) {
|
||||
WARN("Error joining thread!");
|
||||
}
|
||||
if((retval) != NULL) {
|
||||
WARN("Thread returned with error status");
|
||||
}
|
||||
thread++;
|
||||
}
|
||||
|
||||
/* Destroy the global data mutex */
|
||||
int rv = pthread_mutex_destroy(&w->global_data_mutex);
|
||||
if(rv != 0){
|
||||
WARN("Mutex destroy failed. Do not know what to do.");
|
||||
}
|
||||
|
||||
/* All threads joined */
|
||||
a_free(w);
|
||||
|
||||
}
|
||||
|
||||
static void* threadfcn(void* thread_global_data) {
|
||||
|
||||
TRACE(15,"Started worker thread function");
|
||||
/* dbgassert(thread_global_data,NULLPTRDEREF "thread_data in" */
|
||||
/* " threadfcn"); */
|
||||
Workers* w = (Workers*) thread_global_data;
|
||||
|
||||
JobQueue* jq = w->jq;
|
||||
worker_alloc_function walloc = w->w_alloc_fcn;
|
||||
worker_free_function wfree = w->w_free_fcn;
|
||||
worker_function worker_fn = w->fn;
|
||||
void* global_data = w->global_data;
|
||||
|
||||
dbgassert(jq,NULLPTRDEREF "jq in threadfcn");
|
||||
/* dbgassert(walloc,NULLPTRDEREF "walloc in threadfcn"); */
|
||||
/* dbgassert(wfree,NULLPTRDEREF "wfree in threadfcn"); */
|
||||
|
||||
int rv = pthread_mutex_lock(&w->global_data_mutex);
|
||||
if(rv !=0) {
|
||||
WARN("Global data mutex lock failed");
|
||||
pthread_exit((void*) 1);
|
||||
}
|
||||
void* w_data = NULL;
|
||||
if(walloc) {
|
||||
w_data = walloc(global_data);
|
||||
if(!w_data) {
|
||||
WARN(ALLOCFAILED);
|
||||
pthread_exit((void*) 1);
|
||||
}
|
||||
}
|
||||
|
||||
rv = pthread_mutex_unlock(&w->global_data_mutex);
|
||||
if(rv !=0) {
|
||||
WARN("Global data mutex unlock failed");
|
||||
pthread_exit((void*) 1);
|
||||
}
|
||||
|
||||
void* job = NULL;
|
||||
TRACE(20,"Worker ready");
|
||||
while (true) {
|
||||
|
||||
TRACE(10,"--------------- START CYCLE -------------");
|
||||
job = JobQueue_assign(jq);
|
||||
|
||||
/* Kill the thread for the special NULL job */
|
||||
if(!job) break;
|
||||
|
||||
/* Run the worker function */
|
||||
rv = worker_fn(w_data,job);
|
||||
if(rv!=0) {
|
||||
WARN("An error occured during execution of worker function");
|
||||
JobQueue_done(jq,job);
|
||||
break;
|
||||
}
|
||||
|
||||
JobQueue_done(jq,job);
|
||||
TRACE(10,"--------------- CYCLE COMPLETE -------------");
|
||||
}
|
||||
|
||||
JobQueue_done(jq,job);
|
||||
|
||||
/* Call the cleanup function */
|
||||
if(wfree) {
|
||||
wfree(w_data);
|
||||
}
|
||||
TRACE(15,"Exiting thread. Goodbye");
|
||||
pthread_exit((void*) NULL);
|
||||
|
||||
/* This return statement is never reached, but added to have a proper return
|
||||
* type from this function. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif // LASP_PARALLEL
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,65 +0,0 @@
|
||||
// worker.h
|
||||
//
|
||||
// Author: J.A. de Jong - ASCEE
|
||||
//
|
||||
// Description: Provides a clean interface to a pool of worker
|
||||
// threads. This class is used to easily interface with worker threads
|
||||
// by just providing a simple function to be called by a worker thread
|
||||
// on the push of a new job.
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef LASP_WORKER_H
|
||||
#define LASP_WORKER_H
|
||||
#include "lasp_config.h"
|
||||
#include "lasp_types.h"
|
||||
|
||||
#ifdef LASP_PARALLEL
|
||||
typedef struct Workers_s Workers;
|
||||
typedef struct JobQueue_s JobQueue;
|
||||
|
||||
typedef void* (*worker_alloc_function)(void* global_data);
|
||||
typedef int (*worker_function)(void* worker_data,void* job);
|
||||
typedef void (*worker_free_function)(void* worker_data);
|
||||
|
||||
/**
|
||||
* Create a pool of worker threads, that pull jobs from the job queue
|
||||
* and perform the action.
|
||||
*
|
||||
* @param num_workers Number of worker threads to create
|
||||
*
|
||||
* @param jq JobQueue. JobQueue where jobs for the workers are
|
||||
* pushed. Should stay valid as long as the Workers are alive.
|
||||
*
|
||||
* @param worker_alloc_function Function pointer to the function that
|
||||
* will be called right after the thread has been created. The worker
|
||||
* alloc function will get a pointer to the thread global data. This
|
||||
* data will be given to each thread during initialization. Using a
|
||||
* mutex to avoid race conditions on this global data.
|
||||
|
||||
* @param fn Worker function that performs the action on the
|
||||
* data. Will be called every time a job is available from the
|
||||
* JobQueue. Should have a return code of 0 on success.
|
||||
*
|
||||
* @param worker_free_function Cleanup function that is called on
|
||||
* exit.
|
||||
*
|
||||
* @return Pointer to Workers handle. NULL on error.
|
||||
*/
|
||||
Workers* Workers_create(const us num_workers,
|
||||
JobQueue* jq,
|
||||
worker_alloc_function init_fn,
|
||||
worker_function fn,
|
||||
worker_free_function free_fn,
|
||||
void* thread_global_data);
|
||||
|
||||
/**
|
||||
* Free the pool of workers.
|
||||
*
|
||||
* @param w
|
||||
*/
|
||||
void Workers_free(Workers* w);
|
||||
|
||||
#endif // LASP_PARALLEL
|
||||
|
||||
#endif // LASP_WORKER_H
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,13 +0,0 @@
|
||||
# Set a default build type if none was specified
|
||||
set(default_build_type "Release")
|
||||
if(EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
set(default_build_type "Debug")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
message(STATUS "Setting build type to '${default_build_type}' as none was specified.")
|
||||
set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE
|
||||
STRING "Choose the type of build." FORCE)
|
||||
# Set the possible values of build type for cmake-gui
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release")
|
||||
endif()
|
@ -1,44 +0,0 @@
|
||||
# Find the Cython compiler.
|
||||
#
|
||||
# This code sets the following variables:
|
||||
#
|
||||
# CYTHON_EXECUTABLE
|
||||
#
|
||||
# See also UseCython.cmake
|
||||
|
||||
#=============================================================================
|
||||
# Copyright 2011 Kitware, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#=============================================================================
|
||||
|
||||
# Use the Cython executable that lives next to the Python executable
|
||||
# if it is a local installation.
|
||||
find_package( PythonInterp )
|
||||
if( PYTHONINTERP_FOUND )
|
||||
get_filename_component( _python_path ${PYTHON_EXECUTABLE} PATH )
|
||||
find_program( CYTHON_EXECUTABLE
|
||||
NAMES cython cython.bat cython3 cython2
|
||||
HINTS ${_python_path}
|
||||
)
|
||||
else()
|
||||
find_program( CYTHON_EXECUTABLE
|
||||
NAMES cython cython.bat cython3
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
include( FindPackageHandleStandardArgs )
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS( Cython REQUIRED_VARS CYTHON_EXECUTABLE )
|
||||
|
||||
mark_as_advanced( CYTHON_EXECUTABLE )
|
@ -1,310 +0,0 @@
|
||||
# Define a function to create Cython modules.
|
||||
#
|
||||
# For more information on the Cython project, see http://cython.org/.
|
||||
# "Cython is a language that makes writing C extensions for the Python language
|
||||
# as easy as Python itself."
|
||||
#
|
||||
# This file defines a CMake function to build a Cython Python module.
|
||||
# To use it, first include this file.
|
||||
#
|
||||
# include( UseCython )
|
||||
#
|
||||
# Then call cython_add_module to create a module.
|
||||
#
|
||||
# cython_add_module( <module_name> <src1> <src2> ... <srcN> )
|
||||
#
|
||||
# To create a standalone executable, the function
|
||||
#
|
||||
# cython_add_standalone_executable( <executable_name> [MAIN_MODULE src1] <src1> <src2> ... <srcN> )
|
||||
#
|
||||
# To avoid dependence on Python, set the PYTHON_LIBRARY cache variable to point
|
||||
# to a static library. If a MAIN_MODULE source is specified,
|
||||
# the "if __name__ == '__main__':" from that module is used as the C main() method
|
||||
# for the executable. If MAIN_MODULE, the source with the same basename as
|
||||
# <executable_name> is assumed to be the MAIN_MODULE.
|
||||
#
|
||||
# Where <module_name> is the name of the resulting Python module and
|
||||
# <src1> <src2> ... are source files to be compiled into the module, e.g. *.pyx,
|
||||
# *.py, *.c, *.cxx, etc. A CMake target is created with name <module_name>. This can
|
||||
# be used for target_link_libraries(), etc.
|
||||
#
|
||||
# The sample paths set with the CMake include_directories() command will be used
|
||||
# for include directories to search for *.pxd when running the Cython complire.
|
||||
#
|
||||
# Cache variables that effect the behavior include:
|
||||
#
|
||||
# CYTHON_ANNOTATE
|
||||
# CYTHON_NO_DOCSTRINGS
|
||||
# CYTHON_FLAGS
|
||||
#
|
||||
# Source file properties that effect the build process are
|
||||
#
|
||||
# CYTHON_IS_CXX
|
||||
#
|
||||
# If this is set of a *.pyx file with CMake set_source_files_properties()
|
||||
# command, the file will be compiled as a C++ file.
|
||||
#
|
||||
# See also FindCython.cmake
|
||||
|
||||
#=============================================================================
|
||||
# Copyright 2011 Kitware, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#=============================================================================
|
||||
|
||||
# Configuration options.
|
||||
set( CYTHON_ANNOTATE OFF
|
||||
CACHE BOOL "Create an annotated .html file when compiling *.pyx." )
|
||||
set( CYTHON_NO_DOCSTRINGS OFF
|
||||
CACHE BOOL "Strip docstrings from the compiled module." )
|
||||
set( CYTHON_FLAGS "" CACHE STRING
|
||||
"Extra flags to the cython compiler." )
|
||||
mark_as_advanced( CYTHON_ANNOTATE CYTHON_NO_DOCSTRINGS CYTHON_FLAGS )
|
||||
|
||||
find_package( Cython REQUIRED )
|
||||
|
||||
|
||||
set( CYTHON_CXX_EXTENSION "cxx" )
|
||||
set( CYTHON_C_EXTENSION "c" )
|
||||
|
||||
# Create a *.c or *.cxx file from a *.pyx file.
|
||||
# Input the generated file basename. The generate file will put into the variable
|
||||
# placed in the "generated_file" argument. Finally all the *.py and *.pyx files.
|
||||
function( compile_pyx _name generated_file )
|
||||
# Default to assuming all files are C.
|
||||
set( cxx_arg "" )
|
||||
set( extension ${CYTHON_C_EXTENSION} )
|
||||
set( pyx_lang "C" )
|
||||
set( comment "Compiling Cython C source for ${_name}..." )
|
||||
|
||||
set( cython_include_directories "" )
|
||||
set( pxd_dependencies "" )
|
||||
set( pxi_dependencies "" )
|
||||
set( c_header_dependencies "" )
|
||||
set( pyx_locations "" )
|
||||
|
||||
foreach( pyx_file ${ARGN} )
|
||||
get_filename_component( pyx_file_basename "${pyx_file}" NAME_WE )
|
||||
|
||||
# Determine if it is a C or C++ file.
|
||||
get_source_file_property( property_is_cxx ${pyx_file} CYTHON_IS_CXX )
|
||||
if( ${property_is_cxx} )
|
||||
set( cxx_arg "--cplus" )
|
||||
set( extension ${CYTHON_CXX_EXTENSION} )
|
||||
set( pyx_lang "CXX" )
|
||||
set( comment "Compiling Cython CXX source for ${_name}..." )
|
||||
endif()
|
||||
|
||||
# Get the include directories.
|
||||
get_source_file_property( pyx_location ${pyx_file} LOCATION )
|
||||
get_filename_component( pyx_path ${pyx_location} PATH )
|
||||
message(${pyx_path})
|
||||
get_directory_property( cmake_include_directories DIRECTORY ${pyx_path} INCLUDE_DIRECTORIES )
|
||||
list( APPEND cython_include_directories ${cmake_include_directories} )
|
||||
list( APPEND pyx_locations "${pyx_location}" )
|
||||
|
||||
# Determine dependencies.
|
||||
# Add the pxd file will the same name as the given pyx file.
|
||||
unset( corresponding_pxd_file CACHE )
|
||||
find_file( corresponding_pxd_file ${pyx_file_basename}.pxd
|
||||
PATHS "${pyx_path}" ${cmake_include_directories}
|
||||
NO_DEFAULT_PATH )
|
||||
if( corresponding_pxd_file )
|
||||
list( APPEND pxd_dependencies "${corresponding_pxd_file}" )
|
||||
endif()
|
||||
|
||||
# pxd files to check for additional dependencies.
|
||||
set( pxds_to_check "${pyx_file}" "${pxd_dependencies}" )
|
||||
set( pxds_checked "" )
|
||||
set( number_pxds_to_check 1 )
|
||||
while( ${number_pxds_to_check} GREATER 0 )
|
||||
foreach( pxd ${pxds_to_check} )
|
||||
list( APPEND pxds_checked "${pxd}" )
|
||||
list( REMOVE_ITEM pxds_to_check "${pxd}" )
|
||||
|
||||
# check for C header dependencies
|
||||
file( STRINGS "${pxd}" extern_from_statements
|
||||
REGEX "cdef[ ]+extern[ ]+from.*$" )
|
||||
foreach( statement ${extern_from_statements} )
|
||||
# Had trouble getting the quote in the regex
|
||||
string( REGEX REPLACE "cdef[ ]+extern[ ]+from[ ]+[\"]([^\"]+)[\"].*" "\\1" header "${statement}" )
|
||||
unset( header_location CACHE )
|
||||
find_file( header_location ${header} PATHS ${cmake_include_directories} )
|
||||
if( header_location )
|
||||
list( FIND c_header_dependencies "${header_location}" header_idx )
|
||||
if( ${header_idx} LESS 0 )
|
||||
list( APPEND c_header_dependencies "${header_location}" )
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# check for pxd dependencies
|
||||
|
||||
# Look for cimport statements.
|
||||
set( module_dependencies "" )
|
||||
file( STRINGS "${pxd}" cimport_statements REGEX cimport )
|
||||
foreach( statement ${cimport_statements} )
|
||||
if( ${statement} MATCHES from )
|
||||
string( REGEX REPLACE "from[ ]+([^ ]+).*" "\\1" module "${statement}" )
|
||||
else()
|
||||
string( REGEX REPLACE "cimport[ ]+([^ ]+).*" "\\1" module "${statement}" )
|
||||
endif()
|
||||
list( APPEND module_dependencies ${module} )
|
||||
endforeach()
|
||||
list( REMOVE_DUPLICATES module_dependencies )
|
||||
# Add the module to the files to check, if appropriate.
|
||||
foreach( module ${module_dependencies} )
|
||||
unset( pxd_location CACHE )
|
||||
find_file( pxd_location ${module}.pxd
|
||||
PATHS "${pyx_path}" ${cmake_include_directories} NO_DEFAULT_PATH )
|
||||
if( pxd_location )
|
||||
list( FIND pxds_checked ${pxd_location} pxd_idx )
|
||||
if( ${pxd_idx} LESS 0 )
|
||||
list( FIND pxds_to_check ${pxd_location} pxd_idx )
|
||||
if( ${pxd_idx} LESS 0 )
|
||||
list( APPEND pxds_to_check ${pxd_location} )
|
||||
list( APPEND pxd_dependencies ${pxd_location} )
|
||||
endif() # if it is not already going to be checked
|
||||
endif() # if it has not already been checked
|
||||
endif() # if pxd file can be found
|
||||
endforeach() # for each module dependency discovered
|
||||
endforeach() # for each pxd file to check
|
||||
list( LENGTH pxds_to_check number_pxds_to_check )
|
||||
endwhile()
|
||||
|
||||
# Look for included pxi files
|
||||
file(STRINGS "${pyx_file}" include_statements REGEX "include +['\"]([^'\"]+).*")
|
||||
foreach(statement ${include_statements})
|
||||
string(REGEX REPLACE "include +['\"]([^'\"]+).*" "\\1" pxi_file "${statement}")
|
||||
unset(pxi_location CACHE)
|
||||
find_file(pxi_location ${pxi_file}
|
||||
PATHS "${pyx_path}" ${cmake_include_directories} NO_DEFAULT_PATH)
|
||||
if (pxi_location)
|
||||
list(APPEND pxi_dependencies ${pxi_location})
|
||||
endif()
|
||||
endforeach() # for each include statement found
|
||||
|
||||
endforeach() # pyx_file
|
||||
|
||||
# Set additional flags.
|
||||
if( CYTHON_ANNOTATE )
|
||||
set( annotate_arg "--annotate" )
|
||||
endif()
|
||||
|
||||
if( CYTHON_NO_DOCSTRINGS )
|
||||
set( no_docstrings_arg "--no-docstrings" )
|
||||
endif()
|
||||
|
||||
if( "${CMAKE_BUILD_TYPE}" STREQUAL "Debug" OR
|
||||
"${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo" )
|
||||
set( cython_debug_arg "--gdb" )
|
||||
endif()
|
||||
|
||||
if( "${PYTHONLIBS_VERSION_STRING}" MATCHES "^2." )
|
||||
set( version_arg "-2" )
|
||||
elseif( "${PYTHONLIBS_VERSION_STRING}" MATCHES "^3." )
|
||||
set( version_arg "-3" )
|
||||
else()
|
||||
set( version_arg )
|
||||
endif()
|
||||
|
||||
# Include directory arguments.
|
||||
list( REMOVE_DUPLICATES cython_include_directories )
|
||||
set( include_directory_arg "" )
|
||||
foreach( _include_dir ${cython_include_directories} )
|
||||
set( include_directory_arg ${include_directory_arg} "-I" "${_include_dir}" )
|
||||
endforeach()
|
||||
|
||||
# Determining generated file name.
|
||||
set( _generated_file "${CMAKE_CURRENT_BINARY_DIR}/${_name}.${extension}" )
|
||||
set_source_files_properties( ${_generated_file} PROPERTIES GENERATED TRUE )
|
||||
set( ${generated_file} ${_generated_file} PARENT_SCOPE )
|
||||
|
||||
list( REMOVE_DUPLICATES pxd_dependencies )
|
||||
list( REMOVE_DUPLICATES c_header_dependencies )
|
||||
|
||||
# Add the command to run the compiler.
|
||||
add_custom_command( OUTPUT ${_generated_file}
|
||||
COMMAND ${CYTHON_EXECUTABLE}
|
||||
ARGS ${cxx_arg} ${include_directory_arg} ${version_arg}
|
||||
${annotate_arg} ${no_docstrings_arg} ${cython_debug_arg} ${CYTHON_FLAGS}
|
||||
--output-file ${_generated_file} ${pyx_locations}
|
||||
DEPENDS ${pyx_locations} ${pxd_dependencies} ${pxi_dependencies}
|
||||
IMPLICIT_DEPENDS ${pyx_lang} ${c_header_dependencies}
|
||||
COMMENT ${comment}
|
||||
)
|
||||
|
||||
# Remove their visibility to the user.
|
||||
set( corresponding_pxd_file "" CACHE INTERNAL "" )
|
||||
set( header_location "" CACHE INTERNAL "" )
|
||||
set( pxd_location "" CACHE INTERNAL "" )
|
||||
endfunction()
|
||||
|
||||
# cython_add_module( <name> src1 src2 ... srcN )
|
||||
# Build the Cython Python module.
|
||||
function( cython_add_module _name )
|
||||
set( pyx_module_sources "" )
|
||||
set( other_module_sources "" )
|
||||
foreach( _file ${ARGN} )
|
||||
if( ${_file} MATCHES ".*\\.py[x]?$" )
|
||||
list( APPEND pyx_module_sources ${_file} )
|
||||
else()
|
||||
list( APPEND other_module_sources ${_file} )
|
||||
endif()
|
||||
endforeach()
|
||||
compile_pyx( ${_name} generated_file ${pyx_module_sources} )
|
||||
include_directories( ${PYTHON_INCLUDE_DIRS} )
|
||||
python_add_module( ${_name} ${generated_file} ${other_module_sources} )
|
||||
if( APPLE )
|
||||
set_target_properties( ${_name} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup" )
|
||||
else()
|
||||
target_link_libraries( ${_name} ${PYTHON_LIBRARIES} )
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
include( CMakeParseArguments )
|
||||
# cython_add_standalone_executable( _name [MAIN_MODULE src3.py] src1 src2 ... srcN )
|
||||
# Creates a standalone executable the given sources.
|
||||
function( cython_add_standalone_executable _name )
|
||||
set( pyx_module_sources "" )
|
||||
set( other_module_sources "" )
|
||||
set( main_module "" )
|
||||
cmake_parse_arguments( cython_arguments "" "MAIN_MODULE" "" ${ARGN} )
|
||||
include_directories( ${PYTHON_INCLUDE_DIRS} )
|
||||
foreach( _file ${cython_arguments_UNPARSED_ARGUMENTS} )
|
||||
if( ${_file} MATCHES ".*\\.py[x]?$" )
|
||||
get_filename_component( _file_we ${_file} NAME_WE )
|
||||
if( "${_file_we}" STREQUAL "${_name}" )
|
||||
set( main_module "${_file}" )
|
||||
elseif( NOT "${_file}" STREQUAL "${cython_arguments_MAIN_MODULE}" )
|
||||
set( PYTHON_MODULE_${_file_we}_static_BUILD_SHARED OFF )
|
||||
compile_pyx( "${_file_we}_static" generated_file "${_file}" )
|
||||
list( APPEND pyx_module_sources "${generated_file}" )
|
||||
endif()
|
||||
else()
|
||||
list( APPEND other_module_sources ${_file} )
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if( cython_arguments_MAIN_MODULE )
|
||||
set( main_module ${cython_arguments_MAIN_MODULE} )
|
||||
endif()
|
||||
if( NOT main_module )
|
||||
message( FATAL_ERROR "main module not found." )
|
||||
endif()
|
||||
get_filename_component( main_module_we "${main_module}" NAME_WE )
|
||||
set( CYTHON_FLAGS ${CYTHON_FLAGS} --embed )
|
||||
compile_pyx( "${main_module_we}_static" generated_file ${main_module} )
|
||||
add_executable( ${_name} ${generated_file} ${pyx_module_sources} ${other_module_sources} )
|
||||
target_link_libraries( ${_name} ${PYTHON_LIBRARIES} ${pyx_module_libs} )
|
||||
endfunction()
|
@ -1,10 +0,0 @@
|
||||
import numpy as np
|
||||
cimport numpy as cnp
|
||||
|
||||
# Do this, our segfaults will be your destination
|
||||
cnp.import_array()
|
||||
|
||||
DEF LASP_DOUBLE_PRECISION = "@LASP_DOUBLE_PRECISION@"
|
||||
DEF LASP_DEBUG_CYTHON = "@LASP_DEBUG@"
|
||||
|
||||
from libcpp cimport bool
|
@ -1,40 +0,0 @@
|
||||
set(cpp_daq_files lasp_cppdaq.cpp)
|
||||
set(cpp_daq_linklibs ${LASP_THREADING_LIBRARIES})
|
||||
include_directories(../c)
|
||||
|
||||
if(LASP_HAS_RTAUDIO)
|
||||
include_directories(/usr/include/rtaudio)
|
||||
list(APPEND cpp_daq_files lasp_cpprtaudio.cpp)
|
||||
list(PREPEND cpp_daq_linklibs rtaudio)
|
||||
endif()
|
||||
if(LASP_HAS_ULDAQ)
|
||||
list(APPEND cpp_daq_files lasp_cppuldaq.cpp)
|
||||
list(PREPEND cpp_daq_linklibs uldaq)
|
||||
endif()
|
||||
if(win32)
|
||||
list(APPEND cpp_daq_linklibs python${python_version_windll})
|
||||
endif(win32)
|
||||
|
||||
add_library(cpp_daq ${cpp_daq_files})
|
||||
target_link_libraries(cpp_daq ${cpp_daq_linklibs})
|
||||
|
||||
foreach(cython_file lasp_daq lasp_deviceinfo lasp_daqconfig)
|
||||
|
||||
set_source_files_properties(${cython_file}.pyx PROPERTIES
|
||||
CYTHON_IS_CXX TRUE)
|
||||
|
||||
set_source_files_properties(${cython_file}.cxx PROPERTIES
|
||||
COMPILE_FLAGS "${CMAKE_CXX_FLAGS} ${CYTHON_EXTRA_CXX_FLAGS}")
|
||||
|
||||
cython_add_module(${cython_file} ${cython_file}.pyx)
|
||||
|
||||
target_link_libraries(${cython_file} cpp_daq ${cpp_daq_linklibs} lasp_lib)
|
||||
|
||||
endforeach()
|
||||
|
||||
|
||||
# This is the way to make this variable work in all CMakeLists files. It is
|
||||
# also used in the testing directory. But better to already link cpp_daq with
|
||||
# linklibs.
|
||||
|
||||
# set(cpp_daq_linklibs "${cpp_daq_linklibs}" CACHE INTERNAL "cpp_daq_linklibs")
|
@ -1,5 +0,0 @@
|
||||
from .lasp_device_common import *
|
||||
from .lasp_deviceinfo import *
|
||||
from .lasp_daqconfig import *
|
||||
from .lasp_daq import *
|
||||
|
@ -1,152 +0,0 @@
|
||||
import sys
|
||||
include "config.pxi"
|
||||
cimport cython
|
||||
from .lasp_avtype import AvType
|
||||
from libcpp.string cimport string
|
||||
from libcpp.vector cimport vector
|
||||
from libc.stdlib cimport malloc, free
|
||||
from libc.stdio cimport printf, fprintf, stderr
|
||||
from libc.string cimport memcpy, memset
|
||||
from cpython.ref cimport PyObject,Py_INCREF, Py_DECREF
|
||||
|
||||
cdef extern from "lasp_cppthread.h" nogil:
|
||||
cdef cppclass CPPThread[T,F]:
|
||||
CPPThread(F threadfunction, T data)
|
||||
void join()
|
||||
|
||||
void CPPsleep_ms(unsigned int ms)
|
||||
void CPPsleep_us(unsigned int us)
|
||||
|
||||
cdef extern from "lasp_cppqueue.h" nogil:
|
||||
cdef cppclass SafeQueue[T]:
|
||||
SafeQueue()
|
||||
void enqueue(T t)
|
||||
T dequeue()
|
||||
size_t size() const
|
||||
bool empty() const
|
||||
|
||||
cdef extern from "atomic" namespace "std" nogil:
|
||||
cdef cppclass atomic[T]:
|
||||
T load()
|
||||
void store(T)
|
||||
|
||||
cdef extern from "lasp_pyarray.h":
|
||||
PyObject* data_to_ndarray(void* data,
|
||||
int n_rows,int n_cols,
|
||||
int typenum,
|
||||
bool transfer_ownership,
|
||||
bool F_contiguous) with gil
|
||||
|
||||
|
||||
ctypedef size_t us
|
||||
ctypedef vector[bool] boolvec
|
||||
ctypedef vector[double] dvec
|
||||
ctypedef vector[us] usvec
|
||||
|
||||
cdef extern from "lasp_cppdaq.h" nogil:
|
||||
|
||||
cdef cppclass DaqApi:
|
||||
string apiname
|
||||
unsigned apicode
|
||||
unsigned api_specific_subcode
|
||||
|
||||
@staticmethod
|
||||
vector[DaqApi] getAvailableApis();
|
||||
|
||||
cdef cppclass DataType:
|
||||
string name
|
||||
unsigned sw
|
||||
bool is_floating
|
||||
DataType dtype_fl64
|
||||
bool operator==(const DataType&)
|
||||
|
||||
DataType dtype_fl64
|
||||
DataType dtype_fl32
|
||||
DataType dtype_int8
|
||||
DataType dtype_int16
|
||||
DataType dtype_int32
|
||||
DataType dtype_invalid
|
||||
|
||||
cdef cppclass cppDeviceInfo "DeviceInfo":
|
||||
DaqApi api
|
||||
string device_name
|
||||
|
||||
int api_specific_devindex
|
||||
|
||||
vector[DataType] availableDataTypes
|
||||
int prefDataTypeIndex
|
||||
|
||||
vector[double] availableSampleRates
|
||||
int prefSampleRateIndex
|
||||
|
||||
vector[us] availableFramesPerBlock
|
||||
unsigned prefFramesPerBlockIndex
|
||||
|
||||
dvec availableInputRanges
|
||||
int prefInputRangeIndex
|
||||
|
||||
unsigned ninchannels
|
||||
unsigned noutchannels
|
||||
|
||||
string serialize()
|
||||
|
||||
cppDeviceInfo deserialize(string)
|
||||
bool hasInputIEPE
|
||||
bool hasInputACCouplingSwitch
|
||||
bool hasInputTrigger
|
||||
|
||||
cdef cppclass cppDaqConfiguration "DaqConfiguration":
|
||||
DaqApi api
|
||||
string device_name
|
||||
|
||||
boolvec eninchannels
|
||||
boolvec enoutchannels
|
||||
|
||||
vector[double] inchannel_sensitivities
|
||||
vector[string] inchannel_names
|
||||
vector[string] inchannel_metadata
|
||||
|
||||
vector[string] outchannel_names
|
||||
vector[double] outchannel_sensitivities
|
||||
vector[string] outchannel_metadata
|
||||
|
||||
us sampleRateIndex
|
||||
|
||||
us dataTypeIndex
|
||||
|
||||
us framesPerBlockIndex
|
||||
|
||||
bool monitorOutput
|
||||
vector[us] input_qty_idx
|
||||
|
||||
boolvec inputIEPEEnabled
|
||||
boolvec inputACCouplingMode
|
||||
|
||||
usvec inputRangeIndices
|
||||
|
||||
cppDaqConfiguration()
|
||||
cppDaqConfiguration(cppDeviceInfo& devinfo)
|
||||
|
||||
int getHighestInChannel()
|
||||
int getHighestOutChannel()
|
||||
|
||||
int getLowestInChannel()
|
||||
int getLowestOutChannel()
|
||||
|
||||
cdef cppclass cppDaq "Daq":
|
||||
void start(SafeQueue[void*] *inQueue,
|
||||
SafeQueue[void*] *outQueue) except +
|
||||
void stop() except +
|
||||
double samplerate()
|
||||
us neninchannels(bool include_monitorchannel)
|
||||
us nenoutchannels()
|
||||
DataType dataType()
|
||||
us framesPerBlock()
|
||||
bool isRunning()
|
||||
bool duplexMode()
|
||||
|
||||
@staticmethod
|
||||
cppDaq* createDaq(cppDeviceInfo&, cppDaqConfiguration&) except +
|
||||
|
||||
@staticmethod
|
||||
vector[cppDeviceInfo] getDeviceInfo() except +
|
@ -1,198 +0,0 @@
|
||||
#include "lasp_cppdaq.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include "lasp_config.h"
|
||||
|
||||
#define MAX_DEV_COUNT_PER_API 20
|
||||
|
||||
#ifdef LASP_HAS_ULDAQ
|
||||
#include "lasp_cppuldaq.h"
|
||||
#endif
|
||||
#ifdef LASP_HAS_RTAUDIO
|
||||
#include "lasp_cpprtaudio.h"
|
||||
#endif
|
||||
|
||||
vector<DeviceInfo> Daq::getDeviceInfo() {
|
||||
vector<DeviceInfo> devs;
|
||||
#ifdef LASP_HAS_ULDAQ
|
||||
fillUlDaqDeviceInfo(devs);
|
||||
#endif
|
||||
|
||||
#ifdef LASP_HAS_RTAUDIO
|
||||
fillRtAudioDeviceInfo(devs);
|
||||
#endif
|
||||
|
||||
return devs;
|
||||
}
|
||||
|
||||
vector<DaqApi> DaqApi::getAvailableApis() {
|
||||
|
||||
vector<DaqApi> apis;
|
||||
apis.resize(6);
|
||||
#ifdef LASP_HAS_ULDAQ
|
||||
apis.at(uldaqapi.apicode) = uldaqapi;
|
||||
#endif
|
||||
#ifdef LASP_HAS_RTAUDIO
|
||||
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;
|
||||
}
|
||||
|
||||
Daq *Daq::createDaq(const DeviceInfo& devinfo,
|
||||
const DaqConfiguration &config) {
|
||||
|
||||
if(!config.match(devinfo)) {
|
||||
throw 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);
|
||||
}
|
||||
#ifdef LASP_HAS_ULDAQ
|
||||
else if (devinfo.api == uldaqapi) {
|
||||
return createUlDaqDevice(devinfo, config);
|
||||
}
|
||||
#endif
|
||||
#ifdef LASP_HAS_RTAUDIO
|
||||
else if(apicode >= 1 && apicode <= 5) {
|
||||
return createRtAudioDevice(devinfo, config);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
throw std::runtime_error(string("Unable to match API: ") +
|
||||
devinfo.api.apiname);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Daq::Daq(const DeviceInfo &devinfo, const DaqConfiguration &config)
|
||||
: DaqConfiguration(config), DeviceInfo(devinfo) {
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double Daq::samplerate() const {
|
||||
mutexlock lock(mutex);
|
||||
assert(sampleRateIndex < availableSampleRates.size());
|
||||
return availableSampleRates.at(sampleRateIndex);
|
||||
}
|
||||
|
||||
|
||||
DataType Daq::dataType() const {
|
||||
mutexlock lock(mutex);
|
||||
assert((us)dataTypeIndex < availableDataTypes.size());
|
||||
return availableDataTypes.at(dataTypeIndex);
|
||||
}
|
||||
|
||||
|
||||
double Daq::inputRangeForChannel(us ch) const {
|
||||
if (!(ch < ninchannels)) {
|
||||
throw runtime_error("Invalid channel number");
|
||||
}
|
||||
mutexlock lock(mutex);
|
||||
assert(inputRangeIndices.size() == eninchannels.size());
|
||||
return availableInputRanges.at(inputRangeIndices.at(ch));
|
||||
}
|
||||
|
||||
|
||||
us Daq::neninchannels(bool include_monitorchannel) const {
|
||||
mutexlock lock(mutex);
|
||||
us inch = std::count(eninchannels.begin(), eninchannels.end(), true);
|
||||
if (monitorOutput && include_monitorchannel) {
|
||||
inch++;
|
||||
}
|
||||
return inch;
|
||||
}
|
||||
|
||||
|
||||
us Daq::nenoutchannels() const {
|
||||
mutexlock lock(mutex);
|
||||
return std::count(enoutchannels.begin(), enoutchannels.end(), true);
|
||||
}
|
@ -1,332 +0,0 @@
|
||||
#ifndef LASP_CPPDAQ_H
|
||||
#define LASP_CPPDAQ_H
|
||||
#include "lasp_config.h"
|
||||
#include "lasp_types.h"
|
||||
|
||||
#ifdef LASP_HAS_RTAUDIO
|
||||
#include <RtAudio.h>
|
||||
#endif
|
||||
|
||||
#ifdef LASP_HAS_ULDAQ
|
||||
#include <uldaq.h>
|
||||
#endif
|
||||
|
||||
#include "lasp_cppqueue.h"
|
||||
#include "string"
|
||||
#include "vector"
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
|
||||
using std::cerr;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using std::getline;
|
||||
using std::runtime_error;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::to_string;
|
||||
|
||||
typedef vector<bool> boolvec;
|
||||
typedef vector<double> dvec;
|
||||
typedef vector<us> usvec;
|
||||
typedef std::lock_guard<std::mutex> mutexlock;
|
||||
|
||||
class DataType {
|
||||
public:
|
||||
string name;
|
||||
unsigned sw;
|
||||
bool is_floating;
|
||||
|
||||
DataType(const char *name, unsigned sw, bool is_floating)
|
||||
: name(name), sw(sw), is_floating(is_floating) {}
|
||||
DataType() : name("invalid data type"), sw(0), is_floating(false) {}
|
||||
bool operator==(const DataType &o) {
|
||||
return (name == o.name && sw == o.sw && is_floating == o.is_floating);
|
||||
}
|
||||
};
|
||||
|
||||
const DataType dtype_invalid;
|
||||
const DataType dtype_fl32("32-bits floating point", 4, true);
|
||||
const DataType dtype_fl64("64-bits floating point", 8, true);
|
||||
const DataType dtype_int8("8-bits integers", 1, false);
|
||||
const DataType dtype_int24("24-bits integers", 1, false);
|
||||
const DataType dtype_int16("16-bits integers", 2, false);
|
||||
const DataType dtype_int32("32-bits integers", 4, false);
|
||||
|
||||
const std::vector<DataType> dataTypes = {
|
||||
dtype_int8, dtype_int16,dtype_int24, dtype_int32, dtype_fl32, dtype_fl64,
|
||||
};
|
||||
|
||||
class DaqApi {
|
||||
public:
|
||||
string apiname = "Invalid API";
|
||||
int apicode = -1;
|
||||
unsigned api_specific_subcode = 0;
|
||||
|
||||
DaqApi(string apiname, unsigned apicode, unsigned api_specific_subcode = 0)
|
||||
: apiname(apiname), apicode(apicode),
|
||||
api_specific_subcode(api_specific_subcode) {}
|
||||
DaqApi() {}
|
||||
bool operator==(const DaqApi &other) const {
|
||||
return (apiname == other.apiname && apicode == other.apicode &&
|
||||
api_specific_subcode == other.api_specific_subcode);
|
||||
}
|
||||
operator string() const { return apiname + ", code: " + to_string(apicode); }
|
||||
static vector<DaqApi> getAvailableApis();
|
||||
};
|
||||
|
||||
#ifdef LASP_HAS_ULDAQ
|
||||
const DaqApi uldaqapi("UlDaq", 0);
|
||||
#endif
|
||||
#ifdef LASP_HAS_RTAUDIO
|
||||
const DaqApi rtaudioAlsaApi("RtAudio Linux ALSA", 1, RtAudio::Api::LINUX_ALSA);
|
||||
const DaqApi rtaudioPulseaudioApi("RtAudio Linux Pulseaudio", 2,
|
||||
RtAudio::Api::LINUX_PULSE);
|
||||
const DaqApi rtaudioWasapiApi("RtAudio Windows Wasapi", 3,
|
||||
RtAudio::Api::WINDOWS_WASAPI);
|
||||
const DaqApi rtaudioDsApi("RtAudio Windows DirectSound", 4,
|
||||
RtAudio::Api::WINDOWS_DS);
|
||||
const DaqApi rtaudioAsioApi("RtAudio Windows ASIO", 5,
|
||||
RtAudio::Api::WINDOWS_ASIO);
|
||||
#endif
|
||||
|
||||
// Structure containing device info parameters
|
||||
class DeviceInfo {
|
||||
public:
|
||||
DaqApi api;
|
||||
string device_name = "";
|
||||
|
||||
int api_specific_devindex = -1;
|
||||
|
||||
vector<DataType> availableDataTypes;
|
||||
int prefDataTypeIndex = 0;
|
||||
|
||||
vector<double> availableSampleRates;
|
||||
int prefSampleRateIndex = -1;
|
||||
|
||||
vector<us> availableFramesPerBlock;
|
||||
unsigned prefFramesPerBlockIndex = 0;
|
||||
|
||||
dvec availableInputRanges;
|
||||
int prefInputRangeIndex = 0;
|
||||
|
||||
unsigned ninchannels = 0;
|
||||
unsigned noutchannels = 0;
|
||||
|
||||
bool hasInputIEPE = false;
|
||||
bool hasInputACCouplingSwitch = false;
|
||||
bool hasInputTrigger = false;
|
||||
|
||||
/* DeviceInfo(): */
|
||||
/* datatype(dtype_invalid) { } */
|
||||
|
||||
double prefSampleRate() const {
|
||||
if (((us)prefSampleRateIndex < availableSampleRates.size()) &&
|
||||
(prefSampleRateIndex >= 0)) {
|
||||
return availableSampleRates.at(prefSampleRateIndex);
|
||||
} else {
|
||||
throw std::runtime_error("No prefered sample rate available");
|
||||
}
|
||||
}
|
||||
|
||||
operator string() const {
|
||||
std::stringstream str;
|
||||
str << api.apiname + " " << api_specific_devindex << endl
|
||||
<< " number of input channels: " << ninchannels << endl
|
||||
<< " number of output channels: " << noutchannels << endl;
|
||||
return str.str();
|
||||
}
|
||||
|
||||
string 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 DataType& dtype: availableDataTypes) {
|
||||
// WARNING: THIS GOES COMPLETELY WRONG WHEN NAMES contain A TAB!!!
|
||||
str << dtype.name << "\t";
|
||||
str << dtype.sw << "\t";
|
||||
str << dtype.is_floating << "\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();
|
||||
}
|
||||
|
||||
static 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++) {
|
||||
DataType dtype;
|
||||
dtype.name = nexts();
|
||||
dtype.sw =nexti();
|
||||
dtype.is_floating = bool(nexti());
|
||||
devinfo.availableDataTypes.push_back(dtype);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
// Device configuration parameters
|
||||
class DaqConfiguration {
|
||||
public:
|
||||
DaqApi api;
|
||||
string device_name;
|
||||
|
||||
boolvec eninchannels; // Enabled input channelsvice(const DeviceInfo& devinfo,
|
||||
boolvec enoutchannels; // Enabled output channels
|
||||
|
||||
vector<double> inchannel_sensitivities;
|
||||
vector<string> inchannel_names;
|
||||
vector<string> inchannel_metadata;
|
||||
|
||||
vector<double> outchannel_sensitivities;
|
||||
vector<string> outchannel_names;
|
||||
vector<string> outchannel_metadata;
|
||||
|
||||
us sampleRateIndex = 0; // Index in list of sample rates
|
||||
|
||||
us dataTypeIndex = 0; // Required datatype for output, should be
|
||||
// present in the list
|
||||
|
||||
us framesPerBlockIndex = 0;
|
||||
|
||||
bool monitorOutput = false;
|
||||
|
||||
boolvec inputIEPEEnabled;
|
||||
boolvec inputACCouplingMode;
|
||||
|
||||
usvec inputRangeIndices;
|
||||
|
||||
// Create a default configuration, with all channels disabled on both
|
||||
// input and output, and default channel names
|
||||
DaqConfiguration(const DeviceInfo &device);
|
||||
DaqConfiguration() {}
|
||||
|
||||
bool match(const DeviceInfo &devinfo) const;
|
||||
|
||||
int getHighestInChannel() const;
|
||||
int getHighestOutChannel() const;
|
||||
|
||||
int getLowestInChannel() const;
|
||||
int getLowestOutChannel() const;
|
||||
};
|
||||
|
||||
class Daq;
|
||||
class Daq : public DaqConfiguration, public DeviceInfo {
|
||||
|
||||
mutable std::mutex mutex;
|
||||
|
||||
public:
|
||||
static vector<DeviceInfo> getDeviceInfo();
|
||||
|
||||
static Daq *createDaq(const DeviceInfo &, const DaqConfiguration &config);
|
||||
|
||||
Daq(const DeviceInfo &devinfo, const DaqConfiguration &config);
|
||||
|
||||
virtual void start(SafeQueue<void *> *inqueue,
|
||||
SafeQueue<void *> *outqueue) = 0;
|
||||
|
||||
virtual void stop() = 0;
|
||||
|
||||
virtual bool isRunning() const = 0;
|
||||
|
||||
virtual ~Daq(){};
|
||||
us neninchannels(bool include_monitorchannel = true) const;
|
||||
us nenoutchannels() const;
|
||||
|
||||
double samplerate() const;
|
||||
double inputRangeForChannel(us ch) const;
|
||||
DataType dataType() const;
|
||||
|
||||
us framesPerBlock() const {
|
||||
mutexlock lock(mutex);
|
||||
return availableFramesPerBlock.at(framesPerBlockIndex);
|
||||
}
|
||||
bool duplexMode() const {
|
||||
return (neninchannels(false) > 0 && nenoutchannels() > 0);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // LASP_CPPDAQ_H
|
@ -1,56 +0,0 @@
|
||||
// threadsafe_queue.h
|
||||
//
|
||||
// Author: J.A. de Jong
|
||||
//
|
||||
// Description:
|
||||
// Implementation of a thread-safe queue, based on STL queue
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
#ifndef THREADSAFE_QUEUE_H
|
||||
#define THREADSAFE_QUEUE_H
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
// A threadsafe-queue.
|
||||
template <class T>
|
||||
class SafeQueue {
|
||||
std::queue<T> _queue;
|
||||
mutable std::mutex _mutex;
|
||||
std::condition_variable _cv;
|
||||
public:
|
||||
SafeQueue(): _queue(), _mutex() , _cv()
|
||||
{}
|
||||
|
||||
~SafeQueue(){}
|
||||
|
||||
void enqueue(T t) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
_queue.push(t);
|
||||
_cv.notify_one();
|
||||
}
|
||||
|
||||
T dequeue() {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
while(_queue.empty())
|
||||
{
|
||||
// release lock as long as the wait and reaquire it afterwards.
|
||||
_cv.wait(lock);
|
||||
}
|
||||
T val = _queue.front();
|
||||
_queue.pop();
|
||||
return val;
|
||||
}
|
||||
bool empty() const {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
return _queue.size()==0;
|
||||
}
|
||||
size_t size() const {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
return _queue.size();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // THREADSAFE_QUEUE_H
|
||||
//////////////////////////////////////////////////////////////////////
|
@ -1,391 +0,0 @@
|
||||
#include "lasp_cpprtaudio.h"
|
||||
#include <RtAudio.h>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
|
||||
#if MS_WIN64
|
||||
typedef uint8_t u_int8_t;
|
||||
#endif
|
||||
|
||||
using std::atomic;
|
||||
|
||||
void fillRtAudioDeviceInfo(vector<DeviceInfo> &devinfolist) {
|
||||
|
||||
vector<RtAudio::Api> apis;
|
||||
RtAudio::getCompiledApi(apis);
|
||||
|
||||
for(auto api: apis) {
|
||||
RtAudio rtaudio(api);
|
||||
us count = rtaudio.getDeviceCount();
|
||||
for(us devno = 0; devno< count;devno++) {
|
||||
|
||||
RtAudio::DeviceInfo devinfo = rtaudio.getDeviceInfo(devno);
|
||||
if(!devinfo.probed) {
|
||||
// Device capabilities not successfully probed. Continue to next
|
||||
continue;
|
||||
}
|
||||
DeviceInfo d;
|
||||
switch(api){
|
||||
case RtAudio::LINUX_ALSA:
|
||||
d.api = rtaudioAlsaApi;
|
||||
break;
|
||||
case RtAudio::LINUX_PULSE:
|
||||
d.api = rtaudioPulseaudioApi;
|
||||
break;
|
||||
case RtAudio::WINDOWS_WASAPI:
|
||||
d.api = rtaudioWasapiApi;
|
||||
break;
|
||||
case RtAudio::WINDOWS_DS:
|
||||
d.api = rtaudioDsApi;
|
||||
break;
|
||||
case RtAudio::WINDOWS_ASIO:
|
||||
d.api = rtaudioAsioApi;
|
||||
break;
|
||||
default:
|
||||
cerr << "Not implemented RtAudio API, skipping." << endl;
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
d.device_name = devinfo.name;
|
||||
d.api_specific_devindex = devno;
|
||||
|
||||
for(us j=0; j<devinfo.sampleRates.size();j++){
|
||||
us rate = devinfo.sampleRates[j];
|
||||
d.availableSampleRates.push_back((double) rate);
|
||||
if(devinfo.preferredSampleRate == rate) {
|
||||
d.prefSampleRateIndex = j;
|
||||
}
|
||||
}
|
||||
|
||||
d.noutchannels = devinfo.outputChannels;
|
||||
d.ninchannels = devinfo.inputChannels;
|
||||
|
||||
d.availableInputRanges = {1.0};
|
||||
|
||||
RtAudioFormat formats = devinfo.nativeFormats;
|
||||
if(formats & RTAUDIO_SINT8) {
|
||||
d.availableDataTypes.push_back(dtype_int8);
|
||||
}
|
||||
if(formats & RTAUDIO_SINT16) {
|
||||
d.availableDataTypes.push_back(dtype_int16);
|
||||
}
|
||||
if(formats & RTAUDIO_SINT32) {
|
||||
d.availableDataTypes.push_back(dtype_int24);
|
||||
}
|
||||
if(formats & RTAUDIO_SINT32) {
|
||||
d.availableDataTypes.push_back(dtype_fl32);
|
||||
}
|
||||
if(formats & RTAUDIO_FLOAT64) {
|
||||
d.availableDataTypes.push_back(dtype_fl64);
|
||||
}
|
||||
if(d.availableDataTypes.size() == 0) {
|
||||
std::cerr << "RtAudio: No data types found in device!" << endl;
|
||||
}
|
||||
|
||||
d.prefDataTypeIndex = d.availableDataTypes.size() - 1;
|
||||
|
||||
d.availableFramesPerBlock.push_back(512);
|
||||
d.availableFramesPerBlock.push_back(1024);
|
||||
d.availableFramesPerBlock.push_back(2048);
|
||||
d.availableFramesPerBlock.push_back(4096);
|
||||
d.availableFramesPerBlock.push_back(8192);
|
||||
d.prefFramesPerBlockIndex = 1;
|
||||
|
||||
devinfolist.push_back(d);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int mycallback(void *outputBuffer, void *inputBuffer,
|
||||
unsigned int nFrames,
|
||||
double streamTime,
|
||||
RtAudioStreamStatus status,
|
||||
void *userData);
|
||||
|
||||
void myerrorcallback(RtAudioError::Type,const string& errorText);
|
||||
|
||||
class AudioDaq: public Daq {
|
||||
|
||||
SafeQueue<void*> *inqueue = NULL;
|
||||
SafeQueue<void*> *outqueue = NULL;
|
||||
SafeQueue<void*> *outDelayqueue = NULL;
|
||||
|
||||
RtAudio* rtaudio = NULL;
|
||||
RtAudio::StreamParameters* instreamparams = nullptr;
|
||||
RtAudio::StreamParameters* outstreamparams = nullptr;
|
||||
|
||||
us nFramesPerBlock;
|
||||
|
||||
public:
|
||||
AudioDaq(const DeviceInfo& devinfo,
|
||||
const DaqConfiguration& config):
|
||||
Daq(devinfo, config) {
|
||||
|
||||
nFramesPerBlock = this->framesPerBlock();
|
||||
|
||||
if(neninchannels(false) > 0) {
|
||||
instreamparams = new RtAudio::StreamParameters();
|
||||
instreamparams->nChannels = getHighestInChannel() + 1;
|
||||
if(instreamparams->nChannels < 1) {
|
||||
throw runtime_error("Invalid input number of channels");
|
||||
}
|
||||
instreamparams->firstChannel = 0;
|
||||
instreamparams->deviceId = devinfo.api_specific_devindex;
|
||||
}
|
||||
|
||||
if(nenoutchannels() > 0) {
|
||||
outstreamparams = new RtAudio::StreamParameters();
|
||||
outstreamparams->nChannels = getHighestOutChannel() + 1;
|
||||
if(outstreamparams->nChannels < 1) {
|
||||
throw runtime_error("Invalid output number of channels");
|
||||
}
|
||||
outstreamparams->firstChannel = 0;
|
||||
outstreamparams->deviceId = devinfo.api_specific_devindex;
|
||||
}
|
||||
|
||||
RtAudio::StreamOptions streamoptions;
|
||||
streamoptions.flags = RTAUDIO_NONINTERLEAVED | RTAUDIO_HOG_DEVICE;
|
||||
|
||||
streamoptions.numberOfBuffers = 2;
|
||||
streamoptions.streamName = "RtAudio stream";
|
||||
streamoptions.priority = 0;
|
||||
|
||||
RtAudioFormat format;
|
||||
DataType dtype = dataType();
|
||||
if(dtype == dtype_fl32) {
|
||||
format = RTAUDIO_FLOAT32;
|
||||
} else if(dtype == dtype_fl64) {
|
||||
format = RTAUDIO_FLOAT64;
|
||||
} else if(dtype == dtype_int8) {
|
||||
format = RTAUDIO_SINT8;
|
||||
} else if(dtype == dtype_int16) {
|
||||
format = RTAUDIO_SINT16;
|
||||
} else if(dtype == dtype_int32) {
|
||||
format = RTAUDIO_SINT32;
|
||||
} else {
|
||||
throw runtime_error("Invalid data type");
|
||||
}
|
||||
|
||||
try {
|
||||
rtaudio = new RtAudio((RtAudio::Api) devinfo.api.api_specific_subcode);
|
||||
if(!rtaudio) {
|
||||
throw runtime_error("RtAudio allocation failed");
|
||||
}
|
||||
rtaudio->openStream(
|
||||
outstreamparams,
|
||||
instreamparams,
|
||||
format,
|
||||
(us) samplerate(),
|
||||
(unsigned*) &nFramesPerBlock,
|
||||
&mycallback,
|
||||
(void*) this,
|
||||
&streamoptions,
|
||||
&myerrorcallback
|
||||
);
|
||||
} catch(RtAudioError& e) {
|
||||
if(rtaudio) delete rtaudio;
|
||||
if(instreamparams) delete instreamparams;
|
||||
if(outstreamparams) delete outstreamparams;
|
||||
throw;
|
||||
}
|
||||
if(monitorOutput) {
|
||||
outDelayqueue = new SafeQueue<void*>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
friend int mycallback(void *outputBuffer, void *inputBuffer,
|
||||
unsigned int nFrames,
|
||||
double streamTime,
|
||||
RtAudioStreamStatus status,
|
||||
void *userData);
|
||||
|
||||
|
||||
void start(SafeQueue<void*> *inqueue, SafeQueue<void*> *outqueue) {
|
||||
this->inqueue = inqueue;
|
||||
this->outqueue = outqueue;
|
||||
if(monitorOutput) {
|
||||
this->outDelayqueue = new SafeQueue<void*>();
|
||||
|
||||
}
|
||||
|
||||
if(isRunning()){
|
||||
throw runtime_error("Stream already running");
|
||||
}
|
||||
|
||||
if(neninchannels(false) > 0 && !inqueue) {
|
||||
throw runtime_error("inqueue argument not given");
|
||||
}
|
||||
if(nenoutchannels() > 0 && !outqueue) {
|
||||
throw runtime_error("outqueue argument not given");
|
||||
}
|
||||
assert(rtaudio);
|
||||
rtaudio->startStream();
|
||||
|
||||
}
|
||||
|
||||
void stop() {
|
||||
|
||||
if(!isRunning()) {
|
||||
cerr << "Stream is already stopped" << endl;
|
||||
}
|
||||
else {
|
||||
assert(rtaudio);
|
||||
rtaudio->stopStream();
|
||||
}
|
||||
if(inqueue) {
|
||||
inqueue = nullptr;
|
||||
}
|
||||
if(outqueue) {
|
||||
outqueue = nullptr;
|
||||
}
|
||||
if(outDelayqueue) {
|
||||
delete outDelayqueue;
|
||||
outDelayqueue = nullptr;
|
||||
}
|
||||
}
|
||||
bool isRunning() const {return (rtaudio && rtaudio->isStreamRunning());}
|
||||
|
||||
~AudioDaq() {
|
||||
assert(rtaudio);
|
||||
if(isRunning()) {
|
||||
stop();
|
||||
}
|
||||
if(rtaudio->isStreamOpen()) {
|
||||
rtaudio->closeStream();
|
||||
}
|
||||
|
||||
if(rtaudio) delete rtaudio;
|
||||
if(outDelayqueue) delete outDelayqueue;
|
||||
if(instreamparams) delete instreamparams;
|
||||
if(outstreamparams) delete outstreamparams;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Daq* createRtAudioDevice(const DeviceInfo& devinfo,
|
||||
const DaqConfiguration& config) {
|
||||
|
||||
AudioDaq *daq = NULL;
|
||||
|
||||
try {
|
||||
daq = new AudioDaq(devinfo, config);
|
||||
|
||||
} catch (runtime_error &e) {
|
||||
if (daq)
|
||||
delete daq;
|
||||
throw;
|
||||
}
|
||||
return daq;
|
||||
}
|
||||
|
||||
|
||||
int mycallback(
|
||||
void *outputBuffervoid,
|
||||
void *inputBuffervoid,
|
||||
unsigned int nFrames,
|
||||
double streamTime,
|
||||
RtAudioStreamStatus status,
|
||||
void *userData) {
|
||||
|
||||
u_int8_t* inputBuffer = (u_int8_t*) inputBuffervoid;
|
||||
u_int8_t* outputBuffer = (u_int8_t*) outputBuffervoid;
|
||||
|
||||
AudioDaq* daq = (AudioDaq*) userData;
|
||||
DataType dtype = daq->dataType();
|
||||
us neninchannels_inc_mon = daq->neninchannels();
|
||||
us nenoutchannels = daq->nenoutchannels();
|
||||
|
||||
bool monitorOutput = daq->monitorOutput;
|
||||
us bytesperchan = dtype.sw*nFrames;
|
||||
us monitorOffset = ((us) monitorOutput)*bytesperchan;
|
||||
|
||||
SafeQueue<void*> *inqueue = daq->inqueue;
|
||||
SafeQueue<void*> *outqueue = daq->outqueue;
|
||||
SafeQueue<void*> *outDelayqueue = daq->outDelayqueue;
|
||||
|
||||
const boolvec& eninchannels = daq->eninchannels;
|
||||
const boolvec& enoutchannels = daq->enoutchannels;
|
||||
|
||||
if(inputBuffer || monitorOutput) {
|
||||
|
||||
u_int8_t *inbuffercpy = (u_int8_t*) malloc(bytesperchan*neninchannels_inc_mon);
|
||||
if(inputBuffer) {
|
||||
us j=0; // OUR buffer channel counter
|
||||
us i=0; // RtAudio channel counter
|
||||
for(int ch=daq->getLowestInChannel();ch<=daq->getHighestInChannel();ch++) {
|
||||
if(eninchannels[ch]) {
|
||||
memcpy(
|
||||
&(inbuffercpy[monitorOffset+j*bytesperchan]),
|
||||
&(inputBuffer[i*bytesperchan]),
|
||||
bytesperchan);
|
||||
j++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if(monitorOutput) {
|
||||
assert(outDelayqueue);
|
||||
|
||||
if(!daq->outDelayqueue->empty()) {
|
||||
void* dat = daq->outDelayqueue->dequeue();
|
||||
memcpy((void*) inbuffercpy, dat, bytesperchan);
|
||||
free(dat);
|
||||
} else {
|
||||
cerr << "Warning: output delay queue appears empty!" << endl;
|
||||
memset(inbuffercpy, 0, bytesperchan);
|
||||
}
|
||||
}
|
||||
assert(inqueue);
|
||||
inqueue->enqueue(inbuffercpy);
|
||||
|
||||
}
|
||||
|
||||
if(outputBuffer) {
|
||||
assert(outqueue);
|
||||
if(!outqueue->empty()) {
|
||||
u_int8_t* outbuffercpy = (u_int8_t*) outqueue->dequeue();
|
||||
us j=0; // OUR buffer channel counter
|
||||
us i=0; // RtAudio channel counter
|
||||
for(us ch=0;ch<=daq->getHighestOutChannel();ch++) {
|
||||
/* cerr << "Copying from queue... " << endl; */
|
||||
if(enoutchannels[ch]) {
|
||||
memcpy(
|
||||
&(outputBuffer[i*bytesperchan]),
|
||||
&(outbuffercpy[j*bytesperchan]),
|
||||
bytesperchan);
|
||||
j++;
|
||||
}
|
||||
else {
|
||||
/* cerr << "unused output channel in list" << endl; */
|
||||
memset(
|
||||
&(outputBuffer[i*bytesperchan]),0,bytesperchan);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if(!monitorOutput) {
|
||||
free(outbuffercpy);
|
||||
} else {
|
||||
assert(outDelayqueue);
|
||||
outDelayqueue->enqueue((void*) outbuffercpy);
|
||||
}
|
||||
}
|
||||
else {
|
||||
cerr << "RtAudio backend: stream output buffer underflow!" << endl;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
void myerrorcallback(RtAudioError::Type,const string& errorText) {
|
||||
cerr << errorText << endl;
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
#ifndef RTAUDIO_H
|
||||
#define RTAUDIO_H
|
||||
#include "lasp_cppdaq.h"
|
||||
|
||||
Daq* createRtAudioDevice(const DeviceInfo& devinfo,
|
||||
const DaqConfiguration& config);
|
||||
|
||||
void fillRtAudioDeviceInfo(vector<DeviceInfo> &devinfolist);
|
||||
|
||||
#endif // RTAUDIO_H
|
||||
|
||||
|
||||
|
@ -1,34 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef LASP_CPPTHREAD_H
|
||||
#define LASP_CPPTHREAD_H
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <assert.h>
|
||||
// This is a small wrapper around the std library thread.
|
||||
|
||||
template <class T, typename F>
|
||||
class CPPThread {
|
||||
std::thread _thread;
|
||||
public:
|
||||
CPPThread(F threadfcn, T data) :
|
||||
_thread(threadfcn, data) { }
|
||||
|
||||
void join() {
|
||||
assert(_thread.joinable());
|
||||
_thread.join();
|
||||
}
|
||||
/* ~CPPThread() { */
|
||||
|
||||
/* } */
|
||||
|
||||
};
|
||||
|
||||
void CPPsleep_ms(unsigned int ms) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
|
||||
}
|
||||
void CPPsleep_us(unsigned int us) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(us));
|
||||
}
|
||||
|
||||
|
||||
#endif // LASP_CPPTHREAD_H
|
@ -1,636 +0,0 @@
|
||||
#include "lasp_cppuldaq.h"
|
||||
#include "lasp_config.h"
|
||||
#include "lasp_tracer.h"
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <uldaq.h>
|
||||
#include <vector>
|
||||
|
||||
using std::atomic;
|
||||
|
||||
/* using std::this_thread; */
|
||||
const us MAX_DEV_COUNT_PER_API = 100;
|
||||
const us UL_ERR_MSG_LEN = 512;
|
||||
|
||||
inline void showErr(UlError err) {
|
||||
if (err != ERR_NO_ERROR) {
|
||||
char errmsg[UL_ERR_MSG_LEN];
|
||||
ulGetErrMsg(err, errmsg);
|
||||
std::cerr << "UlError: " << errmsg << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
class DT9837A;
|
||||
void threadfcn(DT9837A *td);
|
||||
|
||||
class DT9837A : public Daq {
|
||||
|
||||
atomic<bool> stopThread;
|
||||
DaqDeviceHandle handle = 0;
|
||||
|
||||
std::thread *thread = NULL;
|
||||
SafeQueue<void *> *inqueue = NULL;
|
||||
SafeQueue<void *> *outqueue = NULL;
|
||||
|
||||
double *inbuffer = NULL;
|
||||
double *outbuffer = NULL;
|
||||
|
||||
us nFramesPerBlock;
|
||||
|
||||
public:
|
||||
DT9837A(const DeviceInfo &devinfo, const DaqConfiguration &config)
|
||||
: Daq(devinfo, config) {
|
||||
|
||||
// Some sanity checks
|
||||
if (eninchannels.size() != 4) {
|
||||
throw runtime_error("Invalid length of enabled inChannels vector");
|
||||
}
|
||||
|
||||
if (enoutchannels.size() != 1) {
|
||||
throw runtime_error("Invalid length of enabled outChannels vector");
|
||||
}
|
||||
|
||||
stopThread = false;
|
||||
|
||||
nFramesPerBlock = availableFramesPerBlock[framesPerBlockIndex];
|
||||
|
||||
if (nFramesPerBlock < 24 || nFramesPerBlock > 8192) {
|
||||
throw runtime_error("Unsensible number of samples per block chosen");
|
||||
}
|
||||
|
||||
if (samplerate() < 10000 || samplerate() > 51000) {
|
||||
throw runtime_error("Invalid sample rate");
|
||||
}
|
||||
|
||||
DaqDeviceDescriptor devdescriptors[MAX_DEV_COUNT_PER_API];
|
||||
DaqDeviceDescriptor descriptor;
|
||||
DaqDeviceInterface interfaceType = ANY_IFC;
|
||||
|
||||
UlError err;
|
||||
|
||||
us numdevs = MAX_DEV_COUNT_PER_API;
|
||||
err = ulGetDaqDeviceInventory(interfaceType, devdescriptors, (unsigned*) &numdevs);
|
||||
if (err != ERR_NO_ERROR) {
|
||||
throw runtime_error("Device inventarization failed");
|
||||
}
|
||||
|
||||
if ((us) api_specific_devindex >= numdevs) {
|
||||
throw runtime_error("Device number {deviceno} too high {err}. This could "
|
||||
"happen when the device is currently not connected");
|
||||
}
|
||||
|
||||
descriptor = devdescriptors[api_specific_devindex];
|
||||
|
||||
// get a handle to the DAQ device associated with the first descriptor
|
||||
handle = ulCreateDaqDevice(descriptor);
|
||||
|
||||
if (handle == 0) {
|
||||
throw runtime_error("Unable to create a handle to the specified DAQ "
|
||||
"device. Is the device currently in use? Please make sure to set "
|
||||
"the DAQ configuration in duplex mode if simultaneous input and "
|
||||
"output is required.");
|
||||
}
|
||||
|
||||
err = ulConnectDaqDevice(handle);
|
||||
if (err != ERR_NO_ERROR) {
|
||||
showErr(err);
|
||||
ulReleaseDaqDevice(handle);
|
||||
handle = 0;
|
||||
throw runtime_error("Unable to connect to device: {err}");
|
||||
}
|
||||
|
||||
for (us ch = 0; ch < 4; ch++) {
|
||||
|
||||
err = ulAISetConfigDbl(handle, AI_CFG_CHAN_SENSOR_SENSITIVITY, ch, 1.0);
|
||||
showErr(err);
|
||||
if (err != ERR_NO_ERROR) {
|
||||
throw runtime_error("Fatal: could normalize channel sensitivity");
|
||||
}
|
||||
|
||||
CouplingMode cm = inputACCouplingMode[ch] ? 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;
|
||||
err = ulAISetConfig(handle, AI_CFG_CHAN_IEPE_MODE, ch, iepe);
|
||||
if (err != ERR_NO_ERROR) {
|
||||
showErr(err);
|
||||
throw runtime_error("Fatal: could not set IEPE mode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DT9837A(const DT9837A &) = delete;
|
||||
|
||||
~DT9837A() {
|
||||
UlError err;
|
||||
if (isRunning()) {
|
||||
stop();
|
||||
}
|
||||
|
||||
if (handle) {
|
||||
err = ulDisconnectDaqDevice(handle);
|
||||
showErr(err);
|
||||
err = ulReleaseDaqDevice(handle);
|
||||
showErr(err);
|
||||
}
|
||||
}
|
||||
|
||||
bool isRunning() const { return bool(thread); }
|
||||
|
||||
void start(SafeQueue<void *> *inqueue, SafeQueue<void *> *outqueue) {
|
||||
if (isRunning()) {
|
||||
throw runtime_error("Thread is already running");
|
||||
}
|
||||
|
||||
bool hasinput = neninchannels() > 0;
|
||||
bool hasoutput = nenoutchannels() > 0;
|
||||
|
||||
if (hasinput && !inqueue) {
|
||||
throw runtime_error("Inqueue not given, while input is enabled");
|
||||
}
|
||||
|
||||
if (hasoutput && !outqueue) {
|
||||
throw runtime_error("outqueue not given, while output is enabled");
|
||||
}
|
||||
|
||||
if (hasinput) {
|
||||
assert(!inbuffer);
|
||||
inbuffer =
|
||||
new double[neninchannels() * nFramesPerBlock * 2]; // Watch the 2!
|
||||
}
|
||||
if (hasoutput) {
|
||||
assert(!outbuffer);
|
||||
outbuffer =
|
||||
new double[nenoutchannels() * nFramesPerBlock * 2]; // Watch the 2!
|
||||
}
|
||||
this->inqueue = inqueue;
|
||||
this->outqueue = outqueue;
|
||||
|
||||
stopThread = false;
|
||||
thread = new std::thread(threadfcn, this);
|
||||
}
|
||||
|
||||
void stop() {
|
||||
if (!isRunning()) {
|
||||
throw runtime_error("No data acquisition running");
|
||||
}
|
||||
assert(thread);
|
||||
|
||||
stopThread = true;
|
||||
thread->join();
|
||||
delete thread;
|
||||
thread = NULL;
|
||||
|
||||
outqueue = NULL;
|
||||
inqueue = NULL;
|
||||
if (inbuffer) {
|
||||
delete inbuffer;
|
||||
inbuffer = nullptr;
|
||||
}
|
||||
if (outbuffer) {
|
||||
delete outbuffer;
|
||||
outbuffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
friend void threadfcn(DT9837A *);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Create an empty buffer and fill it with zeros.
|
||||
*
|
||||
* @param size The number of elements in the array
|
||||
*
|
||||
* @return Pointer to the array
|
||||
*/
|
||||
static double* createZeroBuffer(size_t size) {
|
||||
|
||||
double* buf = static_cast<double *>(
|
||||
malloc(sizeof(double) * size));
|
||||
|
||||
for (us sample = 0; sample < size; sample++) {
|
||||
buf[sample] = 0;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
/**
|
||||
* @brief Copy samples from one linear array to the next.
|
||||
*
|
||||
* @param[in] from Buffer to copy from
|
||||
* @param[out] to Buffer to copy to
|
||||
* @param startFrom The position to start in the from-buffer
|
||||
* @param startTo The position to start in the to-buffer
|
||||
* @param N The number of samples to copy.
|
||||
*/
|
||||
static inline void copySamples(double* from,double* to,
|
||||
const us startFrom,const us startTo,const us N) {
|
||||
|
||||
for (us sample = 0; sample < N; sample++) {
|
||||
to[startTo + sample] = from[startFrom + sample];
|
||||
}
|
||||
}
|
||||
|
||||
void threadfcn(DT9837A *td) {
|
||||
|
||||
std::cerr << "Starting DAQ Thread fcn" << endl;
|
||||
|
||||
const us nenoutchannels = td->nenoutchannels();
|
||||
us neninchannels = td->neninchannels();
|
||||
const us nFramesPerBlock = td->nFramesPerBlock;
|
||||
|
||||
const bool hasinput = neninchannels > 0;
|
||||
const bool hasoutput = nenoutchannels > 0;
|
||||
|
||||
bool monitorOutput = td->monitorOutput;
|
||||
|
||||
double *inbuffer = td->inbuffer;
|
||||
double *outbuffer = td->outbuffer;
|
||||
|
||||
SafeQueue<void *> *inqueue = td->inqueue;
|
||||
SafeQueue<void *> *outqueue = td->outqueue;
|
||||
|
||||
ScanStatus inscanstat;
|
||||
ScanStatus outscanstat;
|
||||
TransferStatus inxstat, outxstat;
|
||||
|
||||
double samplerate = td->samplerate();
|
||||
|
||||
const double sleeptime = ((double)nFramesPerBlock) / (4 * samplerate);
|
||||
const us sleeptime_us = (us)(sleeptime * 1e6);
|
||||
/* cerr << "Sleep time in loop: " << sleeptime_us << "us." << endl; */
|
||||
if (sleeptime_us < 10) {
|
||||
cerr << "ERROR: Too small buffer size (nFramesPerBlock) chosen!" << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
const us buffer_mid_idx_in = neninchannels * nFramesPerBlock;
|
||||
const us buffer_mid_idx_out = nenoutchannels * nFramesPerBlock;
|
||||
|
||||
DaqDeviceHandle handle = td->handle;
|
||||
assert(handle);
|
||||
|
||||
DaqInScanFlag inscanflags = DAQINSCAN_FF_DEFAULT;
|
||||
AOutScanFlag outscanflags = AOUTSCAN_FF_DEFAULT;
|
||||
ScanOption scanoptions = SO_CONTINUOUS;
|
||||
UlError err = ERR_NO_ERROR;
|
||||
|
||||
DaqInChanDescriptor *indesc = NULL;
|
||||
|
||||
bool topinenqueued = true;
|
||||
// Start with true here, to not copy here the first time
|
||||
bool botinenqueued = true;
|
||||
|
||||
bool topoutenqueued = true;
|
||||
bool botoutenqueued = true;
|
||||
|
||||
size_t inTotalCount = 0;
|
||||
size_t outTotalCount = 0;
|
||||
|
||||
// initialize output, if any
|
||||
if (hasoutput) {
|
||||
assert(nenoutchannels == 1);
|
||||
assert(outqueue);
|
||||
|
||||
// Initialize the buffer with zeros, before pushing any data.
|
||||
for (us sample = 0; sample < 2 * nFramesPerBlock; sample++) {
|
||||
outbuffer[sample] = 0;
|
||||
}
|
||||
|
||||
cerr << "Starting output DAC" << endl;
|
||||
err = ulAOutScan(handle, 0, 0, BIP10VOLTS,
|
||||
/* BIP60VOLTS, */
|
||||
2 * td->nFramesPerBlock, // Watch the 2 here!
|
||||
&samplerate, scanoptions, outscanflags, outbuffer);
|
||||
|
||||
if (err != ERR_NO_ERROR) {
|
||||
showErr(err);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize input, if any
|
||||
if (hasinput) {
|
||||
indesc = new DaqInChanDescriptor[neninchannels];
|
||||
us j = 0;
|
||||
for (us chin = 0; chin < 4; chin++) {
|
||||
if (td->eninchannels[chin] == true) {
|
||||
indesc[j].type = DAQI_ANALOG_SE;
|
||||
indesc[j].channel = chin;
|
||||
|
||||
double rangeval = td->inputRangeForChannel(chin);
|
||||
Range rangenum;
|
||||
if (abs(rangeval - 1.0) < 1e-8) {
|
||||
rangenum = BIP1VOLTS;
|
||||
} else if (abs(rangeval - 10.0) < 1e-8) {
|
||||
rangenum = BIP10VOLTS;
|
||||
} else {
|
||||
std::cerr << "Fatal: input range value is invalid" << endl;
|
||||
goto exit;
|
||||
}
|
||||
indesc[j].range = rangenum;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
// Overwrite last channel
|
||||
if (monitorOutput) {
|
||||
indesc[j].type = DAQI_DAC;
|
||||
indesc[j].channel = 0;
|
||||
indesc[j].range = BIP10VOLTS;
|
||||
j++;
|
||||
}
|
||||
assert(j == neninchannels);
|
||||
|
||||
cerr << "Starting input ADC" << endl;
|
||||
err = ulDaqInScan(handle, indesc, neninchannels,
|
||||
2 * td->nFramesPerBlock, // Watch the 2 here!
|
||||
&samplerate, scanoptions, inscanflags, inbuffer);
|
||||
if (err != ERR_NO_ERROR) {
|
||||
showErr(err);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Runs scan status on output, to catch up with position
|
||||
if (hasoutput) {
|
||||
err = ulAOutScanStatus(handle, &outscanstat, &outxstat);
|
||||
if (err != ERR_NO_ERROR) {
|
||||
showErr(err);
|
||||
goto exit;
|
||||
}
|
||||
outTotalCount = outxstat.currentScanCount;
|
||||
assert(outscanstat == SS_RUNNING);
|
||||
}
|
||||
|
||||
/* std::cerr << "Entering while loop" << endl; */
|
||||
/* std::cerr << "hasinput: " << hasinput << endl; */
|
||||
while (!td->stopThread && err == ERR_NO_ERROR) {
|
||||
/* std::cerr << "While..." << endl; */
|
||||
if (hasoutput) {
|
||||
err = ulAOutScanStatus(handle, &outscanstat, &outxstat);
|
||||
if (err != ERR_NO_ERROR) {
|
||||
showErr(err);
|
||||
goto exit;
|
||||
}
|
||||
assert(outscanstat == SS_RUNNING);
|
||||
|
||||
if (outxstat.currentScanCount > outTotalCount + 2 * nFramesPerBlock) {
|
||||
cerr << "***** WARNING: Missing output sample blocks, DAQ Scan count="
|
||||
<< outxstat.currentScanCount
|
||||
<< " while loop count = " << outTotalCount
|
||||
<< ", probably due to too small buffer size. *****" << endl;
|
||||
}
|
||||
outTotalCount = outxstat.currentScanCount;
|
||||
|
||||
/* std::cerr << "Samples scanned: " << outxstat.currentTotalCount <<
|
||||
* endl;
|
||||
*/
|
||||
if (outxstat.currentIndex < buffer_mid_idx_out) {
|
||||
topoutenqueued = false;
|
||||
if (!botoutenqueued) {
|
||||
/* cerr << "Copying output buffer to bottom" << endl; */
|
||||
double *bufcpy;
|
||||
assert(nenoutchannels > 0);
|
||||
if (!outqueue->empty()) {
|
||||
bufcpy = (double *)outqueue->dequeue();
|
||||
} else {
|
||||
cerr << "******* WARNING: OUTPUTQUEUE UNDERFLOW, FILLING SIGNAL "
|
||||
"QUEUE WITH ZEROS ***********"
|
||||
<< endl;
|
||||
bufcpy = createZeroBuffer(nFramesPerBlock*nenoutchannels);
|
||||
}
|
||||
copySamples(bufcpy, outbuffer, 0, buffer_mid_idx_out, nFramesPerBlock);
|
||||
free(bufcpy);
|
||||
botoutenqueued = true;
|
||||
}
|
||||
} else {
|
||||
botoutenqueued = false;
|
||||
if (!topoutenqueued) {
|
||||
/* cerr << "Copying output buffer to top" << endl; */
|
||||
double *bufcpy;
|
||||
assert(nenoutchannels > 0);
|
||||
if (!outqueue->empty()) {
|
||||
bufcpy = (double *)outqueue->dequeue();
|
||||
} else {
|
||||
cerr << "******* WARNING: OUTPUTQUEUE UNDERFLOW, FILLING SIGNAL "
|
||||
"QUEUE WITH ZEROS ***********"
|
||||
<< endl;
|
||||
bufcpy = createZeroBuffer(nFramesPerBlock*nenoutchannels);
|
||||
}
|
||||
copySamples(bufcpy, outbuffer, 0, 0, nFramesPerBlock);
|
||||
free(bufcpy);
|
||||
topoutenqueued = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasinput) {
|
||||
err = ulDaqInScanStatus(handle, &inscanstat, &inxstat);
|
||||
if (err != ERR_NO_ERROR) {
|
||||
showErr(err);
|
||||
goto exit;
|
||||
}
|
||||
assert(inscanstat == SS_RUNNING);
|
||||
if (inxstat.currentScanCount > inTotalCount + 2 * nFramesPerBlock) {
|
||||
cerr << "***** ERROR: Missing input sample blocks, count="
|
||||
<< inxstat.currentScanCount
|
||||
<< ", probably due to too small buffer size. Exiting thread. "
|
||||
"*****"
|
||||
<< endl;
|
||||
break;
|
||||
}
|
||||
inTotalCount = inxstat.currentScanCount;
|
||||
|
||||
if ((us) inxstat.currentIndex < buffer_mid_idx_in) {
|
||||
topinenqueued = false;
|
||||
if (!botinenqueued) {
|
||||
/* cerr << "Copying in buffer bot" << endl; */
|
||||
double *bufcpy = static_cast<double *>(
|
||||
malloc(sizeof(double) * nFramesPerBlock * neninchannels));
|
||||
us monitoroffset = monitorOutput ? 1 : 0;
|
||||
assert(neninchannels > 0);
|
||||
for (us channel = 0; channel < (neninchannels - monitoroffset);
|
||||
channel++) {
|
||||
for (us sample = 0; sample < nFramesPerBlock; sample++) {
|
||||
bufcpy[(monitoroffset + channel) * nFramesPerBlock + sample] =
|
||||
inbuffer[buffer_mid_idx_in + sample * neninchannels +
|
||||
channel];
|
||||
}
|
||||
}
|
||||
if (monitorOutput) {
|
||||
// Monitor output goes to first channel, that is
|
||||
// our convention
|
||||
us channel = neninchannels - 1;
|
||||
for (us sample = 0; sample < nFramesPerBlock; sample++) {
|
||||
bufcpy[sample] = inbuffer[buffer_mid_idx_in +
|
||||
sample * neninchannels + channel];
|
||||
}
|
||||
}
|
||||
inqueue->enqueue((void *)bufcpy);
|
||||
botinenqueued = true;
|
||||
}
|
||||
} else {
|
||||
botinenqueued = false;
|
||||
if (!topinenqueued) {
|
||||
double *bufcpy = static_cast<double *>(
|
||||
malloc(sizeof(double) * nFramesPerBlock * neninchannels));
|
||||
us monitoroffset = monitorOutput ? 1 : 0;
|
||||
assert(neninchannels > 0);
|
||||
for (us channel = 0; channel < (neninchannels - monitoroffset);
|
||||
channel++) {
|
||||
for (us sample = 0; sample < nFramesPerBlock; sample++) {
|
||||
bufcpy[(monitoroffset + channel) * nFramesPerBlock + sample] =
|
||||
inbuffer[sample * neninchannels + channel];
|
||||
}
|
||||
}
|
||||
if (monitorOutput) {
|
||||
// Monitor output goes to first channel, that is
|
||||
// our convention
|
||||
us channel = neninchannels - 1;
|
||||
for (us sample = 0; sample < nFramesPerBlock; sample++) {
|
||||
bufcpy[sample] = inbuffer[sample * neninchannels + channel];
|
||||
}
|
||||
}
|
||||
|
||||
/* cerr << "Copying in buffer top" << endl; */
|
||||
inqueue->enqueue((void *)bufcpy);
|
||||
topinenqueued = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(sleeptime_us));
|
||||
|
||||
} // End of while loop
|
||||
/* std::cerr << "Exit of while loop" << endl; */
|
||||
|
||||
exit:
|
||||
|
||||
if (hasoutput) {
|
||||
ulAOutScanStop(handle);
|
||||
if (err != ERR_NO_ERROR) {
|
||||
showErr(err);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasinput) {
|
||||
ulDaqInScanStop(handle);
|
||||
if (err != ERR_NO_ERROR) {
|
||||
showErr(err);
|
||||
}
|
||||
}
|
||||
|
||||
if (indesc)
|
||||
delete indesc;
|
||||
std::cerr << "Exit of DAQ thread fcn" << endl;
|
||||
}
|
||||
|
||||
Daq *createUlDaqDevice(const DeviceInfo &devinfo,
|
||||
const DaqConfiguration &config) {
|
||||
|
||||
DT9837A *daq = NULL;
|
||||
|
||||
try {
|
||||
daq = new DT9837A(devinfo, config);
|
||||
|
||||
} catch (runtime_error &e) {
|
||||
if (daq)
|
||||
delete daq;
|
||||
throw;
|
||||
}
|
||||
return daq;
|
||||
}
|
||||
|
||||
|
||||
void fillUlDaqDeviceInfo(vector<DeviceInfo> &devinfolist) {
|
||||
|
||||
fsTRACE(15);
|
||||
|
||||
UlError err;
|
||||
unsigned int numdevs = MAX_DEV_COUNT_PER_API;
|
||||
|
||||
DaqDeviceDescriptor devdescriptors[MAX_DEV_COUNT_PER_API];
|
||||
DaqDeviceDescriptor descriptor;
|
||||
DaqDeviceInterface interfaceType = ANY_IFC;
|
||||
|
||||
err = ulGetDaqDeviceInventory(interfaceType, devdescriptors,static_cast<unsigned*>(&numdevs));
|
||||
|
||||
if (err != ERR_NO_ERROR) {
|
||||
throw std::runtime_error("UlDaq device inventarization failed");
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < numdevs; i++) {
|
||||
|
||||
descriptor = devdescriptors[i];
|
||||
|
||||
DeviceInfo devinfo;
|
||||
devinfo.api = uldaqapi;
|
||||
string name, interface;
|
||||
|
||||
if (string(descriptor.productName) == "DT9837A") {
|
||||
if (descriptor.devInterface == USB_IFC) {
|
||||
name = "USB - ";
|
||||
} else if (descriptor.devInterface == BLUETOOTH_IFC) {
|
||||
/* devinfo. */
|
||||
name = "Bluetooth - ";
|
||||
} else if (descriptor.devInterface == ETHERNET_IFC) {
|
||||
/* devinfo. */
|
||||
name = "Ethernet - ";
|
||||
}
|
||||
|
||||
name += string(descriptor.productName) + " ";
|
||||
name += string(descriptor.uniqueId);
|
||||
devinfo.device_name = name;
|
||||
|
||||
devinfo.api_specific_devindex = i;
|
||||
devinfo.availableDataTypes.push_back(dtype_fl64);
|
||||
devinfo.prefDataTypeIndex = 0;
|
||||
|
||||
devinfo.availableSampleRates.push_back(8000);
|
||||
devinfo.availableSampleRates.push_back(10000);
|
||||
devinfo.availableSampleRates.push_back(11025);
|
||||
devinfo.availableSampleRates.push_back(16000);
|
||||
devinfo.availableSampleRates.push_back(20000);
|
||||
devinfo.availableSampleRates.push_back(22050);
|
||||
devinfo.availableSampleRates.push_back(24000);
|
||||
devinfo.availableSampleRates.push_back(32000);
|
||||
devinfo.availableSampleRates.push_back(44056);
|
||||
devinfo.availableSampleRates.push_back(44100);
|
||||
devinfo.availableSampleRates.push_back(47250);
|
||||
devinfo.availableSampleRates.push_back(48000);
|
||||
devinfo.availableSampleRates.push_back(50000);
|
||||
devinfo.availableSampleRates.push_back(50400);
|
||||
devinfo.availableSampleRates.push_back(51000);
|
||||
|
||||
devinfo.prefSampleRateIndex = 11;
|
||||
|
||||
devinfo.availableFramesPerBlock.push_back(512);
|
||||
devinfo.availableFramesPerBlock.push_back(1024);
|
||||
devinfo.availableFramesPerBlock.push_back(2048);
|
||||
devinfo.availableFramesPerBlock.push_back(4096);
|
||||
devinfo.availableFramesPerBlock.push_back(8192);
|
||||
devinfo.prefFramesPerBlockIndex = 2;
|
||||
|
||||
devinfo.availableInputRanges = {1.0, 10.0};
|
||||
devinfo.prefInputRangeIndex = 0;
|
||||
|
||||
devinfo.ninchannels = 4;
|
||||
devinfo.noutchannels = 1;
|
||||
|
||||
devinfo.hasInputIEPE = true;
|
||||
devinfo.hasInputACCouplingSwitch = true;
|
||||
devinfo.hasInputTrigger = true;
|
||||
|
||||
// Finally, this devinfo is pushed back in list
|
||||
devinfolist.push_back(devinfo);
|
||||
}
|
||||
}
|
||||
|
||||
feTRACE(15);
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
#ifndef ULDAQ_H
|
||||
#define ULDAQ_H
|
||||
#include "lasp_cppqueue.h"
|
||||
#include "lasp_cppdaq.h"
|
||||
|
||||
Daq* createUlDaqDevice(const DeviceInfo& devinfo,
|
||||
const DaqConfiguration& config);
|
||||
|
||||
void fillUlDaqDeviceInfo(vector<DeviceInfo> &devinfolist);
|
||||
|
||||
#endif // ULDAQ_H
|
||||
|
||||
|
@ -1,13 +0,0 @@
|
||||
include "lasp_common_decls.pxd"
|
||||
|
||||
ctypedef struct PyStreamData
|
||||
|
||||
cdef class Daq:
|
||||
cdef:
|
||||
PyStreamData *sd
|
||||
cppDaq* daq_device
|
||||
cdef public:
|
||||
double samplerate
|
||||
unsigned int nFramesPerBlock
|
||||
|
||||
cdef cleanupStream(self, PyStreamData* sd)
|
@ -1,329 +0,0 @@
|
||||
cimport cython
|
||||
from ..lasp_common import AvType
|
||||
from .lasp_deviceinfo cimport DeviceInfo
|
||||
from .lasp_daqconfig cimport DaqConfiguration
|
||||
from numpy cimport import_array
|
||||
|
||||
from cpython.ref cimport PyObject,Py_INCREF, Py_DECREF
|
||||
import numpy as np
|
||||
import logging
|
||||
|
||||
__all__ = ['Daq']
|
||||
|
||||
cdef cnp.NPY_TYPES getCNumpyDataType(DataType& dt):
|
||||
if(dt == dtype_fl32):
|
||||
return cnp.NPY_FLOAT32
|
||||
elif(dt == dtype_fl64):
|
||||
return cnp.NPY_FLOAT64
|
||||
elif(dt == dtype_int8):
|
||||
return cnp.NPY_INT8
|
||||
elif(dt == dtype_int16):
|
||||
return cnp.NPY_INT16
|
||||
elif(dt == dtype_int32):
|
||||
return cnp.NPY_INT32
|
||||
else:
|
||||
raise ValueError('Unknown data type')
|
||||
|
||||
cdef getNumpyDataType(DataType& dt):
|
||||
if(dt == dtype_fl32):
|
||||
return np.dtype(np.float32)
|
||||
elif(dt == dtype_fl64):
|
||||
return np.dtype(np.float64)
|
||||
elif(dt == dtype_int8):
|
||||
return np.dtype(np.int8)
|
||||
elif(dt == dtype_int16):
|
||||
return np.dtype(np.int16)
|
||||
elif(dt == dtype_int32):
|
||||
return np.dtype(np.int32)
|
||||
else:
|
||||
raise ValueError('Unknown data type')
|
||||
|
||||
DEF QUEUE_BUFFER_TIME = 0.5
|
||||
|
||||
ctypedef struct PyStreamData:
|
||||
PyObject* pyCallback
|
||||
|
||||
# Flag used to pass the stopThread.
|
||||
atomic[bool] stopThread
|
||||
|
||||
# Flag to indicate that the signal generator queue has been filled for the
|
||||
# first time.
|
||||
atomic[bool] ready
|
||||
|
||||
# Number of frames per block
|
||||
unsigned nFramesPerBlock
|
||||
|
||||
# Number of bytes per channel
|
||||
unsigned int nBytesPerChan
|
||||
|
||||
unsigned ninchannels
|
||||
unsigned noutchannels
|
||||
|
||||
double samplerate
|
||||
|
||||
cnp.NPY_TYPES npy_format
|
||||
|
||||
# If either of these queue pointers are NULL, it means the stream does not have an
|
||||
# input, or output.
|
||||
SafeQueue[void*] *inQueue
|
||||
SafeQueue[void*] *outQueue
|
||||
CPPThread[void*, void (*)(void*)] *thread
|
||||
|
||||
cdef void audioCallbackPythonThreadFunction(void* voidsd) nogil:
|
||||
cdef:
|
||||
PyStreamData* sd = <PyStreamData*> voidsd
|
||||
|
||||
double* inbuffer = NULL
|
||||
double* outbuffer = NULL
|
||||
|
||||
unsigned noutchannels= sd.noutchannels
|
||||
unsigned ninchannels= sd.ninchannels
|
||||
unsigned nBytesPerChan= sd.nBytesPerChan
|
||||
unsigned nFramesPerBlock= sd.nFramesPerBlock
|
||||
|
||||
double sleeptime = (<double> sd.nFramesPerBlock)/(8*sd.samplerate);
|
||||
# Sleep time in microseconds
|
||||
us sleeptime_us = <us> (sleeptime*1e6);
|
||||
|
||||
us nblocks_buffer = <us> max(1, (QUEUE_BUFFER_TIME * sd.samplerate /
|
||||
sd.nFramesPerBlock))
|
||||
|
||||
with gil:
|
||||
import_array()
|
||||
npy_format = cnp.NPY_FLOAT64
|
||||
callback = <object> sd.pyCallback
|
||||
# print(f'Number of input channels: {ninchannels}')
|
||||
# print(f'Number of out channels: {noutchannels}')
|
||||
# fprintf(stderr, 'Sleep time: %d us\n', sleeptime_us)
|
||||
|
||||
if sd.outQueue:
|
||||
for i in range(nblocks_buffer):
|
||||
outbuffer = <double*> malloc(sizeof(double)*nBytesPerChan*noutchannels)
|
||||
memset(outbuffer, 0, sizeof(double)*nBytesPerChan*noutchannels)
|
||||
sd.outQueue.enqueue(<double*> outbuffer)
|
||||
sd.ready.store(True)
|
||||
|
||||
while not sd.stopThread.load():
|
||||
with gil:
|
||||
if sd.outQueue:
|
||||
while sd.outQueue.size() < nblocks_buffer:
|
||||
outbuffer = <double*> malloc(sizeof(double)*nBytesPerChan*noutchannels)
|
||||
|
||||
npy_output = <object> data_to_ndarray(
|
||||
outbuffer,
|
||||
nFramesPerBlock,
|
||||
noutchannels,
|
||||
sd.npy_format,
|
||||
False, # Do not transfer ownership to the temporary
|
||||
# Numpy container
|
||||
True) # F-contiguous
|
||||
try:
|
||||
rval = callback(None, npy_output, nFramesPerBlock)
|
||||
|
||||
except Exception as e:
|
||||
logging.error('exception in Cython callback for audio output: ', str(e))
|
||||
return
|
||||
|
||||
sd.outQueue.enqueue(<double*> outbuffer)
|
||||
|
||||
if sd.inQueue and not sd.inQueue.empty():
|
||||
# Waiting indefinitely on the queue...
|
||||
inbuffer = <double*> sd.inQueue.dequeue()
|
||||
if inbuffer == NULL:
|
||||
logging.debug('Received empty buffer on input, stopping thread...\n')
|
||||
return
|
||||
|
||||
npy_input = <object> data_to_ndarray(
|
||||
inbuffer,
|
||||
nFramesPerBlock,
|
||||
ninchannels,
|
||||
sd.npy_format,
|
||||
True, # Do transfer ownership
|
||||
True) # F-contiguous is True: data is Fortran-cont.
|
||||
|
||||
try:
|
||||
rval = callback(npy_input, None, nFramesPerBlock)
|
||||
|
||||
except Exception as e:
|
||||
logging.error('exception in cython callback for audio input: ', str(e))
|
||||
return
|
||||
|
||||
CPPsleep_us(sleeptime_us);
|
||||
|
||||
# Outputbuffer is free'ed by the audiothread, so should not be touched
|
||||
# here.
|
||||
outbuffer = NULL
|
||||
|
||||
# Inputbuffer memory is owned by Numpy, so should not be free'ed
|
||||
inbuffer = NULL
|
||||
|
||||
cdef class Daq:
|
||||
|
||||
def __cinit__(self, DeviceInfo pydevinfo, DaqConfiguration pydaqconfig):
|
||||
|
||||
"""
|
||||
Acquires a daq handle, and opens the device
|
||||
|
||||
"""
|
||||
cdef:
|
||||
cppDaqConfiguration* daqconfig
|
||||
cppDeviceInfo* devinfo
|
||||
vector[cppDeviceInfo] devinfos
|
||||
|
||||
self.daq_device = NULL
|
||||
self.sd = NULL
|
||||
|
||||
daqconfig = &(pydaqconfig.config)
|
||||
devinfo = &(pydevinfo.devinfo)
|
||||
|
||||
try:
|
||||
self.daq_device = cppDaq.createDaq(devinfo[0], daqconfig[0])
|
||||
except Exception as e:
|
||||
raise
|
||||
self.nFramesPerBlock = self.daq_device.framesPerBlock()
|
||||
self.samplerate = self.daq_device.samplerate()
|
||||
|
||||
if self.nFramesPerBlock > 8192 or self.nFramesPerBlock < 512:
|
||||
del self.daq_device
|
||||
raise ValueError('Invalid number of nFramesPerBlock')
|
||||
|
||||
def __dealloc__(self):
|
||||
# fprintf(stderr, "UlDaq.__dealloc__\n")
|
||||
if self.sd is not NULL:
|
||||
logging.debug("UlDaq.__dealloc__: stopping stream.")
|
||||
self.stop()
|
||||
|
||||
if self.daq_device is not NULL:
|
||||
del self.daq_device
|
||||
self.daq_device = NULL
|
||||
|
||||
def getNumpyDataType(self):
|
||||
cdef:
|
||||
DataType dt = self.daq_device.dataType()
|
||||
return getNumpyDataType(dt)
|
||||
|
||||
|
||||
def isRunning(self):
|
||||
return self.sd is not NULL
|
||||
|
||||
@staticmethod
|
||||
def getDeviceInfo():
|
||||
cdef:
|
||||
vector[cppDeviceInfo] devinfos = cppDaq.getDeviceInfo()
|
||||
pydevinfo = {}
|
||||
for i in range(devinfos.size()):
|
||||
d = DeviceInfo()
|
||||
d.devinfo = <cppDeviceInfo> devinfos[i]
|
||||
if d.api not in pydevinfo.keys():
|
||||
pydevinfo[d.api] = [d]
|
||||
else:
|
||||
pydevinfo[d.api].append(d)
|
||||
|
||||
return pydevinfo
|
||||
|
||||
@cython.nonecheck(True)
|
||||
def start(self, audiocallback):
|
||||
"""
|
||||
Opens a stream with specified parameters
|
||||
|
||||
Args:
|
||||
avstream: AvStream instance
|
||||
|
||||
Returns: None
|
||||
"""
|
||||
if self.sd is not NULL:
|
||||
assert self.daq_device is not NULL
|
||||
raise RuntimeError('Stream is already opened.')
|
||||
|
||||
cdef:
|
||||
cppDaq* daq = self.daq_device
|
||||
unsigned nFramesPerBlock = self.nFramesPerBlock
|
||||
DataType dtype = self.daq_device.dataType()
|
||||
double samplerate = self.samplerate
|
||||
|
||||
|
||||
# All set, allocate the stream!
|
||||
self.sd = <PyStreamData*> malloc(sizeof(PyStreamData))
|
||||
if self.sd == NULL:
|
||||
del daq
|
||||
raise MemoryError('Could not allocate stream: memory error.')
|
||||
|
||||
|
||||
self.sd.stopThread.store(False)
|
||||
self.sd.ready.store(False)
|
||||
|
||||
self.sd.inQueue = NULL
|
||||
self.sd.outQueue = NULL
|
||||
|
||||
self.sd.thread = NULL
|
||||
self.sd.samplerate = <double> samplerate
|
||||
|
||||
self.sd.ninchannels = daq.neninchannels(True)
|
||||
self.sd.noutchannels = daq.nenoutchannels()
|
||||
self.sd.nBytesPerChan = nFramesPerBlock*dtype.sw
|
||||
self.sd.nFramesPerBlock = nFramesPerBlock
|
||||
self.sd.npy_format = getCNumpyDataType(dtype)
|
||||
|
||||
if daq.neninchannels(True) > 0:
|
||||
self.sd.inQueue = new SafeQueue[void*]()
|
||||
if daq.nenoutchannels() > 0:
|
||||
self.sd.outQueue = new SafeQueue[void*]()
|
||||
|
||||
self.sd.pyCallback = <PyObject*> audiocallback
|
||||
|
||||
# Increase reference count to the callback
|
||||
Py_INCREF(<object> audiocallback)
|
||||
|
||||
with nogil:
|
||||
self.sd.thread = new CPPThread[void*, void (*)(void*)](audioCallbackPythonThreadFunction,
|
||||
<void*> self.sd)
|
||||
|
||||
while not self.sd.ready.load():
|
||||
# Allow stream stome time to start
|
||||
CPPsleep_ms(100)
|
||||
|
||||
self.daq_device.start(
|
||||
self.sd.inQueue,
|
||||
self.sd.outQueue)
|
||||
|
||||
return self.daq_device.samplerate()
|
||||
|
||||
def stop(self):
|
||||
if self.sd is NULL:
|
||||
raise RuntimeError('Stream is not opened')
|
||||
|
||||
self.cleanupStream(self.sd)
|
||||
self.sd = NULL
|
||||
|
||||
cdef cleanupStream(self, PyStreamData* sd):
|
||||
|
||||
with nogil:
|
||||
if sd.thread:
|
||||
sd.stopThread.store(True)
|
||||
|
||||
if sd.inQueue:
|
||||
# If waiting in the input queue, hereby we let it run.
|
||||
sd.inQueue.enqueue(NULL)
|
||||
|
||||
sd.thread.join()
|
||||
del sd.thread
|
||||
sd.thread = NULL
|
||||
|
||||
if sd.inQueue:
|
||||
while not sd.inQueue.empty():
|
||||
free(sd.inQueue.dequeue())
|
||||
del sd.inQueue
|
||||
|
||||
if sd.outQueue:
|
||||
while not sd.outQueue.empty():
|
||||
free(sd.outQueue.dequeue())
|
||||
del sd.outQueue
|
||||
sd.outQueue = NULL
|
||||
logging.debug("End cleanup stream queues...\n")
|
||||
|
||||
if sd.pyCallback:
|
||||
Py_DECREF(<object> sd.pyCallback)
|
||||
sd.pyCallback = NULL
|
||||
|
||||
free(sd)
|
||||
|
@ -1,6 +0,0 @@
|
||||
include "lasp_common_decls.pxd"
|
||||
from .lasp_deviceinfo cimport DeviceInfo
|
||||
|
||||
cdef class DaqConfiguration:
|
||||
cdef:
|
||||
cppDaqConfiguration config
|
@ -1,295 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""!
|
||||
Author: J.A. de Jong - ASCEE
|
||||
|
||||
Description:
|
||||
|
||||
Data Acquistiion (DAQ) device descriptors, and the DAQ devices themselves
|
||||
|
||||
"""
|
||||
__all__ = ['DaqConfiguration', 'DaqConfigurations']
|
||||
from ..lasp_common import lasp_shelve, SIQtys, Qty
|
||||
from .lasp_device_common import DaqChannel
|
||||
import json
|
||||
|
||||
cdef class DaqConfigurations:
|
||||
cdef public:
|
||||
object input_config, output_config, duplex_mode
|
||||
|
||||
def __init__(self, duplex_mode, DaqConfiguration input_config,
|
||||
DaqConfiguration output_config):
|
||||
|
||||
self.input_config = input_config
|
||||
self.output_config = output_config
|
||||
self.duplex_mode = duplex_mode
|
||||
|
||||
def to_json(self):
|
||||
return json.dumps(
|
||||
dict(
|
||||
duplex_mode = self.duplex_mode,
|
||||
input_config = self.input_config.to_json(),
|
||||
output_config = self.output_config.to_json(),
|
||||
)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def from_json(daq_configs_json):
|
||||
configs_dict = json.loads(daq_configs_json)
|
||||
input_config = DaqConfiguration.from_json(configs_dict['input_config'])
|
||||
output_config = DaqConfiguration.from_json(configs_dict['output_config'])
|
||||
return DaqConfigurations(configs_dict['duplex_mode'],
|
||||
input_config,
|
||||
output_config)
|
||||
|
||||
@staticmethod
|
||||
def loadAllConfigs():
|
||||
"""
|
||||
Returns a dictionary of all configurations presets. The dictionary keys
|
||||
are the names of the configurations
|
||||
|
||||
"""
|
||||
with lasp_shelve() as sh:
|
||||
configs_json = sh.load('daqconfigs', {})
|
||||
configs = {}
|
||||
for name, val in configs_json.items():
|
||||
configs[name] = DaqConfigurations.from_json(val)
|
||||
return configs
|
||||
|
||||
@staticmethod
|
||||
def loadConfigs(name: str):
|
||||
"""
|
||||
Load a configuration preset, containing input config and output config
|
||||
"""
|
||||
|
||||
with lasp_shelve() as sh:
|
||||
configs_json = sh.load('daqconfigs', {})
|
||||
return DaqConfigurations.from_json(configs_json[name])
|
||||
|
||||
def saveConfigs(self, name):
|
||||
with lasp_shelve() as sh:
|
||||
configs_json = sh.load('daqconfigs', {})
|
||||
configs_json[name] = self.to_json()
|
||||
sh.store('daqconfigs', configs_json)
|
||||
|
||||
@staticmethod
|
||||
def deleteConfigs(name):
|
||||
with lasp_shelve() as sh:
|
||||
configs_json = sh.load('daqconfigs', {})
|
||||
del configs_json[name]
|
||||
sh.store('daqconfigs', configs_json)
|
||||
|
||||
def constructDaqConfig(dict_data):
|
||||
return DaqConfiguration.from_dict(dict_data)
|
||||
|
||||
cdef class DaqConfiguration:
|
||||
"""
|
||||
Initialize a device descriptor
|
||||
"""
|
||||
def __cinit__(self):
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return str(self.to_json())
|
||||
|
||||
@staticmethod
|
||||
def fromDeviceInfo(DeviceInfo devinfo):
|
||||
cdef:
|
||||
cppDaqConfiguration cconfig
|
||||
|
||||
d = DaqConfiguration()
|
||||
cconfig = cppDaqConfiguration(devinfo.devinfo)
|
||||
d.config = cconfig
|
||||
return d
|
||||
|
||||
@staticmethod
|
||||
def from_json(jsonstring):
|
||||
config_dict = json.loads(jsonstring)
|
||||
return DaqConfiguration.from_dict(config_dict)
|
||||
|
||||
def __reduce__(self):
|
||||
return (constructDaqConfig, (self.to_dict(),))
|
||||
|
||||
@staticmethod
|
||||
def from_dict(pydict):
|
||||
cdef:
|
||||
cppDaqConfiguration config
|
||||
vector[DaqApi] apis = DaqApi.getAvailableApis()
|
||||
|
||||
config.api = apis[pydict['apicode']]
|
||||
config.device_name = pydict['device_name'].encode('utf-8')
|
||||
config.eninchannels = pydict['eninchannels']
|
||||
config.enoutchannels = pydict['enoutchannels']
|
||||
|
||||
config.inchannel_names = [inchname.encode('utf-8') for inchname in
|
||||
pydict['inchannel_names']]
|
||||
|
||||
config.outchannel_names = [outchname.encode('utf-8') for outchname in
|
||||
pydict['outchannel_names']]
|
||||
|
||||
config.inchannel_sensitivities = pydict['inchannel_sensitivities']
|
||||
config.outchannel_sensitivities = pydict['outchannel_sensitivities']
|
||||
|
||||
config.sampleRateIndex = pydict['sampleRateIndex']
|
||||
config.framesPerBlockIndex = pydict['framesPerBlockIndex']
|
||||
config.dataTypeIndex = pydict['dataTypeIndex']
|
||||
config.monitorOutput = pydict['monitorOutput']
|
||||
config.inputIEPEEnabled = pydict['inputIEPEEnabled']
|
||||
config.inputACCouplingMode = pydict['inputACCouplingMode']
|
||||
config.inputRangeIndices = pydict['inputRangeIndices']
|
||||
config.inchannel_metadata = [inchmeta.encode('utf-8') for inchmeta in
|
||||
pydict['inchannel_metadata']]
|
||||
config.outchannel_metadata = [outchmeta.encode('utf-8') for outchmeta in
|
||||
pydict['outchannel_metadata']]
|
||||
|
||||
pydaqcfg = DaqConfiguration()
|
||||
pydaqcfg.config = config
|
||||
|
||||
return pydaqcfg
|
||||
|
||||
def to_dict(self):
|
||||
return dict(
|
||||
apicode = self.config.api.apicode,
|
||||
device_name = self.config.device_name.decode('utf-8'),
|
||||
|
||||
eninchannels = self.eninchannels(),
|
||||
enoutchannels = self.enoutchannels(),
|
||||
|
||||
inchannel_names = [name.decode('utf-8') for name in
|
||||
self.config.inchannel_names],
|
||||
|
||||
outchannel_names = [name.decode('utf-8') for name in
|
||||
self.config.outchannel_names],
|
||||
|
||||
inchannel_sensitivities = [sens for sens in
|
||||
self.config.inchannel_sensitivities],
|
||||
outchannel_sensitivities = [sens for sens in
|
||||
self.config.outchannel_sensitivities],
|
||||
|
||||
inchannel_metadata = [inchmeta.decode('utf-8') for inchmeta in
|
||||
self.config.inchannel_metadata],
|
||||
outchannel_metadata = [outchmeta.decode('utf-8') for outchmeta in
|
||||
self.config.outchannel_metadata],
|
||||
|
||||
sampleRateIndex = self.config.sampleRateIndex,
|
||||
dataTypeIndex = self.config.dataTypeIndex,
|
||||
framesPerBlockIndex = self.config.framesPerBlockIndex,
|
||||
monitorOutput = self.config.monitorOutput,
|
||||
|
||||
inputIEPEEnabled = self.config.inputIEPEEnabled,
|
||||
inputACCouplingMode = self.config.inputACCouplingMode,
|
||||
inputRangeIndices = self.config.inputRangeIndices,
|
||||
)
|
||||
|
||||
def to_json(self):
|
||||
return json.dumps(self.to_dict())
|
||||
|
||||
def getInChannel(self, i:int):
|
||||
return DaqChannel(
|
||||
channel_enabled=self.config.eninchannels.at(i),
|
||||
channel_name=self.config.inchannel_names.at(i).decode('utf-8'),
|
||||
sensitivity=self.config.inchannel_sensitivities.at(i),
|
||||
range_index=self.config.inputRangeIndices.at(i),
|
||||
ACCoupling_enabled=self.config.inputACCouplingMode.at(i),
|
||||
IEPE_enabled=self.config.inputIEPEEnabled.at(i),
|
||||
channel_metadata=self.config.inchannel_metadata.at(i).decode('utf-8'),
|
||||
)
|
||||
def getOutChannel(self, i:int):
|
||||
return DaqChannel(
|
||||
channel_enabled=self.config.enoutchannels.at(i),
|
||||
channel_name=self.config.outchannel_names.at(i).decode('utf-8'),
|
||||
channel_metadata=self.config.outchannel_metadata.at(i).decode('utf-8'),
|
||||
)
|
||||
|
||||
def setInChannel(self, i:int, ch: DaqChannel):
|
||||
self.config.eninchannels[i] = ch.channel_enabled
|
||||
self.config.inchannel_names[i] = ch.channel_name.encode('utf-8')
|
||||
self.config.inchannel_sensitivities[i] = ch.sensitivity
|
||||
self.config.inputRangeIndices[i] = ch.range_index
|
||||
self.config.inputACCouplingMode[i] = ch.ACCoupling_enabled
|
||||
self.config.inputIEPEEnabled[i] = ch.IEPE_enabled
|
||||
self.config.inchannel_metadata[i] = ch.channel_metadata.encode('utf-8')
|
||||
|
||||
def setOutChannel(self, i:int, ch: DaqChannel):
|
||||
self.config.enoutchannels[i] = ch.channel_enabled
|
||||
self.config.outchannel_names[i] = ch.channel_name.encode('utf-8')
|
||||
self.config.outchannel_metadata[i] = ch.channel_metadata.encode('utf-8')
|
||||
|
||||
def getEnabledInChannels(self, include_monitor=True):
|
||||
inch = []
|
||||
for i, enabled in enumerate(self.config.eninchannels):
|
||||
if enabled:
|
||||
inch.append(self.getInChannel(i))
|
||||
if include_monitor:
|
||||
outch = self.getEnabledOutChannels()
|
||||
if len(outch) > 0 and self.monitorOutput:
|
||||
inch.insert(0, outch[0])
|
||||
|
||||
return inch
|
||||
|
||||
def getEnabledOutChannels(self):
|
||||
outch = []
|
||||
for i, enabled in enumerate(self.config.enoutchannels):
|
||||
if enabled:
|
||||
outch.append(self.getOutChannel(i))
|
||||
return outch
|
||||
|
||||
@property
|
||||
def api(self):
|
||||
return self.config.api.apiname.decode('utf-8')
|
||||
|
||||
@api.setter
|
||||
def api(self, apitxt):
|
||||
cdef:
|
||||
vector[DaqApi] apis = DaqApi.getAvailableApis()
|
||||
for api in apis:
|
||||
if api.apiname.decode('utf-8') == apitxt:
|
||||
self.config.api = api
|
||||
return
|
||||
raise RuntimeError(f'Api {apitxt} unavailable')
|
||||
|
||||
def eninchannels(self):
|
||||
return self.config.eninchannels
|
||||
|
||||
def enoutchannels(self):
|
||||
return self.config.enoutchannels
|
||||
|
||||
@property
|
||||
def sampleRateIndex(self):
|
||||
return self.config.sampleRateIndex
|
||||
|
||||
@sampleRateIndex.setter
|
||||
def sampleRateIndex(self, int idx):
|
||||
self.config.sampleRateIndex = idx
|
||||
|
||||
@property
|
||||
def dataTypeIndex(self):
|
||||
return self.config.dataTypeIndex
|
||||
|
||||
@dataTypeIndex.setter
|
||||
def dataTypeIndex(self, int idx):
|
||||
self.config.dataTypeIndex = idx
|
||||
|
||||
@property
|
||||
def framesPerBlockIndex(self):
|
||||
return self.config.framesPerBlockIndex
|
||||
|
||||
@framesPerBlockIndex.setter
|
||||
def framesPerBlockIndex(self, int idx):
|
||||
self.config.framesPerBlockIndex = idx
|
||||
|
||||
@property
|
||||
def monitorOutput(self):
|
||||
return self.config.monitorOutput
|
||||
|
||||
@monitorOutput.setter
|
||||
def monitorOutput(self, bool idx):
|
||||
self.config.monitorOutput = idx
|
||||
|
||||
@property
|
||||
def device_name(self):
|
||||
return self.config.device_name.decode('utf-8')
|
||||
|
||||
@device_name.setter
|
||||
def device_name(self, idx):
|
||||
self.config.device_name = idx.encode('utf-8')
|
||||
|
@ -1,5 +0,0 @@
|
||||
include "lasp_common_decls.pxd"
|
||||
|
||||
cdef class DeviceInfo:
|
||||
cdef:
|
||||
cppDeviceInfo devinfo
|
@ -1,128 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""!
|
||||
Author: J.A. de Jong - ASCEE
|
||||
|
||||
Description:
|
||||
|
||||
DeviceInfo C++ object wrapper
|
||||
|
||||
"""
|
||||
__all__ = ['DeviceInfo']
|
||||
|
||||
def pickle(dat):
|
||||
dev = DeviceInfo()
|
||||
# print('DESERIALIZE****')
|
||||
dev.devinfo = dev.devinfo.deserialize(dat)
|
||||
return dev
|
||||
|
||||
cdef class DeviceInfo:
|
||||
def __cinit__(self):
|
||||
pass
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def __reduce__(self):
|
||||
serialized = self.devinfo.serialize()
|
||||
# print('SERIALIZE****')
|
||||
return (pickle, (serialized,))
|
||||
|
||||
@property
|
||||
def api(self): return self.devinfo.api.apiname.decode('utf-8')
|
||||
|
||||
@property
|
||||
def name(self): return self.devinfo.device_name.decode('utf-8')
|
||||
|
||||
def __repr__(self):
|
||||
return self.api + ': ' + self.name
|
||||
|
||||
@property
|
||||
def ninchannels(self): return self.devinfo.ninchannels
|
||||
|
||||
@property
|
||||
def noutchannels(self): return self.devinfo.noutchannels
|
||||
|
||||
@property
|
||||
def availableSampleRates(self): return self.devinfo.availableSampleRates
|
||||
|
||||
@property
|
||||
def availableFramesPerBlock(self):
|
||||
return self.devinfo.availableFramesPerBlock
|
||||
|
||||
@property
|
||||
def availableDataTypes(self):
|
||||
pydtypes = []
|
||||
for datatype in self.devinfo.availableDataTypes:
|
||||
pydtypes.append(datatype.name.decode('utf-8'))
|
||||
return pydtypes
|
||||
|
||||
@property
|
||||
def prefSampleRateIndex(self):
|
||||
return self.devinfo.prefSampleRateIndex
|
||||
|
||||
@property
|
||||
def prefSampleRate(self):
|
||||
return self.availableSampleRates[
|
||||
self.prefSampleRateIndex]
|
||||
|
||||
@property
|
||||
def prefFramesPerBlockIndex(self):
|
||||
return self.devinfo.prefFramesPerBlockIndex
|
||||
|
||||
@property
|
||||
def prefFramesPerBlock(self):
|
||||
return self.availableFramesPerBlock[
|
||||
self.prefFramesPerBlockIndex]
|
||||
|
||||
@property
|
||||
def prefDataTypeIndex(self):
|
||||
return self.devinfo.prefDataTypeIndex
|
||||
|
||||
@property
|
||||
def prefDataType(self):
|
||||
return self.availableDataTypes[
|
||||
self.prefDataTypeIndex]
|
||||
@property
|
||||
def hasInputIEPE(self):
|
||||
return self.devinfo.hasInputIEPE
|
||||
|
||||
@property
|
||||
def hasInputACCouplingSwitch(self):
|
||||
return self.devinfo.hasInputACCouplingSwitch
|
||||
|
||||
@property
|
||||
def hasInputTrigger(self):
|
||||
return self.devinfo.hasInputTrigger
|
||||
|
||||
@property
|
||||
def inputRanges(self):
|
||||
return self.devinfo.availableInputRanges
|
||||
|
||||
@staticmethod
|
||||
def getDeviceInfo():
|
||||
"""
|
||||
Returns device information objects (DeviceInfo) for all available
|
||||
devices
|
||||
"""
|
||||
cdef:
|
||||
vector[cppDeviceInfo] devinfos
|
||||
us numdevs, devno
|
||||
cppDeviceInfo* devinfo
|
||||
|
||||
devinfos = cppDaq.getDeviceInfo()
|
||||
numdevs = devinfos.size()
|
||||
|
||||
pydevinfos = []
|
||||
for devno in range(numdevs):
|
||||
devinfo = &(devinfos[devno])
|
||||
|
||||
d = DeviceInfo()
|
||||
d.devinfo = devinfo[0]
|
||||
|
||||
pydevinfos.append(d)
|
||||
return pydevinfos
|
||||
|
||||
@property
|
||||
def availableInputRanges(self):
|
||||
return self.devinfo.availableInputRanges
|
||||
|
@ -1,777 +0,0 @@
|
||||
include "lasp_common_decls.pxd"
|
||||
|
||||
__all__ = ['RtAudio', 'get_numpy_dtype_from_format_string',
|
||||
'get_sampwidth_from_format_string']
|
||||
|
||||
cdef extern from "RtAudio.h" nogil:
|
||||
ctypedef unsigned long RtAudioStreamStatus
|
||||
RtAudioStreamStatus RTAUDIO_INPUT_OVERFLOW
|
||||
RtAudioStreamStatus RTAUDIO_OUTPUT_UNDERFLOW
|
||||
|
||||
cdef cppclass RtAudioError:
|
||||
ctypedef enum Type:
|
||||
WARNING
|
||||
DEBUG_WARNING
|
||||
UNSPECIFIED
|
||||
NO_DEVICES_FOUND
|
||||
INVALID_DEVICE
|
||||
MEMORY_ERROR
|
||||
INVALID_PARAMETER
|
||||
INVALID_USE
|
||||
DRIVER_ERROR
|
||||
SYSTEM_ERROR
|
||||
THREAD_ERROR
|
||||
|
||||
ctypedef unsigned long RtAudioStreamFlags
|
||||
RtAudioStreamFlags RTAUDIO_NONINTERLEAVED
|
||||
RtAudioStreamFlags RTAUDIO_MINIMIZE_LATENCY
|
||||
RtAudioStreamFlags RTAUDIO_HOG_DEVICE
|
||||
RtAudioStreamFlags RTAUDIO_SCHEDULE_REALTIME
|
||||
RtAudioStreamFlags RTAUDIO_ALSA_USE_DEFAULT
|
||||
RtAudioStreamFlags RTAUDIO_JACK_DONT_CONNECT
|
||||
|
||||
ctypedef unsigned long RtAudioFormat
|
||||
RtAudioFormat RTAUDIO_SINT8
|
||||
RtAudioFormat RTAUDIO_SINT16
|
||||
RtAudioFormat RTAUDIO_SINT24
|
||||
RtAudioFormat RTAUDIO_SINT32
|
||||
RtAudioFormat RTAUDIO_FLOAT32
|
||||
RtAudioFormat RTAUDIO_FLOAT64
|
||||
|
||||
ctypedef int (*RtAudioCallback)(void* outputBuffer,
|
||||
void* inputBuffer,
|
||||
unsigned int nFramesPerBlock,
|
||||
double streamTime,
|
||||
RtAudioStreamStatus status,
|
||||
void* userData)
|
||||
|
||||
ctypedef void (*RtAudioErrorCallback)(RtAudioError.Type _type,
|
||||
const string& errortxt)
|
||||
|
||||
cdef cppclass cppRtAudio "RtAudio":
|
||||
enum Api:
|
||||
UNSPECIFIED
|
||||
LINUX_ALSA
|
||||
LINUX_PULSE
|
||||
MACOSX_CORE
|
||||
WINDOWS_WASAPI
|
||||
WINDOWS_ASIO
|
||||
WINDOWS_DS
|
||||
|
||||
cppclass DeviceInfo:
|
||||
bool probed
|
||||
string name
|
||||
unsigned int outputChannels
|
||||
unsigned int inputChannels
|
||||
unsigned int duplexChannels
|
||||
bool isDefaultOutput
|
||||
bool isDefaultInput
|
||||
vector[unsigned int] sampleRates
|
||||
unsigned int preferredSampleRate
|
||||
RtAudioFormat nativeFormats
|
||||
|
||||
cppclass StreamOptions:
|
||||
StreamOptions()
|
||||
RtAudioStreamFlags flags
|
||||
unsigned int numberOfBuffers
|
||||
string streamName
|
||||
int priority
|
||||
cppclass StreamParameters:
|
||||
StreamParameters()
|
||||
unsigned int deviceId
|
||||
unsigned int nChannels
|
||||
unsigned int firstChannel
|
||||
|
||||
@staticmethod
|
||||
void getCompiledApi(vector[cppRtAudio.Api]& apis)
|
||||
|
||||
@staticmethod
|
||||
cppRtAudio.Api getCompiledApiByName(string& name)
|
||||
|
||||
@staticmethod
|
||||
string getApiDisplayName(cppRtAudio.Api api)
|
||||
|
||||
@staticmethod
|
||||
string getApiName(cppRtAudio.Api api)
|
||||
|
||||
# RtAudio() except +
|
||||
cppRtAudio() except +
|
||||
cppRtAudio(cppRtAudio.Api api) except +
|
||||
|
||||
# ~RtAudio() Destructors should not be listed
|
||||
|
||||
unsigned int getDeviceCount()
|
||||
DeviceInfo getDeviceInfo(unsigned int device)
|
||||
unsigned int getDefaultOutputDevice()
|
||||
unsigned int getDefaultInputDevice()
|
||||
void openStream(StreamParameters* outputParameters,
|
||||
StreamParameters* intputParameters,
|
||||
RtAudioFormat _format,
|
||||
unsigned int sampleRate,
|
||||
unsigned int* bufferFrames,
|
||||
RtAudioCallback callback,
|
||||
void* userData,
|
||||
void* StreamOptions,
|
||||
RtAudioErrorCallback) except +
|
||||
void closeStream()
|
||||
void startStream() except +
|
||||
void stopStream() except +
|
||||
void abortStream() except +
|
||||
bool isStreamOpen()
|
||||
bool isStreamRunning()
|
||||
double getStreamTime()
|
||||
void setStreamTime(double) except +
|
||||
long getStreamLatency()
|
||||
unsigned int getStreamSampleRate()
|
||||
void showWarnings(bool value)
|
||||
|
||||
|
||||
_formats_strkey = {
|
||||
'8-bit integers': (RTAUDIO_SINT8, 1, np.int8),
|
||||
'16-bit integers': (RTAUDIO_SINT16, 2, np.int16),
|
||||
'24-bit integers': (RTAUDIO_SINT24, 3),
|
||||
'32-bit integers': (RTAUDIO_SINT32, 4, np.int32),
|
||||
'32-bit floats': (RTAUDIO_FLOAT32, 4, np.float32),
|
||||
'64-bit floats': (RTAUDIO_FLOAT64, 8, np.float64),
|
||||
}
|
||||
_formats_rtkey = {
|
||||
RTAUDIO_SINT8: ('8-bit integers', 1, cnp.NPY_INT8),
|
||||
RTAUDIO_SINT16: ('16-bit integers', 2, cnp.NPY_INT16),
|
||||
RTAUDIO_SINT24: ('24-bit integers', 3),
|
||||
RTAUDIO_SINT32: ('32-bit integers', 4, cnp.NPY_INT32),
|
||||
RTAUDIO_FLOAT32: ('32-bit floats', 4, cnp.NPY_FLOAT32),
|
||||
RTAUDIO_FLOAT64: ('64-bit floats', 8, cnp.NPY_FLOAT64),
|
||||
}
|
||||
|
||||
def get_numpy_dtype_from_format_string(format_string):
|
||||
return _formats_strkey[format_string][-1]
|
||||
def get_sampwidth_from_format_string(format_string):
|
||||
return _formats_strkey[format_string][-2]
|
||||
|
||||
|
||||
# It took me quite a long time to fully understand Cython's idiosyncrasies
|
||||
# concerning C(++) callbacks, the GIL and passing Python objects as pointers
|
||||
# into C(++) functions. But finally, here it is!
|
||||
|
||||
# cdef void fromNPYToBuffer(cnp.ndarray arr,
|
||||
# void* buf):
|
||||
# """
|
||||
# Copy a Python numpy array over to a buffer
|
||||
# No checks, just memcpy! Careful!
|
||||
# """
|
||||
# memcpy(buf, arr.data, arr.size*arr.itemsize)
|
||||
|
||||
|
||||
|
||||
ctypedef struct PyStreamData:
|
||||
PyObject* pyCallback
|
||||
RtAudioFormat sampleformat
|
||||
|
||||
# Flag used to pass the stopThread.
|
||||
atomic[bool] stopThread
|
||||
|
||||
# Number of frames per block
|
||||
unsigned nFramesPerBlock
|
||||
# Number of bytes per channel
|
||||
unsigned int nBytesPerChan
|
||||
|
||||
# Number of blocks to delay the output before adding to the input
|
||||
unsigned int outputDelayBlocks
|
||||
|
||||
# The structures as used by RtAudio
|
||||
cppRtAudio.StreamParameters inputParams
|
||||
cppRtAudio.StreamParameters outputParams
|
||||
|
||||
bool* inputChannelsEnabled
|
||||
bool* outputChannelsEnabled
|
||||
|
||||
unsigned ninputchannels_forwarded
|
||||
unsigned noutputchannels_forwarded
|
||||
|
||||
# If these queue pointers are NULL, it means the stream does not have an
|
||||
# input, or output.
|
||||
SafeQueue[void*] *outputDelayQueue
|
||||
SafeQueue[void*] *inputQueue
|
||||
SafeQueue[void*] *outputQueue
|
||||
CPPThread[void*, void (*)(void*)] *thread
|
||||
|
||||
|
||||
cdef int audioCallback(void* outputbuffer,
|
||||
void* inputbuffer,
|
||||
unsigned int nFramesPerBlock,
|
||||
double streamTime,
|
||||
RtAudioStreamStatus status,
|
||||
void* userData) nogil:
|
||||
"""
|
||||
Calls the Python callback function and converts data
|
||||
|
||||
"""
|
||||
cdef:
|
||||
int rval = 0
|
||||
PyStreamData* stream
|
||||
void* outputbuffercpy = NULL
|
||||
void* inputbuffercpy = NULL
|
||||
unsigned j, i
|
||||
unsigned bytesperchan
|
||||
unsigned noutputchannels_forwarded, ninputchannels_forwarded
|
||||
bint ch_en
|
||||
|
||||
stream = <PyStreamData*>(userData)
|
||||
bytesperchan = stream.nBytesPerChan
|
||||
ninputchannels_forwarded = stream.ninputchannels_forwarded
|
||||
noutputchannels_forwarded = stream.noutputchannels_forwarded
|
||||
|
||||
# with gil:
|
||||
# print(f'bytesperchan: {bytesperchan}')
|
||||
# print(f'ninputchannels_forwarded:: {ninputchannels_forwarded}')
|
||||
# print(f'noutputchannels_forwarded:: {noutputchannels_forwarded}')
|
||||
# fprintf(stderr, "Stream heartbeat...\n")
|
||||
|
||||
|
||||
# Returning 2 means aborting the stream immediately
|
||||
if status == RTAUDIO_INPUT_OVERFLOW:
|
||||
fprintf(stderr, 'Input overflow.\n')
|
||||
stream.stopThread.store(True)
|
||||
return 2
|
||||
elif status == RTAUDIO_OUTPUT_UNDERFLOW:
|
||||
fprintf(stderr, 'Output underflow.\n')
|
||||
# stream.stopThread.store(True)
|
||||
return 0
|
||||
|
||||
if nFramesPerBlock != stream.nFramesPerBlock:
|
||||
printf('Number of frames mismath in callback data!\n')
|
||||
stream.stopThread.store(True)
|
||||
return 2
|
||||
|
||||
if inputbuffer:
|
||||
# fprintf(stderr, "enter copying input buffer code\n")
|
||||
# with gil:
|
||||
# assert stream.inputQueue is not NULL
|
||||
inputbuffercpy = malloc(bytesperchan*ninputchannels_forwarded)
|
||||
if not inputbuffercpy:
|
||||
fprintf(stderr, "Error allocating buffer\n")
|
||||
return 2
|
||||
|
||||
if stream.outputDelayQueue:
|
||||
if stream.outputDelayQueue.size() > stream.outputDelayBlocks:
|
||||
outputbuffercpy = stream.outputDelayQueue.dequeue()
|
||||
memcpy(inputbuffercpy, outputbuffercpy,
|
||||
bytesperchan*noutputchannels_forwarded)
|
||||
|
||||
# Cleanup buffer
|
||||
free(outputbuffercpy)
|
||||
outputbuffercpy = NULL
|
||||
else:
|
||||
memset(inputbuffercpy, 0,
|
||||
bytesperchan*noutputchannels_forwarded)
|
||||
|
||||
# Starting channel for copying input channels according to the channel
|
||||
# map
|
||||
j = stream.noutputchannels_forwarded
|
||||
else:
|
||||
j = 0
|
||||
i = 0
|
||||
for i in range(stream.inputParams.nChannels):
|
||||
ch_en = stream.inputChannelsEnabled[i]
|
||||
if ch_en:
|
||||
copyChannel(inputbuffercpy, inputbuffer, bytesperchan, j, i)
|
||||
j+=1
|
||||
i+=1
|
||||
|
||||
stream.inputQueue.enqueue(inputbuffercpy)
|
||||
|
||||
if outputbuffer:
|
||||
# with gil:
|
||||
# assert stream.outputQueue
|
||||
# fprintf(stderr, "enter copying output buffer code\n")
|
||||
if stream.outputQueue.empty():
|
||||
fprintf(stderr, 'Stream output buffer underflow, zero-ing buffer...\n')
|
||||
memset(outputbuffer, 0, stream.outputParams.nChannels*bytesperchan)
|
||||
else:
|
||||
outputbuffercpy = stream.outputQueue.dequeue()
|
||||
# fprintf(stderr, 'Copying data to stream output buffer...\n')
|
||||
j = 0
|
||||
i = 0
|
||||
for i in range(stream.outputParams.nChannels):
|
||||
ch_en = stream.outputChannelsEnabled[i]
|
||||
if ch_en:
|
||||
copyChannel(outputbuffer, outputbuffercpy, bytesperchan, i, j)
|
||||
j+=1
|
||||
else:
|
||||
# If channel is not enabled, we set the data to zero
|
||||
memset(<void*> &((<char*> outputbuffer)[bytesperchan*i]), 0, bytesperchan)
|
||||
pass
|
||||
i+=1
|
||||
|
||||
if stream.outputDelayQueue:
|
||||
# fprintf(stderr, "Adding to delay queue\n")
|
||||
stream.outputDelayQueue.enqueue(outputbuffercpy)
|
||||
else:
|
||||
free(outputbuffercpy)
|
||||
|
||||
outputbuffercpy = NULL
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
cdef void audioCallbackPythonThreadFunction(void* voidstream) nogil:
|
||||
cdef:
|
||||
PyStreamData* stream
|
||||
cnp.NPY_TYPES npy_format
|
||||
void* inputbuffer = NULL
|
||||
void* outputbuffer = NULL
|
||||
unsigned noutputchannels_forwarded
|
||||
unsigned ninputchannels_forwarded
|
||||
unsigned nBytesPerChan
|
||||
unsigned nFramesPerBlock
|
||||
|
||||
stream = <PyStreamData*> voidstream
|
||||
ninputchannels_forwarded = stream.ninputchannels_forwarded
|
||||
noutputchannels_forwarded = stream.noutputchannels_forwarded
|
||||
nBytesPerChan = stream.nBytesPerChan
|
||||
nFramesPerBlock = stream.nFramesPerBlock
|
||||
|
||||
with gil:
|
||||
npy_format = _formats_rtkey[stream.sampleformat][2]
|
||||
callback = <object> stream.pyCallback
|
||||
print(f'noutputchannels_forwarded: {noutputchannels_forwarded}')
|
||||
print(f'ninputchannels_forwarded: {ninputchannels_forwarded}')
|
||||
print(f'nBytesPerChan: {nBytesPerChan}')
|
||||
print(f'nFramesPerBlock: {nFramesPerBlock}')
|
||||
|
||||
while True:
|
||||
if stream.stopThread.load() == True:
|
||||
printf('Stopping thread...\n')
|
||||
return
|
||||
|
||||
if stream.inputQueue:
|
||||
# fprintf(stderr, "Waiting on input queue\n")
|
||||
inputbuffer = stream.inputQueue.dequeue()
|
||||
if not inputbuffer:
|
||||
printf('Stopping thread...\n')
|
||||
return
|
||||
|
||||
if stream.outputQueue:
|
||||
# fprintf(stderr, 'Allocating output buffer...\n')
|
||||
outputbuffer = malloc(nBytesPerChan*noutputchannels_forwarded)
|
||||
# memset(outputbuffer, 0, nBytesPerChan*noutputchannels_forwarded)
|
||||
|
||||
with gil:
|
||||
|
||||
# Obtain stream information
|
||||
npy_input = None
|
||||
npy_output = None
|
||||
|
||||
if stream.inputQueue:
|
||||
# assert(inputbuffer)
|
||||
try:
|
||||
# print(f'========ninputchannels_forwarded: {ninputchannels_forwarded}')
|
||||
# print(f'========nFramesPerBlock: {nFramesPerBlock}')
|
||||
# print(f'========npy_format: {npy_format}')
|
||||
|
||||
npy_input = <object> data_to_ndarray(
|
||||
inputbuffer,
|
||||
nFramesPerBlock,
|
||||
ninputchannels_forwarded,
|
||||
npy_format,
|
||||
True, # Do transfer ownership
|
||||
True) # F-contiguous is True: data is Fortran-cont.
|
||||
# fprintf(stderr, "Copying array...\n")
|
||||
# fprintf(stderr, "End Copying array...\n")
|
||||
|
||||
except Exception as e:
|
||||
print('exception in cython callback for audio input: ', str(e))
|
||||
return
|
||||
|
||||
if stream.outputQueue:
|
||||
# fprintf(stderr, 'Copying output buffer to Numpy...\n')
|
||||
try:
|
||||
npy_output = <object> data_to_ndarray(
|
||||
outputbuffer,
|
||||
nFramesPerBlock,
|
||||
noutputchannels_forwarded,
|
||||
npy_format,
|
||||
False, # Do not transfer ownership
|
||||
True) # F-contiguous
|
||||
|
||||
except Exception as e:
|
||||
print('exception in Cython callback for audio output: ', str(e))
|
||||
return
|
||||
|
||||
try:
|
||||
# fprintf(stderr, "Python callback...\n")
|
||||
rval = callback(npy_input,
|
||||
npy_output,
|
||||
nFramesPerBlock,
|
||||
)
|
||||
# fprintf(stderr, "Return from Python callback...\n")
|
||||
|
||||
except Exception as e:
|
||||
print('Exception in Cython callback: ', str(e))
|
||||
return
|
||||
|
||||
if stream.outputQueue:
|
||||
# fprintf(stderr, 'Enqueuing output buffer...\n')
|
||||
|
||||
stream.outputQueue.enqueue(outputbuffer)
|
||||
if not stream.inputQueue:
|
||||
# fprintf(stderr, 'No input queue!\n')
|
||||
while stream.outputQueue.size() > 10 and not stream.stopThread.load():
|
||||
# printf('Sleeping...\n')
|
||||
# No input queue to wait on, so we relax a bit here.
|
||||
CPPsleep_ms(1);
|
||||
|
||||
# Outputbuffer is free'ed by the audiothread, so should not be touched
|
||||
# here.
|
||||
outputbuffer = NULL
|
||||
|
||||
# Inputbuffer memory is owned by Numpy, so should not be free'ed
|
||||
inputbuffer = NULL
|
||||
|
||||
|
||||
cdef void errorCallback(RtAudioError.Type _type,const string& errortxt) nogil:
|
||||
fprintf(stderr, 'RtAudio error callback called: ')
|
||||
fprintf(stderr, errortxt.c_str())
|
||||
fprintf(stderr, '\n')
|
||||
|
||||
|
||||
cdef class RtAudio:
|
||||
cdef:
|
||||
cppRtAudio* _rtaudio
|
||||
PyStreamData* sd
|
||||
int api
|
||||
|
||||
def __cinit__(self, pyapi):
|
||||
if pyapi.apiname != 'RtAudio':
|
||||
raise RuntimeError('RtAudio constructor called with invalid Api instance')
|
||||
cdef:
|
||||
cppRtAudio.Api api = <cppRtAudio.Api> pyapi.internalnr
|
||||
|
||||
self._rtaudio = new cppRtAudio(api)
|
||||
self.sd = NULL
|
||||
self._rtaudio.showWarnings(True)
|
||||
self.api = api
|
||||
|
||||
def __dealloc__(self):
|
||||
if self.sd is not NULL:
|
||||
# fprintf(stderr, 'Force closing stream...')
|
||||
if self._rtaudio.isStreamRunning():
|
||||
self._rtaudio.stopStream()
|
||||
self._rtaudio.closeStream()
|
||||
|
||||
self.cleanupStream(self.sd)
|
||||
self.sd = NULL
|
||||
del self._rtaudio
|
||||
|
||||
@staticmethod
|
||||
def getApis():
|
||||
cdef:
|
||||
vector[cppRtAudio.Api] apis
|
||||
cppRtAudio.getCompiledApi(apis)
|
||||
|
||||
apilist = []
|
||||
for api in apis:
|
||||
apilist.append(
|
||||
DAQApi(
|
||||
backendname= 'RtAudio',
|
||||
apiname = cppRtAudio.getApiName(api).decode('utf-8'),
|
||||
internalnr=<int> api))
|
||||
|
||||
return apilist
|
||||
|
||||
cpdef unsigned int getDefaultOutputDevice(self):
|
||||
return self._rtaudio.getDefaultOutputDevice()
|
||||
|
||||
cpdef unsigned int getDefaultInputDevice(self):
|
||||
return self._rtaudio.getDefaultInputDevice()
|
||||
|
||||
def getDeviceInfo(self):
|
||||
"""
|
||||
Return device information of the current device
|
||||
"""
|
||||
sampleformats = []
|
||||
|
||||
cdef:
|
||||
cppRtAudio.DeviceInfo devinfo
|
||||
unsigned devcount, i
|
||||
|
||||
devinfo_py = []
|
||||
|
||||
devcount = self._rtaudio.getDeviceCount()
|
||||
|
||||
for i in range(devcount):
|
||||
|
||||
devinfo = self._rtaudio.getDeviceInfo(i)
|
||||
|
||||
nf = devinfo.nativeFormats
|
||||
for format_ in [ RTAUDIO_SINT8, RTAUDIO_SINT16, RTAUDIO_SINT24,
|
||||
RTAUDIO_SINT32, RTAUDIO_FLOAT32, RTAUDIO_FLOAT64]:
|
||||
if nf & format_:
|
||||
sampleformats.append(_formats_rtkey[format_][0])
|
||||
|
||||
devinfo_py.append(DeviceInfo(
|
||||
api = self.api,
|
||||
index = i,
|
||||
probed = devinfo.probed,
|
||||
name = devinfo.name.decode('utf-8'),
|
||||
outputchannels = devinfo.outputChannels,
|
||||
inputchannels = devinfo.inputChannels,
|
||||
duplexchannels = devinfo.duplexChannels,
|
||||
samplerates = devinfo.sampleRates,
|
||||
sampleformats = sampleformats,
|
||||
prefsamplerate = devinfo.preferredSampleRate))
|
||||
|
||||
return devinfo_py
|
||||
|
||||
@cython.nonecheck(True)
|
||||
def start(self,
|
||||
avstream
|
||||
):
|
||||
"""
|
||||
Opening a stream with specified parameters
|
||||
|
||||
Args:
|
||||
avstream: AvStream instance
|
||||
|
||||
Returns: None
|
||||
"""
|
||||
|
||||
if self.sd is not NULL:
|
||||
raise RuntimeError('Stream is already opened.')
|
||||
|
||||
daqconfig = avstream.daqconfig
|
||||
avtype = avstream.avtype
|
||||
device = avstream.device
|
||||
|
||||
cdef:
|
||||
bint duplex_mode = daqconfig.duplex_mode
|
||||
bint monitorOutput = daqconfig.monitor_gen
|
||||
unsigned int outputDelayBlocks = daqconfig.outputDelayBlocks
|
||||
cppRtAudio.StreamParameters *rtOutputParams_ptr = NULL
|
||||
cppRtAudio.StreamParameters *rtInputParams_ptr = NULL
|
||||
cppRtAudio.StreamOptions streamoptions
|
||||
size_t sw
|
||||
unsigned int nFramesPerBlock = int(daqconfig.nFramesPerBlock)
|
||||
int firstinputchannel, firstoutputchannel
|
||||
int lastinputchannel, lastoutputchannel
|
||||
unsigned int ninputchannels_forwarded=0
|
||||
unsigned int ninputchannels_rtaudio=0
|
||||
unsigned int noutputchannels_rtaudio=0
|
||||
unsigned int noutputchannels_forwarded=0
|
||||
unsigned int samplerate
|
||||
int i
|
||||
bint input_stream=False, output_stream=False
|
||||
bool* inputChannelsEnabled
|
||||
bool* outputChannelsEnabled
|
||||
|
||||
if daqconfig.nFramesPerBlock > 8192 or daqconfig.nFramesPerBlock < 512:
|
||||
raise ValueError('Invalid number of nFramesPerBlock')
|
||||
|
||||
if daqconfig.outputDelayBlocks < 0 or daqconfig.outputDelayBlocks > 10:
|
||||
raise ValueError('Invalid number of outputDelayBlocks')
|
||||
|
||||
try:
|
||||
|
||||
# Determine sample rate and sample format, determine whether we are an
|
||||
# input or an output stream, or both
|
||||
if avtype == AvType.audio_input or duplex_mode:
|
||||
# Here, we override the sample format in case of duplex mode.
|
||||
sampleformat = daqconfig.en_input_sample_format
|
||||
samplerate = int(daqconfig.en_input_rate)
|
||||
input_stream = True
|
||||
if duplex_mode:
|
||||
output_stream = True
|
||||
else:
|
||||
sampleformat = daqconfig.en_output_sample_format
|
||||
samplerate = int(daqconfig.en_output_rate)
|
||||
output_stream = True
|
||||
|
||||
print(f'Is input stream: {input_stream}')
|
||||
print(f'Is output stream: {output_stream}')
|
||||
|
||||
sw = get_sampwidth_from_format_string(sampleformat)
|
||||
print(f'samplewidth: {sw}')
|
||||
|
||||
# All set, allocate the stream!
|
||||
self.sd = <PyStreamData*> malloc(sizeof(PyStreamData))
|
||||
if self.sd == NULL:
|
||||
raise MemoryError('Could not allocate stream: memory error.')
|
||||
|
||||
self.sd.pyCallback = <PyObject*> avstream._audioCallback
|
||||
# Increase reference count to the callback
|
||||
Py_INCREF(<object> avstream._audioCallback)
|
||||
|
||||
self.sd.sampleformat = _formats_strkey[sampleformat][0]
|
||||
self.sd.stopThread.store(False)
|
||||
self.sd.inputQueue = NULL
|
||||
self.sd.outputQueue = NULL
|
||||
self.sd.outputDelayQueue = NULL
|
||||
|
||||
self.sd.thread = NULL
|
||||
|
||||
self.sd.outputDelayBlocks = outputDelayBlocks
|
||||
self.sd.ninputchannels_forwarded = 0
|
||||
self.sd.noutputchannels_forwarded = 0
|
||||
self.sd.inputChannelsEnabled = NULL
|
||||
self.sd.outputChannelsEnabled = NULL
|
||||
|
||||
# Create channel maps for input channels, set RtAudio input stream
|
||||
# parameters
|
||||
if input_stream:
|
||||
firstinputchannel = daqconfig.firstEnabledInputChannelNumber()
|
||||
lastinputchannel = daqconfig.lastEnabledInputChannelNumber()
|
||||
ninputchannels_rtaudio = lastinputchannel-firstinputchannel+1
|
||||
|
||||
# print(firstinputchannel)
|
||||
# print(lastinputchannel)
|
||||
# print(ninputchannels_rtaudio)
|
||||
|
||||
if lastinputchannel < 0 or ninputchannels_rtaudio < 1:
|
||||
raise ValueError('Not enough input channels selected')
|
||||
input_ch = daqconfig.input_channel_configs
|
||||
|
||||
inputChannelsEnabled = <bool*> malloc(sizeof(bool)*ninputchannels_rtaudio)
|
||||
self.sd.inputChannelsEnabled = inputChannelsEnabled
|
||||
|
||||
for i in range(firstinputchannel, lastinputchannel+1):
|
||||
ch_en = input_ch[i].channel_enabled
|
||||
if ch_en:
|
||||
ninputchannels_forwarded += 1
|
||||
inputChannelsEnabled[i] = ch_en
|
||||
|
||||
rtInputParams_ptr = &self.sd.inputParams
|
||||
rtInputParams_ptr.deviceId = device.index
|
||||
rtInputParams_ptr.nChannels = ninputchannels_rtaudio
|
||||
rtInputParams_ptr.firstChannel = firstinputchannel
|
||||
|
||||
self.sd.inputQueue = new SafeQueue[void*]()
|
||||
self.sd.ninputchannels_forwarded = ninputchannels_forwarded
|
||||
|
||||
|
||||
# Create channel maps for output channels
|
||||
if output_stream:
|
||||
firstoutputchannel = daqconfig.firstEnabledOutputChannelNumber()
|
||||
lastoutputchannel = daqconfig.lastEnabledOutputChannelNumber()
|
||||
noutputchannels_rtaudio = lastoutputchannel-firstoutputchannel+1
|
||||
|
||||
# print(firstoutputchannel)
|
||||
# print(lastoutputchannel)
|
||||
# print(noutputchannels_rtaudio)
|
||||
|
||||
if lastoutputchannel < 0 or noutputchannels_rtaudio < 1:
|
||||
raise ValueError('Not enough output channels selected')
|
||||
output_ch = daqconfig.output_channel_configs
|
||||
|
||||
outputChannelsEnabled = <bool*> malloc(sizeof(bool)*noutputchannels_rtaudio)
|
||||
self.sd.outputChannelsEnabled = outputChannelsEnabled
|
||||
for i in range(firstoutputchannel, lastoutputchannel+1):
|
||||
ch_en = output_ch[i].channel_enabled
|
||||
if ch_en:
|
||||
noutputchannels_forwarded += 1
|
||||
outputChannelsEnabled[i] = ch_en
|
||||
|
||||
rtOutputParams_ptr = &self.sd.outputParams
|
||||
rtOutputParams_ptr.deviceId = device.index
|
||||
rtOutputParams_ptr.nChannels = noutputchannels_rtaudio
|
||||
rtOutputParams_ptr.firstChannel = firstoutputchannel
|
||||
|
||||
self.sd.outputQueue = new SafeQueue[void*]()
|
||||
self.sd.noutputchannels_forwarded = noutputchannels_forwarded
|
||||
|
||||
if monitorOutput and duplex_mode:
|
||||
self.sd.outputDelayQueue = new SafeQueue[void*]()
|
||||
self.sd.ninputchannels_forwarded += noutputchannels_forwarded
|
||||
|
||||
|
||||
streamoptions.flags = RTAUDIO_HOG_DEVICE
|
||||
streamoptions.flags |= RTAUDIO_NONINTERLEAVED
|
||||
streamoptions.numberOfBuffers = 4
|
||||
streamoptions.streamName = "LASP Audio stream".encode('utf-8')
|
||||
streamoptions.priority = 1
|
||||
|
||||
self._rtaudio.openStream(rtOutputParams_ptr,
|
||||
rtInputParams_ptr,
|
||||
_formats_strkey[sampleformat][0],
|
||||
samplerate,
|
||||
&nFramesPerBlock,
|
||||
audioCallback,
|
||||
<void*> self.sd,
|
||||
&streamoptions, # Stream options
|
||||
errorCallback # Error callback
|
||||
)
|
||||
|
||||
self.sd.nBytesPerChan = nFramesPerBlock*sw
|
||||
self.sd.nFramesPerBlock = nFramesPerBlock
|
||||
|
||||
self._rtaudio.startStream()
|
||||
|
||||
except Exception as e:
|
||||
print('Exception occured in stream opening: ', e)
|
||||
self.cleanupStream(self.sd)
|
||||
self.sd = NULL
|
||||
raise e
|
||||
|
||||
with nogil:
|
||||
self.sd.thread = new CPPThread[void*, void (*)(void*)](audioCallbackPythonThreadFunction,
|
||||
<void*> self.sd)
|
||||
# Allow it to start
|
||||
CPPsleep_ms(500)
|
||||
pass
|
||||
|
||||
return nFramesPerBlock, samplerate
|
||||
|
||||
def stop(self):
|
||||
if self.sd is NULL:
|
||||
raise RuntimeError('Stream is not running')
|
||||
|
||||
try:
|
||||
self._rtaudio.stopStream()
|
||||
self._rtaudio.closeStream()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
pass
|
||||
self.cleanupStream(self.sd)
|
||||
self.sd = NULL
|
||||
|
||||
cdef cleanupStream(self, PyStreamData* stream):
|
||||
if stream == NULL:
|
||||
return
|
||||
|
||||
with nogil:
|
||||
if stream.thread:
|
||||
stream.stopThread.store(True)
|
||||
if stream.inputQueue:
|
||||
# If waiting in the input queue, hereby we let it run.
|
||||
stream.inputQueue.enqueue(NULL)
|
||||
# printf('Joining thread...\n')
|
||||
# HERE WE SHOULD RELEASE THE GIL, as exiting the thread function
|
||||
# will require the GIL, which is locked by this thread!
|
||||
stream.thread.join()
|
||||
# printf('Thread joined!\n')
|
||||
del stream.thread
|
||||
stream.thread = NULL
|
||||
|
||||
if stream.inputChannelsEnabled:
|
||||
free(stream.inputChannelsEnabled)
|
||||
if stream.outputChannelsEnabled:
|
||||
free(stream.outputChannelsEnabled)
|
||||
|
||||
if stream.outputQueue:
|
||||
while not stream.outputQueue.empty():
|
||||
free(stream.outputQueue.dequeue())
|
||||
del stream.outputQueue
|
||||
if stream.inputQueue:
|
||||
while not stream.inputQueue.empty():
|
||||
free(stream.inputQueue.dequeue())
|
||||
del stream.inputQueue
|
||||
if stream.outputDelayQueue:
|
||||
while not stream.outputDelayQueue.empty():
|
||||
free(stream.outputDelayQueue.dequeue())
|
||||
del stream.outputDelayQueue
|
||||
fprintf(stderr, "End cleanup stream queues...\n")
|
||||
|
||||
if stream.pyCallback:
|
||||
Py_DECREF(<object> stream.pyCallback)
|
||||
stream.pyCallback = NULL
|
||||
# fprintf(stderr, "End cleanup callback...\n")
|
||||
free(stream)
|
@ -1,625 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Author: J.A. de Jong
|
||||
|
||||
Description: Controlling an audio stream in a different process.
|
||||
"""
|
||||
import logging
|
||||
import multiprocessing as mp
|
||||
# import cv2 as cv
|
||||
import signal
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum, auto, unique
|
||||
from typing import List
|
||||
|
||||
import numpy as np
|
||||
|
||||
from .device import Daq, DaqChannel, DaqConfiguration, DeviceInfo
|
||||
from .filter import highpass
|
||||
from .lasp_atomic import Atomic
|
||||
from .lasp_common import AvType
|
||||
from .lasp_multiprocessingpatch import apply_patch
|
||||
from .wrappers import SosFilterBank
|
||||
|
||||
apply_patch()
|
||||
|
||||
__all__ = ["StreamManager", "ignoreSigInt", "StreamStatus"]
|
||||
|
||||
|
||||
def ignoreSigInt():
|
||||
"""
|
||||
Ignore sigint signal. Should be set on all processes to let the main
|
||||
process control this signal.
|
||||
"""
|
||||
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||
|
||||
|
||||
@dataclass
|
||||
class StreamMetaData:
|
||||
# Sample rate [Hz]
|
||||
fs: float
|
||||
|
||||
# Input channels
|
||||
in_ch: List[DaqChannel]
|
||||
|
||||
# Output channels
|
||||
out_ch: List[DaqChannel]
|
||||
|
||||
# blocksize
|
||||
blocksize: int
|
||||
|
||||
# The data type of input and output blocks.
|
||||
dtype: np.dtype
|
||||
|
||||
|
||||
@unique
|
||||
class StreamMsg(Enum):
|
||||
"""
|
||||
First part, control messages that can be send to the stream
|
||||
"""
|
||||
|
||||
startStream = auto()
|
||||
stopStream = auto()
|
||||
stopAllStreams = auto()
|
||||
getStreamMetaData = auto()
|
||||
endProcess = auto()
|
||||
scanDaqDevices = auto()
|
||||
"""
|
||||
Second part, status messages that are send back on all listeners
|
||||
"""
|
||||
# "Normal messages"
|
||||
deviceList = auto()
|
||||
streamStarted = auto()
|
||||
streamStopped = auto()
|
||||
streamMetaData = auto()
|
||||
streamData = auto()
|
||||
|
||||
# Error messages
|
||||
# Some error occured, which mostly leads to a stop of the stream
|
||||
streamError = auto()
|
||||
# An error occured, but we recovered
|
||||
streamTemporaryError = auto()
|
||||
# A fatal error occured. This leads to serious errors in the application
|
||||
streamFatalError = auto()
|
||||
|
||||
|
||||
class AudioStream:
|
||||
"""
|
||||
Audio stream.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
avtype: AvType,
|
||||
devices: list,
|
||||
daqconfig: DaqConfiguration,
|
||||
processCallback: callable,
|
||||
):
|
||||
"""
|
||||
Initializes the audio stream and tries to start it.
|
||||
|
||||
avtype: AvType
|
||||
devices: List of device information
|
||||
daqconfig: DaqConfiguration to used to generate audio stream backend
|
||||
processCallback: callback function that will be called from a different
|
||||
thread, with arguments (AudioStream, in
|
||||
"""
|
||||
logging.debug("AudioStream()")
|
||||
|
||||
# self.running = Atomic(False)
|
||||
# self.aframectr = Atomic(0)
|
||||
self.running = False
|
||||
self.aframectr = 0
|
||||
self.avtype = avtype
|
||||
|
||||
api_devices = devices[daqconfig.api]
|
||||
self.processCallback = processCallback
|
||||
|
||||
matching_devices = [
|
||||
device for device in api_devices if device.name == daqconfig.device_name
|
||||
]
|
||||
|
||||
if len(matching_devices) == 0:
|
||||
raise RuntimeError(f"Could not find device {daqconfig.device_name}")
|
||||
|
||||
# TODO: We pick te first one, what to do if we have multiple matches?
|
||||
# Is that even possible?
|
||||
device = matching_devices[0]
|
||||
|
||||
self.daq = Daq(device, daqconfig)
|
||||
en_in_ch = daqconfig.getEnabledInChannels(include_monitor=True)
|
||||
en_out_ch = daqconfig.getEnabledOutChannels()
|
||||
|
||||
if en_in_ch == 0 and en_out_ch == 0:
|
||||
raise RuntimeError("No enabled input / output channels")
|
||||
elif en_out_ch == 0 and avtype in (AvType.audio_duplex, AvType.audio_output):
|
||||
raise RuntimeError("No enabled output channels")
|
||||
elif en_in_ch == 0 and avtype in (AvType.audio_input, AvType.audio_duplex):
|
||||
raise RuntimeError("No enabled input channels")
|
||||
|
||||
logging.debug("Ready to start device...")
|
||||
samplerate = self.daq.start(self.streamCallback)
|
||||
|
||||
# Create required Highpass filters for incoming data
|
||||
self.hpfs = [None] * len(en_in_ch)
|
||||
for i, ch in enumerate(en_in_ch):
|
||||
# Simple filter with a single bank and one section
|
||||
if ch.highpass > 0:
|
||||
fb = SosFilterBank(1, 1)
|
||||
hpf = highpass(samplerate, ch.highpass, Q=np.sqrt(2) / 2)
|
||||
fb.setFilter(0, hpf[None, :])
|
||||
self.hpfs[i] = fb
|
||||
|
||||
self.streammetadata = StreamMetaData(
|
||||
fs=samplerate,
|
||||
in_ch=en_in_ch,
|
||||
out_ch=en_out_ch,
|
||||
blocksize=self.daq.nFramesPerBlock,
|
||||
dtype=self.daq.getNumpyDataType(),
|
||||
)
|
||||
self.running = True
|
||||
|
||||
def streamCallback(self, indata, outdata, nframes):
|
||||
"""
|
||||
This is called (from a separate thread) for each block
|
||||
of audio data.
|
||||
"""
|
||||
if not self.running:
|
||||
return 1
|
||||
self.aframectr += 1
|
||||
|
||||
# TODO: Fix this. This gives bug on Windows, the threading lock does
|
||||
# give a strange erro.
|
||||
try:
|
||||
if not self.running:
|
||||
return 1
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
if indata is not None:
|
||||
indata_filtered = np.empty_like(indata)
|
||||
nchannels = indata.shape[1]
|
||||
|
||||
for i in range(nchannels):
|
||||
# Filter each channel to the optional high-pass, which could also
|
||||
# be an empty filter
|
||||
if self.hpfs[i] is not None:
|
||||
indata_float = indata[:, [i]].astype(np.float)
|
||||
filtered_ch_float = self.hpfs[i].filter_(indata_float)
|
||||
|
||||
indata_filtered[:, i] = filtered_ch_float.astype(
|
||||
self.streammetadata.dtype
|
||||
)[:, 0]
|
||||
else:
|
||||
# One-to-one copy
|
||||
indata_filtered[:, i] = indata[:, i]
|
||||
else:
|
||||
indata_filtered = indata
|
||||
|
||||
# rv = self.processCallback(self, indata, outdata)
|
||||
rv = self.processCallback(self, indata_filtered, outdata)
|
||||
if rv != 0:
|
||||
self.running <<= False
|
||||
return rv
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
Stop the DAQ stream. Should be called only once.
|
||||
"""
|
||||
daq = self.daq
|
||||
self.daq = None
|
||||
self.running <<= False
|
||||
daq.stop()
|
||||
|
||||
self.streammetadata = None
|
||||
|
||||
|
||||
class AvStreamProcess(mp.Process):
|
||||
"""
|
||||
Different process on which all audio streams are running.
|
||||
"""
|
||||
|
||||
def __init__(self, pipe, msg_qlist, indata_qlist, outq):
|
||||
"""
|
||||
|
||||
Args:
|
||||
pipe: Message control pipe on which commands are received.
|
||||
msg_qlist: List of queues on which stream status and events are
|
||||
sent. Here, everything is send, except for the captured data
|
||||
itself.
|
||||
indata_qlist: List of queues on which captured data from a DAQ is
|
||||
send. This one gets all events, but also captured data.
|
||||
outq: On this queue, the stream process receives data to be send as
|
||||
output to the devices.
|
||||
|
||||
"""
|
||||
super().__init__()
|
||||
|
||||
self.pipe = pipe
|
||||
self.msg_qlist = msg_qlist
|
||||
self.indata_qlist = indata_qlist
|
||||
self.outq = outq
|
||||
|
||||
self.devices = {}
|
||||
self.daqconfigs = None
|
||||
|
||||
# In, out, duplex
|
||||
self.streams = {t: None for t in list(AvType)}
|
||||
|
||||
# When this is set, a kill on the main process will also kill the
|
||||
# siggen process. Highly wanted feature
|
||||
self.daemon = True
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
The actual function running in a different process.
|
||||
"""
|
||||
# First things first, ignore interrupt signals for THIS process
|
||||
# https://stackoverflow.com/questions/21104997/keyboard-interrupt-with-pythons-multiprocessing
|
||||
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||
|
||||
# Check for devices
|
||||
self.rescanDaqDevices()
|
||||
|
||||
while True:
|
||||
try:
|
||||
msg, data = self.pipe.recv()
|
||||
except OSError:
|
||||
logging.error("Error with pipe, terminating process")
|
||||
self.stopAllStreams()
|
||||
self.terminate()
|
||||
logging.debug(f"Streamprocess obtained message {msg}")
|
||||
|
||||
if msg == StreamMsg.scanDaqDevices:
|
||||
self.rescanDaqDevices()
|
||||
|
||||
elif msg == StreamMsg.stopAllStreams:
|
||||
self.stopAllStreams()
|
||||
|
||||
elif msg == StreamMsg.endProcess:
|
||||
self.stopAllStreams()
|
||||
# and.. exit!
|
||||
return
|
||||
|
||||
elif msg == StreamMsg.getStreamMetaData:
|
||||
(avtype, ) = data
|
||||
duplex = self.streams[AvType.audio_duplex]
|
||||
stream = duplex if duplex else self.streams[avtype]
|
||||
|
||||
self.sendAllQueues(
|
||||
StreamMsg.streamMetaData, avtype, stream.streammetadata
|
||||
)
|
||||
|
||||
elif msg == StreamMsg.startStream:
|
||||
avtype, daqconfig = data
|
||||
self.startStream(avtype, daqconfig)
|
||||
|
||||
elif msg == StreamMsg.stopStream:
|
||||
(avtype,) = data
|
||||
self.stopStream(avtype)
|
||||
|
||||
def startStream(self, avtype: AvType, daqconfig: DaqConfiguration):
|
||||
"""
|
||||
Start a stream, based on type and configuration
|
||||
|
||||
"""
|
||||
self.stopRequiredExistingStreams(avtype)
|
||||
# Empty the queue from existing stuff (puts the signal generator
|
||||
# directly in action!).
|
||||
if avtype in (AvType.audio_duplex, AvType.audio_output):
|
||||
while not self.outq.empty():
|
||||
self.outq.get()
|
||||
try:
|
||||
stream = AudioStream(avtype, self.devices, daqconfig, self.streamCallback)
|
||||
self.streams[avtype] = stream
|
||||
self.sendAllQueues(StreamMsg.streamStarted, avtype, stream.streammetadata)
|
||||
|
||||
except Exception as e:
|
||||
self.sendAllQueues(
|
||||
StreamMsg.streamError, avtype, f"Error starting stream: {str(e)}"
|
||||
)
|
||||
return
|
||||
|
||||
def stopStream(self, avtype: AvType):
|
||||
"""
|
||||
Stop an existing stream, and sets the attribute in the list of streams
|
||||
to None
|
||||
|
||||
Args:
|
||||
stream: AudioStream instance
|
||||
"""
|
||||
stream = self.streams[avtype]
|
||||
if stream is not None:
|
||||
try:
|
||||
stream.stop()
|
||||
self.sendAllQueues(StreamMsg.streamStopped, stream.avtype)
|
||||
except Exception as e:
|
||||
self.sendAllQueues(
|
||||
StreamMsg.streamError,
|
||||
stream.avtype,
|
||||
f"Error occured in stopping stream: {str(e)}",
|
||||
)
|
||||
self.streams[avtype] = None
|
||||
|
||||
def stopRequiredExistingStreams(self, avtype: AvType):
|
||||
"""
|
||||
Stop all existing streams that conflict with the current avtype
|
||||
"""
|
||||
if avtype == AvType.audio_input:
|
||||
# For a new input, duplex and input needs to be stopped
|
||||
stream_to_stop = (AvType.audio_input, AvType.audio_duplex)
|
||||
elif avtype == AvType.audio_output:
|
||||
# For a new output, duplex and output needs to be stopped
|
||||
stream_to_stop = (AvType.audio_output, AvType.audio_duplex)
|
||||
elif avtype == AvType.audio_duplex:
|
||||
# All others have to stop
|
||||
stream_to_stop = list(AvType) # All of them
|
||||
else:
|
||||
raise ValueError("BUG")
|
||||
|
||||
for stream in stream_to_stop:
|
||||
if stream is not None:
|
||||
self.stopStream(stream)
|
||||
|
||||
def stopAllStreams(self):
|
||||
"""
|
||||
Stops all streams
|
||||
"""
|
||||
for key in self.streams.keys():
|
||||
self.stopStream(key)
|
||||
|
||||
def isStreamRunning(self, avtype: AvType = None):
|
||||
"""
|
||||
Check whether a stream is running
|
||||
|
||||
Args:
|
||||
avtype: The stream type to check whether it is still running. If
|
||||
None, it checks all streams.
|
||||
|
||||
Returns:
|
||||
True if a stream is running, otherwise false
|
||||
"""
|
||||
if avtype is None:
|
||||
avtype = list(AvType)
|
||||
else:
|
||||
avtype = (avtype,)
|
||||
for t in avtype:
|
||||
if self.streams[t] is not None and self.streams[t].running():
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def rescanDaqDevices(self):
|
||||
"""
|
||||
Rescan the available DaQ devices.
|
||||
|
||||
"""
|
||||
if self.isStreamRunning():
|
||||
self.sendAllQueues(
|
||||
StreamMsg.streamError,
|
||||
None,
|
||||
"A stream is running, cannot rescan DAQ devices.",
|
||||
)
|
||||
return
|
||||
|
||||
self.devices = Daq.getDeviceInfo()
|
||||
self.sendAllQueues(StreamMsg.deviceList, self.devices)
|
||||
|
||||
def streamCallback(self, audiostream, indata, outdata):
|
||||
"""This is called (from a separate thread) for each audio block."""
|
||||
# logging.debug('streamCallback()')
|
||||
if outdata is not None:
|
||||
if not self.outq.empty():
|
||||
newdata = self.outq.get()
|
||||
if newdata.shape[0] != outdata.shape[0] or newdata.ndim != 1:
|
||||
msgtxt = "Invalid output data obtained from queue"
|
||||
logging.fatal(msgtxt)
|
||||
self.sendAllQueues(
|
||||
StreamMsg.streamFatalError, audiostream.avtype, msgtxt
|
||||
)
|
||||
return 1
|
||||
outdata[:, :] = newdata[:, None]
|
||||
else:
|
||||
msgtxt = "Signal generator buffer underflow. Signal generator cannot keep up with data generation."
|
||||
# logging.error(msgtxt)
|
||||
self.sendAllQueues(
|
||||
StreamMsg.streamTemporaryError, audiostream.avtype, msgtxt
|
||||
)
|
||||
outdata[:, :] = 0
|
||||
|
||||
if indata is not None:
|
||||
self.sendInQueues(StreamMsg.streamData, indata)
|
||||
|
||||
return 0
|
||||
|
||||
# Wrapper functions that safe some typing, they do not require an
|
||||
# explanation.
|
||||
def sendInQueues(self, msg, *data):
|
||||
# logging.debug('sendInQueues()')
|
||||
try:
|
||||
for q in self.indata_qlist:
|
||||
# Fan out the input data to all queues in the queue list
|
||||
q.put((msg, data))
|
||||
except ValueError:
|
||||
logging.error("Error with data queue, terminating process")
|
||||
self.stopAllStreams()
|
||||
self.terminate()
|
||||
|
||||
def sendAllQueues(self, msg, *data):
|
||||
"""
|
||||
Destined for all queues, including capture data queues
|
||||
"""
|
||||
self.sendInQueues(msg, *data)
|
||||
try:
|
||||
for q in self.msg_qlist:
|
||||
# Fan out the input data to all queues in the queue list
|
||||
q.put((msg, data))
|
||||
except ValueError:
|
||||
logging.error("Error with data queue, terminating process")
|
||||
self.stopAllStreams()
|
||||
self.terminate()
|
||||
|
||||
|
||||
@dataclass
|
||||
class StreamStatus:
|
||||
lastStatus: StreamMsg = StreamMsg.streamStopped
|
||||
errorTxt: str = None
|
||||
streammetadata: StreamMetaData = None
|
||||
|
||||
|
||||
class StreamManager:
|
||||
"""
|
||||
Audio and video data stream manager, to which queus can be added
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""Open a stream for audio in/output and video input. For audio output,"""
|
||||
|
||||
# Initialize streamstatus
|
||||
self.streamstatus = {t: StreamStatus() for t in list(AvType)}
|
||||
|
||||
self.devices = None
|
||||
|
||||
# Multiprocessing manager, pipe, output queue, input queue,
|
||||
self.manager = mp.managers.SyncManager()
|
||||
|
||||
# Start this manager and ignore interrupts
|
||||
# https://stackoverflow.com/questions/21104997/keyboard-interrupt-with-pythons-multiprocessing
|
||||
self.manager.start(ignoreSigInt)
|
||||
|
||||
# List of queues for all entities that require 'microphone' or input
|
||||
# data. We need a local list, to manage listener queues, as the queues
|
||||
# which are in the manager list get a new object id. The local list is
|
||||
# used to find the index in the manager queues list upon deletion by
|
||||
# 'removeListener()'
|
||||
self.indata_qlist = self.manager.list([])
|
||||
self.indata_qlist_local = []
|
||||
|
||||
self.msg_qlist = self.manager.list([])
|
||||
self.msg_qlist_local = []
|
||||
|
||||
# Queue used for signal generator data
|
||||
self.outq = self.manager.Queue()
|
||||
|
||||
# Messaging pipe
|
||||
self.pipe, child_pipe = mp.Pipe(duplex=True)
|
||||
|
||||
# This is the queue on which this class listens for stream process
|
||||
# messages.
|
||||
self.our_msgqueue = self.addMsgQueueListener()
|
||||
|
||||
# Create the stream process
|
||||
self.streamProcess = AvStreamProcess(
|
||||
child_pipe, self.msg_qlist, self.indata_qlist, self.outq
|
||||
)
|
||||
self.streamProcess.start()
|
||||
|
||||
def scanDaqDevices(self):
|
||||
"""
|
||||
Output the message to the stream process to rescan the list of devices
|
||||
"""
|
||||
self.sendPipe(StreamMsg.scanDaqDevices, None)
|
||||
|
||||
def getStreamStatus(self, avtype: AvType):
|
||||
"""
|
||||
Sends a request for the stream status over the pipe, for given AvType
|
||||
"""
|
||||
self.sendPipe(StreamMsg.getStreamMetaData, avtype)
|
||||
|
||||
def getOutputQueue(self):
|
||||
"""
|
||||
Returns the output queue object.
|
||||
|
||||
Note, should (of course) only be used by one signal generator at the time!
|
||||
"""
|
||||
return self.outq
|
||||
|
||||
def addMsgQueueListener(self):
|
||||
"""
|
||||
Add a listener queue to the list of message queues, and return the
|
||||
queue.
|
||||
|
||||
Returns:
|
||||
listener queue
|
||||
"""
|
||||
newqueue = self.manager.Queue()
|
||||
self.msg_qlist.append(newqueue)
|
||||
self.msg_qlist_local.append(newqueue)
|
||||
return newqueue
|
||||
|
||||
def removeMsgQueueListener(self, queue):
|
||||
"""
|
||||
Remove an input listener queue from the message queue list.
|
||||
"""
|
||||
# Uses a local queue list to find the index, based on the queue
|
||||
idx = self.msg_qlist_local.index(queue)
|
||||
del self.msg_qlist_local[idx]
|
||||
del self.msg_qlist[idx]
|
||||
|
||||
def addInQueueListener(self):
|
||||
"""
|
||||
Add a listener queue to the list of queues, and return the queue.
|
||||
|
||||
Returns:
|
||||
listener queue
|
||||
"""
|
||||
newqueue = self.manager.Queue()
|
||||
self.indata_qlist.append(newqueue)
|
||||
self.indata_qlist_local.append(newqueue)
|
||||
return newqueue
|
||||
|
||||
def removeInQueueListener(self, queue):
|
||||
"""
|
||||
Remove an input listener queue from the queue list.
|
||||
"""
|
||||
# Uses a local queue list to find the index, based on the queue
|
||||
idx = self.indata_qlist_local.index(queue)
|
||||
del self.indata_qlist[idx]
|
||||
del self.indata_qlist_local[idx]
|
||||
|
||||
def startStream(self, avtype: AvType, daqconfig: DaqConfiguration):
|
||||
"""
|
||||
Start the stream, which means the callbacks are called with stream
|
||||
data (audio/video)
|
||||
|
||||
Args:
|
||||
wait: Wait until the stream starts talking before returning from
|
||||
this function.
|
||||
|
||||
"""
|
||||
logging.debug("Starting stream...")
|
||||
self.sendPipe(StreamMsg.startStream, avtype, daqconfig)
|
||||
|
||||
def stopStream(self, avtype: AvType):
|
||||
logging.debug(f"StreamManager::stopStream({avtype})")
|
||||
self.sendPipe(StreamMsg.stopStream, avtype)
|
||||
|
||||
def stopAllStreams(self):
|
||||
self.sendPipe(StreamMsg.stopAllStreams)
|
||||
|
||||
def cleanup(self):
|
||||
"""
|
||||
Stops the stream if it is still running, and after that, it stops the
|
||||
stream process.
|
||||
|
||||
This method SHOULD always be called before removing a AvStream object.
|
||||
Otherwise things will wait forever...
|
||||
|
||||
"""
|
||||
self.sendPipe(StreamMsg.endProcess, None)
|
||||
logging.debug("Joining stream process...")
|
||||
self.streamProcess.join()
|
||||
logging.debug("Joining stream process done")
|
||||
|
||||
def hasVideo(self):
|
||||
"""
|
||||
Stub, TODO: for future
|
||||
"""
|
||||
return False
|
||||
|
||||
def sendPipe(self, msg, *data):
|
||||
"""
|
||||
Send a message with data over the control pipe
|
||||
"""
|
||||
self.pipe.send((msg, data))
|
@ -1,57 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Author: J.A. de Jong
|
||||
|
||||
Description: MonkeyPatch required to let the Multiprocessing library work properly.
|
||||
Should be applied prior to running any other multiprocessing code. Comes from
|
||||
Stackoverflow and is mainly used for managing a list of queues that can be
|
||||
shared between processes.
|
||||
|
||||
For more information, see:
|
||||
https://stackoverflow.com/questions/46779860/multiprocessing-managers-and-custom-classes
|
||||
"""
|
||||
from multiprocessing import managers
|
||||
import logging
|
||||
from functools import wraps
|
||||
from inspect import signature
|
||||
|
||||
orig_AutoProxy = managers.AutoProxy
|
||||
|
||||
__all__ = ['apply_patch']
|
||||
|
||||
|
||||
@wraps(managers.AutoProxy)
|
||||
def AutoProxy(*args, incref=True, manager_owned=False, **kwargs):
|
||||
# Create the autoproxy without the manager_owned flag, then
|
||||
# update the flag on the generated instance. If the manager_owned flag
|
||||
# is set, `incref` is disabled, so set it to False here for the same
|
||||
# result.
|
||||
autoproxy_incref = False if manager_owned else incref
|
||||
proxy = orig_AutoProxy(*args, incref=autoproxy_incref, **kwargs)
|
||||
proxy._owned_by_manager = manager_owned
|
||||
return proxy
|
||||
|
||||
|
||||
def apply_patch():
|
||||
if "manager_owned" in signature(managers.AutoProxy).parameters:
|
||||
return
|
||||
|
||||
logging.debug("Patching multiprocessing.managers.AutoProxy to add manager_owned")
|
||||
managers.AutoProxy = AutoProxy
|
||||
|
||||
# re-register any types already registered to SyncManager without a custom
|
||||
# proxy type, as otherwise these would all be using the old unpatched AutoProxy
|
||||
SyncManager = managers.SyncManager
|
||||
registry = managers.SyncManager._registry
|
||||
for typeid, (callable, exposed, method_to_typeid, proxytype) in registry.items():
|
||||
if proxytype is not orig_AutoProxy:
|
||||
continue
|
||||
create_method = hasattr(managers.SyncManager, typeid)
|
||||
SyncManager.register(
|
||||
typeid,
|
||||
callable=callable,
|
||||
exposed=exposed,
|
||||
method_to_typeid=method_to_typeid,
|
||||
create_method=create_method,
|
||||
)
|
||||
|
@ -1,318 +0,0 @@
|
||||
#!/usr/bin/python3.8
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Read data from stream and record sound and video at the same time
|
||||
"""
|
||||
import dataclasses, logging, os, time, h5py
|
||||
from .lasp_avstream import StreamManager, StreamMetaData, StreamMsg
|
||||
from .lasp_common import AvType
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class RecordStatus:
|
||||
curT: float
|
||||
done: bool
|
||||
|
||||
|
||||
class Recording:
|
||||
"""
|
||||
Class used to perform a recording.
|
||||
"""
|
||||
def __init__(self, fn: str, streammgr: StreamManager,
|
||||
rectime: float = None, wait: bool = True,
|
||||
progressCallback=None,
|
||||
startDelay: float=0):
|
||||
"""
|
||||
Start a recording. Blocks if wait is set to True.
|
||||
|
||||
Args:
|
||||
fn: Filename to record to. Extension is automatically added if not
|
||||
provided.
|
||||
stream: AvStream instance to record from. Should have input
|
||||
channels!
|
||||
rectime: Recording time [s], None for infinite, in seconds. If set
|
||||
to None, or np.inf, the recording continues indefintely.
|
||||
progressCallback: callable that is called with an instance of
|
||||
RecordStatus instance as argument.
|
||||
startDelay: Optional delay added before the recording is *actually*
|
||||
started in [s].
|
||||
"""
|
||||
ext = '.h5'
|
||||
if ext not in fn:
|
||||
fn += ext
|
||||
|
||||
self.smgr = streammgr
|
||||
self.metadata = None
|
||||
|
||||
assert startDelay >= 0
|
||||
self.startDelay = startDelay
|
||||
# Flag used to indicate that we have passed the start delay
|
||||
self.startDelay_passed = False
|
||||
self.rectime = rectime
|
||||
self.fn = fn
|
||||
|
||||
self.video_frame_positions = []
|
||||
self.curT_rounded_to_seconds = 0
|
||||
|
||||
# Counter of the number of blocks
|
||||
self.ablockno = 0
|
||||
self.vframeno = 0
|
||||
|
||||
self.progressCallback = progressCallback
|
||||
self.wait = wait
|
||||
|
||||
self.f = h5py.File(self.fn, 'w')
|
||||
|
||||
# This flag is used to delete the file on finish(), and can be used
|
||||
# when a recording is canceled.
|
||||
self.deleteFile = False
|
||||
|
||||
try:
|
||||
# Input queue
|
||||
self.inq = streammgr.addInQueueListener()
|
||||
|
||||
except RuntimeError:
|
||||
# Cleanup stuff, something is going wrong when starting the stream
|
||||
try:
|
||||
self.f.close()
|
||||
except Exception as e:
|
||||
logging.error(
|
||||
'Error preliminary closing measurement file {fn}: {str(e)}')
|
||||
|
||||
self.__deleteFile()
|
||||
raise
|
||||
|
||||
# Try to obtain stream metadata
|
||||
streammgr.getStreamStatus(AvType.audio_input)
|
||||
|
||||
self.ad = None
|
||||
|
||||
logging.debug('Starting record....')
|
||||
# TODO: Fix this later when we want video
|
||||
# if stream.hasVideo():
|
||||
# stream.addCallback(self.aCallback, AvType.audio_input)
|
||||
self.stop = False
|
||||
|
||||
if self.wait:
|
||||
logging.debug('Stop recording with CTRL-C')
|
||||
try:
|
||||
while not self.stop:
|
||||
self.handleQueue()
|
||||
time.sleep(0.01)
|
||||
except KeyboardInterrupt:
|
||||
logging.debug("Keyboard interrupt on record")
|
||||
finally:
|
||||
self.finish()
|
||||
|
||||
def handleQueue(self):
|
||||
"""
|
||||
This method should be called to grab data from the input queue, which
|
||||
is filled by the stream, and put it into a file. It should be called at
|
||||
a regular interval to prevent overflowing of the queue. It is called
|
||||
within the start() method of the recording, if block is set to True.
|
||||
Otherwise, it should be called from its parent at regular intervals.
|
||||
For example, in Qt this can be done using a QTimer.
|
||||
|
||||
|
||||
"""
|
||||
# logging.debug('handleQueue()')
|
||||
while self.inq.qsize() > 0:
|
||||
msg, data = self.inq.get()
|
||||
# logging.debug(f'Obtained message: {msg}')
|
||||
if msg == StreamMsg.streamData:
|
||||
samples, = data
|
||||
self.__addTimeData(samples)
|
||||
elif msg == StreamMsg.streamStarted:
|
||||
logging.debug(f'handleQueue obtained message {msg}')
|
||||
avtype, metadata = data
|
||||
if metadata is None:
|
||||
raise RuntimeError('BUG: no stream metadata')
|
||||
if avtype in (AvType.audio_duplex, AvType.audio_input):
|
||||
self.processStreamMetaData(metadata)
|
||||
elif msg == StreamMsg.streamMetaData:
|
||||
logging.debug(f'handleQueue obtained message {msg}')
|
||||
avtype, metadata = data
|
||||
if metadata is not None:
|
||||
self.processStreamMetaData(metadata)
|
||||
elif msg == StreamMsg.streamTemporaryError:
|
||||
pass
|
||||
else:
|
||||
logging.debug(f'handleQueue obtained message {msg}')
|
||||
# An error occured, we do not remove the file, but we stop.
|
||||
self.stop = True
|
||||
logging.debug(f'Stream message: {msg}. Recording stopped unexpectedly')
|
||||
raise RuntimeError('Recording stopped unexpectedly')
|
||||
|
||||
|
||||
def processStreamMetaData(self, md: StreamMetaData):
|
||||
"""
|
||||
Stream metadata has been catched. This is used to set all metadata in
|
||||
the measurement file
|
||||
|
||||
"""
|
||||
logging.debug('Recording::processStreamMetaData()')
|
||||
if self.metadata is not None:
|
||||
# Metadata already obtained. We check whether the new metadata is
|
||||
# compatible. Otherwise an error occurs
|
||||
if md != self.metadata:
|
||||
raise RuntimeError('BUG: Incompatible stream metadata!')
|
||||
return
|
||||
|
||||
# The 'Audio' dataset as specified in lasp_measurement, where data is
|
||||
# sent to. We use gzip as compression, this gives moderate a moderate
|
||||
# compression to the data.
|
||||
f = self.f
|
||||
blocksize = md.blocksize
|
||||
nchannels = len(md.in_ch)
|
||||
self.ad = f.create_dataset('audio',
|
||||
(1, blocksize, nchannels),
|
||||
dtype=md.dtype,
|
||||
maxshape=(
|
||||
None, # This means, we can add blocks
|
||||
# indefinitely
|
||||
blocksize,
|
||||
nchannels),
|
||||
compression='gzip'
|
||||
)
|
||||
|
||||
# TODO: This piece of code is not up-to-date and should be changed at a
|
||||
# later instance once we really want to record video simultaneously
|
||||
# with audio.
|
||||
# if smgr.hasVideo():
|
||||
# video_x, video_y = smgr.video_x, smgr.video_y
|
||||
# self.vd = f.create_dataset('video',
|
||||
# (1, video_y, video_x, 3),
|
||||
# dtype='uint8',
|
||||
# maxshape=(
|
||||
# None, video_y, video_x, 3),
|
||||
# compression='gzip'
|
||||
# )
|
||||
|
||||
# Set the bunch of attributes
|
||||
f.attrs['samplerate'] = md.fs
|
||||
f.attrs['nchannels'] = nchannels
|
||||
f.attrs['blocksize'] = blocksize
|
||||
f.attrs['sensitivity'] = [ch.sensitivity for ch in md.in_ch]
|
||||
f.attrs['channelNames'] = [ch.channel_name for ch in md.in_ch]
|
||||
f.attrs['time'] = time.time()
|
||||
self.blocksize = blocksize
|
||||
self.fs = md.fs
|
||||
|
||||
# Measured physical quantity metadata
|
||||
f.attrs['qtys'] = [ch.qty.to_json() for ch in md.in_ch]
|
||||
self.metadata = md
|
||||
|
||||
def setDelete(self, val: bool):
|
||||
"""
|
||||
Set the delete flag. If set, measurement file is deleted at the end of
|
||||
the recording. Typically used for cleaning up after canceling a
|
||||
recording.
|
||||
"""
|
||||
self.deleteFile = val
|
||||
|
||||
def finish(self):
|
||||
"""
|
||||
This method should be called to finish and a close a recording file,
|
||||
remove the queue from the stream, etc.
|
||||
|
||||
"""
|
||||
logging.debug('Recording::finish()')
|
||||
smgr = self.smgr
|
||||
|
||||
# TODO: Fix when video
|
||||
# if smgr.hasVideo():
|
||||
# smgr.removeCallback(self.vCallback, AvType.video_input)
|
||||
# self.f['video_frame_positions'] = self.video_frame_positions
|
||||
|
||||
try:
|
||||
smgr.removeInQueueListener(self.inq)
|
||||
except Exception as e:
|
||||
logging.error(f'Could not remove queue from smgr: {e}')
|
||||
|
||||
try:
|
||||
# Close the recording file
|
||||
self.f.close()
|
||||
except Exception as e:
|
||||
logging.error(f'Error closing file: {e}')
|
||||
|
||||
logging.debug('Recording ended')
|
||||
if self.deleteFile:
|
||||
self.__deleteFile()
|
||||
|
||||
def __deleteFile(self):
|
||||
"""
|
||||
Cleanup the recording file.
|
||||
"""
|
||||
try:
|
||||
os.remove(self.fn)
|
||||
except Exception as e:
|
||||
logging.error(f'Error deleting file: {self.fn}')
|
||||
|
||||
def __addTimeData(self, indata):
|
||||
"""
|
||||
Called by handleQueue() and adds new time data to the storage file.
|
||||
"""
|
||||
# logging.debug('Recording::__addTimeData()')
|
||||
|
||||
if self.stop:
|
||||
# Stop flag is raised. We stop recording here.
|
||||
return
|
||||
|
||||
# The current time that is recorded and stored into the file, without
|
||||
# the new data
|
||||
if not self.metadata:
|
||||
# We obtained stream data, but metadata is not yet available.
|
||||
# Therefore, we request it explicitly and then we return
|
||||
logging.info('Requesting stream metadata')
|
||||
self.smgr.getStreamStatus(AvType.audio_input)
|
||||
self.smgr.getStreamStatus(AvType.audio_duplex)
|
||||
return
|
||||
|
||||
curT = self.ablockno*self.blocksize/self.fs
|
||||
|
||||
# Increase the block counter
|
||||
self.ablockno += 1
|
||||
|
||||
if curT < self.startDelay and not self.startDelay_passed:
|
||||
# Start delay has not been passed
|
||||
return
|
||||
elif curT >= 0 and not self.startDelay_passed:
|
||||
# Start delay passed, switch the flag!
|
||||
self.startDelay_passed = True
|
||||
# Reset the audio block counter and the time
|
||||
self.ablockno = 1
|
||||
curT = 0
|
||||
|
||||
recstatus = RecordStatus(
|
||||
curT=curT,
|
||||
done=False)
|
||||
|
||||
if self.progressCallback is not None:
|
||||
self.progressCallback(recstatus)
|
||||
|
||||
curT_rounded_to_seconds = int(curT)
|
||||
if curT_rounded_to_seconds > self.curT_rounded_to_seconds:
|
||||
self.curT_rounded_to_seconds = curT_rounded_to_seconds
|
||||
print(f'{curT_rounded_to_seconds}', end='', flush=True)
|
||||
else:
|
||||
print('.', end='', flush=True)
|
||||
|
||||
if self.rectime is not None and curT > self.rectime:
|
||||
# We are done!
|
||||
if self.progressCallback is not None:
|
||||
recstatus.done = True
|
||||
self.progressCallback(recstatus)
|
||||
self.stop = True
|
||||
return
|
||||
|
||||
# Add the data to the file, and resize the audio data blocks
|
||||
self.ad.resize(self.ablockno, axis=0)
|
||||
self.ad[self.ablockno-1, :, :] = indata
|
||||
|
||||
|
||||
# def _vCallback(self, frame, framectr):
|
||||
# self.video_frame_positions.append(self.ablockno())
|
||||
# vframeno = self.vframeno
|
||||
# self.vd.resize(vframeno+1, axis=0)
|
||||
# self.vd[vframeno, :, :] = frame
|
||||
# self.vframeno += 1
|
@ -1,369 +0,0 @@
|
||||
#!/usr/bin/env python3.6
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Author: J.A. de Jong - ASCEE
|
||||
|
||||
Description: Signal generator code
|
||||
|
||||
"""
|
||||
import multiprocessing as mp
|
||||
import dataclasses
|
||||
import logging
|
||||
from typing import Tuple
|
||||
import numpy as np
|
||||
|
||||
from .filter import PinkNoise
|
||||
from .lasp_octavefilter import SosOctaveFilterBank, SosThirdOctaveFilterBank
|
||||
from .filter import OctaveBankDesigner, PinkNoise, ThirdOctaveBankDesigner
|
||||
from .lasp_avstream import StreamManager, ignoreSigInt
|
||||
from .wrappers import Siggen as pyxSiggen, Equalizer
|
||||
from enum import Enum, unique, auto
|
||||
|
||||
QUEUE_BUFFER_TIME = 0.5 # The amount of time used in the queues for buffering
|
||||
# of data, larger is more stable, but also enlarges latency
|
||||
|
||||
__all__ = ["SignalType", "NoiseType", "SiggenMessage", "SiggenData", "Siggen"]
|
||||
|
||||
|
||||
@unique
|
||||
class SignalType(Enum):
|
||||
Periodic = 0
|
||||
Noise = 1
|
||||
Sweep = 2
|
||||
Meas = 3
|
||||
|
||||
|
||||
@unique
|
||||
class NoiseType(Enum):
|
||||
white = "White noise"
|
||||
pink = "Pink noise"
|
||||
|
||||
def __str__(self):
|
||||
return str(self.value)
|
||||
|
||||
@staticmethod
|
||||
def fillComboBox(combo):
|
||||
for type_ in list(NoiseType):
|
||||
combo.addItem(str(type_))
|
||||
|
||||
@staticmethod
|
||||
def getCurrent(cb):
|
||||
return list(NoiseType)[cb.currentIndex()]
|
||||
|
||||
|
||||
class SiggenWorkerDone(Exception):
|
||||
def __str__(self):
|
||||
return "Done generating signal"
|
||||
|
||||
|
||||
@unique
|
||||
class SiggenMessage(Enum):
|
||||
"""
|
||||
Different messages that can be send to the signal generator over the pipe
|
||||
connection.
|
||||
"""
|
||||
endProcess = auto() # Stop and quit the signal generator
|
||||
adjustVolume = auto() # Adjust the volume
|
||||
newEqSettings = auto() # Forward new equalizer settings
|
||||
newSiggenData = auto() # Forward new equalizer settings
|
||||
ready = auto() # Send out once, once the signal generator is ready with
|
||||
# pre-generating data.
|
||||
mute = auto() # Mute / unmute siggen
|
||||
|
||||
# These messages are send back to the main thread over the pipe
|
||||
error = auto()
|
||||
done = auto()
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class SiggenData:
|
||||
"""
|
||||
Metadata used to create a Signal Generator
|
||||
"""
|
||||
fs: float # Sample rate [Hz]
|
||||
|
||||
# Number of frames "samples" to send in one block
|
||||
nframes_per_block: int
|
||||
|
||||
# The data type to output
|
||||
dtype: np.dtype
|
||||
|
||||
# Muted?
|
||||
muted: bool
|
||||
|
||||
# Level of output signal [dBFS]el
|
||||
level_dB: float
|
||||
|
||||
# Signal type specific data, i.e.
|
||||
signaltype: SignalType
|
||||
signaltypedata: Tuple = None
|
||||
|
||||
# Settings for the equalizer etc
|
||||
eqdata: object = None # Equalizer data
|
||||
|
||||
class SiggenProcess(mp.Process):
|
||||
"""
|
||||
Main function running in a different process, is responsible for generating
|
||||
new signal data. Uses the signal queue to push new generated signal data
|
||||
on.
|
||||
"""
|
||||
def __init__(self, siggendata, dataq, pipe):
|
||||
|
||||
"""
|
||||
Args:
|
||||
siggendata: The signal generator data to start with.
|
||||
dataq: The queue to put generated signal on
|
||||
pipe: Control and status messaging pipe
|
||||
"""
|
||||
super().__init__()
|
||||
|
||||
# When this is set, a kill on the main process will also kill the
|
||||
# siggen process. Highly wanted feature
|
||||
self.daemon = True
|
||||
|
||||
self.dataq = dataq
|
||||
self.siggendata = siggendata
|
||||
self.pipe = pipe
|
||||
self.eq = None
|
||||
self.siggen = None
|
||||
|
||||
fs = self.siggendata.fs
|
||||
nframes_per_block = siggendata.nframes_per_block
|
||||
self.nblocks_buffer = max(
|
||||
1, int(QUEUE_BUFFER_TIME * fs/ nframes_per_block)
|
||||
)
|
||||
|
||||
def newSiggen(self, siggendata: SiggenData):
|
||||
"""
|
||||
Create a signal generator based on parameters specified in global
|
||||
function data.
|
||||
|
||||
Args:
|
||||
siggendata: SiggenData. Metadata to create a new signal generator.
|
||||
"""
|
||||
|
||||
logging.debug('newSiggen')
|
||||
|
||||
fs = siggendata.fs
|
||||
nframes_per_block = siggendata.nframes_per_block
|
||||
level_dB = siggendata.level_dB
|
||||
signaltype = siggendata.signaltype
|
||||
signaltypedata = siggendata.signaltypedata
|
||||
|
||||
# Muted state
|
||||
self.muted = siggendata.muted
|
||||
|
||||
if signaltype == SignalType.Periodic:
|
||||
freq, = signaltypedata
|
||||
siggen = pyxSiggen.sineWave(fs, freq, level_dB)
|
||||
elif signaltype == SignalType.Noise:
|
||||
noisetype, zerodBpoint = signaltypedata
|
||||
if noisetype == NoiseType.white:
|
||||
sos_colorfilter = None
|
||||
elif noisetype == NoiseType.pink:
|
||||
sos_colorfilter = PinkNoise(fs, zerodBpoint).flatten()
|
||||
else:
|
||||
raise ValueError(f"Unknown noise type")
|
||||
|
||||
siggen = pyxSiggen.noise(fs, level_dB, sos_colorfilter)
|
||||
|
||||
elif signaltype == SignalType.Sweep:
|
||||
fl, fu, Ts, Tq, sweep_flags = signaltypedata
|
||||
siggen = pyxSiggen.sweep(fs, fl, fu, Ts, Tq, sweep_flags, level_dB)
|
||||
|
||||
else:
|
||||
raise ValueError(f"Not implemented signal type: {signaltype}")
|
||||
|
||||
logging.debug('newSiggen')
|
||||
return siggen
|
||||
|
||||
def generate(self):
|
||||
"""
|
||||
Generate a single block of data and put it on the data queue
|
||||
"""
|
||||
signal = self.siggen.genSignal(self.siggendata.nframes_per_block)
|
||||
dtype = self.siggendata.dtype
|
||||
if self.eq is not None:
|
||||
signal = self.eq.equalize(signal)
|
||||
if np.issubdtype(dtype, np.integer):
|
||||
bitdepth_fixed = dtype.itemsize * 8
|
||||
signal *= 2 ** (bitdepth_fixed - 1) - 1
|
||||
if self.muted:
|
||||
# Mute it
|
||||
signal *= 0
|
||||
try:
|
||||
self.dataq.put(signal.astype(dtype))
|
||||
except ValueError:
|
||||
# As of Python 3.8, a value error on a Queue means that the oter
|
||||
# end of the process died.
|
||||
logging.error("Error with data queue, terminating process")
|
||||
self.terminate()
|
||||
|
||||
def newEqualizer(self, eqdata):
|
||||
"""
|
||||
Create an equalizer object from equalizer data
|
||||
|
||||
Args:
|
||||
eqdata: dictionary containing equalizer data. TODO: document the
|
||||
requiring fields.
|
||||
"""
|
||||
if eqdata is None:
|
||||
return None
|
||||
eq_type = eqdata['type']
|
||||
eq_levels = eqdata['levels']
|
||||
fs = self.siggendata.fs
|
||||
|
||||
if eq_type == 'three':
|
||||
fb = SosThirdOctaveFilterBank(fs)
|
||||
elif eq_type == 'one':
|
||||
fb = SosOctaveFilterBank(fs)
|
||||
|
||||
eq = Equalizer(fb._fb)
|
||||
if eq_levels is not None:
|
||||
eq.setLevels(eq_levels)
|
||||
return eq
|
||||
|
||||
def sendPipe(self, msgtype, msg):
|
||||
try:
|
||||
self.pipe.send((msgtype, msg))
|
||||
except OSError:
|
||||
logging.error("Error with pipe, terminating process")
|
||||
self.terminate()
|
||||
|
||||
def run(self):
|
||||
# The main function of the actual process
|
||||
# First things first
|
||||
ignoreSigInt()
|
||||
|
||||
try:
|
||||
self.siggen = self.newSiggen(self.siggendata)
|
||||
except Exception as e:
|
||||
self.sendPipe(SiggenMessage.error, str(e))
|
||||
|
||||
try:
|
||||
self.eq = self.newEqualizer(self.siggendata.eqdata)
|
||||
except Exception as e:
|
||||
self.sendPipe(SiggenMessage.error, str(e))
|
||||
|
||||
# Pre-generate blocks of signal data
|
||||
while self.dataq.qsize() < self.nblocks_buffer:
|
||||
self.generate()
|
||||
|
||||
self.sendPipe(SiggenMessage.ready, None)
|
||||
|
||||
while True:
|
||||
# Wait here for a while, to check for messages to consume
|
||||
if self.pipe.poll(timeout=QUEUE_BUFFER_TIME / 4):
|
||||
try:
|
||||
msg, data = self.pipe.recv()
|
||||
except OSError:
|
||||
logging.error("Error with pipe, terminating process")
|
||||
self.terminate()
|
||||
|
||||
if msg == SiggenMessage.endProcess:
|
||||
logging.debug("Signal generator caught 'endProcess' message. Exiting.")
|
||||
return 0
|
||||
elif msg == SiggenMessage.mute:
|
||||
self.muted = data
|
||||
elif msg == SiggenMessage.adjustVolume:
|
||||
level_dB = data
|
||||
logging.debug(f"Signal generator caught 'adjustVolume' message. New volume = {level_dB:.1f} dB FS")
|
||||
self.siggen.setLevel(level_dB)
|
||||
elif msg == SiggenMessage.newEqSettings:
|
||||
eqdata = data
|
||||
self.eq = self.newEqualizer(eqdata)
|
||||
elif msg == SiggenMessage.newSiggenData:
|
||||
siggendata = data
|
||||
self.siggen = self.newSiggen(siggendata)
|
||||
else:
|
||||
self.pipe.send(
|
||||
SiggenMessage.error, "BUG: Generator caught unknown message. Quiting"
|
||||
)
|
||||
while self.dataq.qsize() < self.nblocks_buffer:
|
||||
# Generate new data and put it in the queue!
|
||||
try:
|
||||
self.generate()
|
||||
except SiggenWorkerDone:
|
||||
self.sendPipe(SiggenMessage.done, None)
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
class Siggen:
|
||||
"""
|
||||
Signal generator class, generates signal data in a different process to
|
||||
unload the work in the calling thread.
|
||||
"""
|
||||
|
||||
def __init__(self, dataq, siggendata: SiggenData):
|
||||
""""""
|
||||
|
||||
self.pipe, client_end = mp.Pipe(duplex=True)
|
||||
|
||||
self.stopped = False
|
||||
|
||||
self.process = SiggenProcess(siggendata, dataq, client_end)
|
||||
self.process.start()
|
||||
|
||||
if not self.process.is_alive():
|
||||
raise RuntimeError('Unexpected signal generator exception')
|
||||
|
||||
# Block waiting here for signal generator to be ready
|
||||
msg, data = self.pipe.recv()
|
||||
if msg == SiggenMessage.ready:
|
||||
logging.debug('Signal generator ready')
|
||||
elif msg == SiggenMessage.error:
|
||||
e = data
|
||||
raise RuntimeError(f'Signal generator exception: {str(e)}')
|
||||
else:
|
||||
# Done, or something
|
||||
if msg == SiggenMessage.done:
|
||||
self.stopped = True
|
||||
|
||||
self.handle_msgs()
|
||||
|
||||
def setLevel(self, new_level):
|
||||
"""
|
||||
Set a new signal level to the generator
|
||||
|
||||
Args:
|
||||
new_level: The new level in [dBFS]
|
||||
"""
|
||||
self.pipe.send((SiggenMessage.adjustVolume, new_level))
|
||||
|
||||
def mute(self, mute):
|
||||
self.pipe.send((SiggenMessage.mute, mute))
|
||||
|
||||
def setEqData(self, eqdata):
|
||||
self.pipe.send((SiggenMessage.newEqSettings, eqdata))
|
||||
|
||||
def setSiggenData(self, siggendata: SiggenData):
|
||||
"""
|
||||
Updates the whole signal generator, based on new signal generator data.
|
||||
"""
|
||||
self.pipe.send((SiggenMessage.newSiggenData, siggendata))
|
||||
|
||||
def handle_msgs(self):
|
||||
while self.pipe.poll():
|
||||
msg, data = self.pipe.recv()
|
||||
if msg == SiggenMessage.error:
|
||||
raise RuntimeError(
|
||||
f"Error in initialization of signal generator: {data}"
|
||||
)
|
||||
# elif msg == SiggenMessage.done:
|
||||
# self.stop()
|
||||
|
||||
|
||||
def cleanup(self):
|
||||
logging.debug('Siggen::stop()')
|
||||
self.pipe.send((SiggenMessage.endProcess, None))
|
||||
self.pipe.close()
|
||||
|
||||
logging.debug('Joining siggen process')
|
||||
self.process.join()
|
||||
logging.debug('Joining siggen process done')
|
||||
self.process.close()
|
||||
|
||||
self.process = None
|
||||
|
@ -1,798 +0,0 @@
|
||||
"""
|
||||
This file contains the Cython wrapper functions to C implementations.
|
||||
"""
|
||||
include "config.pxi"
|
||||
from libc.stdio cimport printf
|
||||
from numpy cimport import_array
|
||||
|
||||
import_array()
|
||||
|
||||
|
||||
IF LASP_DOUBLE_PRECISION == "ON":
|
||||
ctypedef double d
|
||||
ctypedef double complex c
|
||||
NUMPY_FLOAT_TYPE = np.float64
|
||||
NUMPY_COMPLEX_TYPE = np.complex128
|
||||
CYTHON_NUMPY_FLOAT_t = cnp.NPY_FLOAT64
|
||||
CYTHON_NUMPY_COMPLEX_t = cnp.NPY_COMPLEX128
|
||||
|
||||
ELSE:
|
||||
ctypedef float d
|
||||
ctypedef float complex c
|
||||
NUMPY_FLOAT_TYPE = np.float32
|
||||
NUMPY_COMPLEX_TYPE = np.complex64
|
||||
CYTHON_NUMPY_FLOAT_t = cnp.NPY_FLOAT32
|
||||
CYTHON_NUMPY_COMPLEX_t = cnp.NPY_COMPLEX64
|
||||
|
||||
ctypedef size_t us
|
||||
|
||||
cdef extern from "lasp_tracer.h":
|
||||
void setTracerLevel(int)
|
||||
void TRACE(int,const char*)
|
||||
void fsTRACE(int)
|
||||
void feTRACE(int)
|
||||
void clearScreen()
|
||||
|
||||
|
||||
cdef extern from "lasp_mat.h" nogil:
|
||||
ctypedef struct dmat:
|
||||
us n_cols
|
||||
us n_rows
|
||||
d* _data
|
||||
bint _foreign_data
|
||||
ctypedef struct cmat:
|
||||
pass
|
||||
ctypedef cmat vc
|
||||
ctypedef dmat vd
|
||||
|
||||
dmat dmat_foreign_data(us n_rows,
|
||||
us n_cols,
|
||||
d* data,
|
||||
bint own_data)
|
||||
cmat cmat_foreign_data(us n_rows,
|
||||
us n_cols,
|
||||
c* data,
|
||||
bint own_data)
|
||||
|
||||
cmat cmat_alloc(us n_rows,us n_cols)
|
||||
dmat dmat_alloc(us n_rows,us n_cols)
|
||||
vd vd_foreign(const us size,d* data)
|
||||
void vd_free(vd*)
|
||||
|
||||
void dmat_free(dmat*)
|
||||
void cmat_free(cmat*)
|
||||
void cmat_copy(cmat* to,cmat* from_)
|
||||
|
||||
|
||||
cdef extern from "numpy/arrayobject.h":
|
||||
void PyArray_ENABLEFLAGS(cnp.ndarray arr, int flags)
|
||||
|
||||
|
||||
cdef extern from "lasp_python.h":
|
||||
object dmat_to_ndarray(dmat*,bint transfer_ownership)
|
||||
|
||||
__all__ = ['AvPowerSpectra', 'SosFilterBank', 'FilterBank', 'Siggen',
|
||||
'sweep_flag_forward', 'sweep_flag_backward', 'sweep_flag_linear',
|
||||
'sweep_flag_exponential',
|
||||
'load_fft_wisdom', 'store_fft_wisdom']
|
||||
|
||||
setTracerLevel(15)
|
||||
|
||||
cdef extern from "cblas.h":
|
||||
int openblas_get_num_threads()
|
||||
void openblas_set_num_threads(int)
|
||||
|
||||
# If we touch this variable: we get segfaults when running from
|
||||
# Spyder!
|
||||
# openblas_set_num_threads(8)
|
||||
# print("Number of threads: ",
|
||||
# openblas_get_num_threads())
|
||||
|
||||
def cls():
|
||||
clearScreen()
|
||||
# cls()
|
||||
|
||||
cdef extern from "lasp_fft.h":
|
||||
void c_load_fft_wisdom "load_fft_wisdom" (const char* wisdom)
|
||||
char* c_store_fft_wisdom "store_fft_wisdom" ()
|
||||
ctypedef struct c_Fft "Fft"
|
||||
c_Fft* Fft_create(us nfft)
|
||||
void Fft_free(c_Fft*)
|
||||
void Fft_fft(c_Fft*,dmat * timedate,cmat * res) nogil
|
||||
void Fft_ifft(c_Fft*,cmat * freqdata,dmat* timedata) nogil
|
||||
us Fft_nfft(c_Fft*)
|
||||
|
||||
def load_fft_wisdom(const unsigned char[::1] wisdom):
|
||||
c_load_fft_wisdom(<const char*> &wisdom[0])
|
||||
|
||||
from cpython cimport PyBytes_FromString
|
||||
from libc.stdlib cimport free
|
||||
|
||||
def store_fft_wisdom():
|
||||
cdef char* wisdom = c_store_fft_wisdom()
|
||||
|
||||
if wisdom != NULL:
|
||||
try:
|
||||
bts = PyBytes_FromString(wisdom)
|
||||
finally:
|
||||
free(wisdom)
|
||||
return bts
|
||||
else:
|
||||
return None
|
||||
|
||||
cdef class Fft:
|
||||
cdef:
|
||||
c_Fft* _fft
|
||||
|
||||
def __cinit__(self, us nfft):
|
||||
self._fft = Fft_create(nfft)
|
||||
if self._fft == NULL:
|
||||
raise RuntimeError('Fft allocation failed')
|
||||
|
||||
def __dealloc__(self):
|
||||
if self._fft!=NULL:
|
||||
Fft_free(self._fft)
|
||||
|
||||
def fft(self,d[::1,:] timedata):
|
||||
|
||||
cdef us nfft = Fft_nfft(self._fft)
|
||||
cdef us nchannels = timedata.shape[1]
|
||||
assert timedata.shape[0] ==nfft
|
||||
|
||||
result = np.empty((nfft//2+1,nchannels),
|
||||
dtype=NUMPY_COMPLEX_TYPE,
|
||||
order='F')
|
||||
|
||||
# result[:,:] = np.nan+1j*np.nan
|
||||
cdef c[::1,:] result_view = result
|
||||
cdef cmat r = cmat_foreign_data(result.shape[0],
|
||||
result.shape[1],
|
||||
&result_view[0,0],
|
||||
False)
|
||||
|
||||
cdef dmat t = dmat_foreign_data(timedata.shape[0],
|
||||
timedata.shape[1],
|
||||
&timedata[0,0],
|
||||
False)
|
||||
with nogil:
|
||||
Fft_fft(self._fft,&t,&r)
|
||||
|
||||
dmat_free(&t)
|
||||
cmat_free(&r)
|
||||
|
||||
return result
|
||||
|
||||
def ifft(self,c[::1,:] freqdata):
|
||||
|
||||
cdef us nfft = Fft_nfft(self._fft)
|
||||
cdef us nchannels = freqdata.shape[1]
|
||||
assert freqdata.shape[0] == nfft//2+1
|
||||
|
||||
|
||||
# result[:,:] = np.nan+1j*np.nan
|
||||
|
||||
cdef cmat f = cmat_foreign_data(freqdata.shape[0],
|
||||
freqdata.shape[1],
|
||||
&freqdata[0,0],
|
||||
False)
|
||||
|
||||
timedata = np.empty((nfft,nchannels),
|
||||
dtype=NUMPY_FLOAT_TYPE,
|
||||
order='F')
|
||||
|
||||
cdef d[::1,:] timedata_view = timedata
|
||||
cdef dmat t = dmat_foreign_data(timedata.shape[0],
|
||||
timedata.shape[1],
|
||||
&timedata_view[0,0],
|
||||
False)
|
||||
|
||||
with nogil:
|
||||
Fft_ifft(self._fft,&f,&t)
|
||||
|
||||
dmat_free(&t)
|
||||
cmat_free(&f)
|
||||
|
||||
return timedata
|
||||
|
||||
cdef extern from "lasp_window.h":
|
||||
ctypedef enum WindowType:
|
||||
Hann
|
||||
Hamming
|
||||
Rectangular
|
||||
Bartlett
|
||||
Blackman
|
||||
|
||||
# Export these constants to Python
|
||||
class Window:
|
||||
hann = Hann
|
||||
hamming = Hamming
|
||||
rectangular = Rectangular
|
||||
bartlett = Bartlett
|
||||
blackman = Blackman
|
||||
|
||||
cdef extern from "lasp_ps.h":
|
||||
ctypedef struct c_PowerSpectra "PowerSpectra"
|
||||
c_PowerSpectra* PowerSpectra_alloc(const us nfft,
|
||||
const WindowType wt)
|
||||
|
||||
void PowerSpectra_compute(const c_PowerSpectra* ps,
|
||||
const dmat * timedata,
|
||||
cmat * result) nogil
|
||||
|
||||
|
||||
void PowerSpectra_free(c_PowerSpectra*)
|
||||
|
||||
cdef class PowerSpectra:
|
||||
cdef:
|
||||
c_PowerSpectra* _ps
|
||||
|
||||
def __cinit__(self, us nfft,us window=Window.rectangular):
|
||||
self._ps = PowerSpectra_alloc(nfft,<WindowType> window)
|
||||
if self._ps == NULL:
|
||||
raise RuntimeError('PowerSpectra allocation failed')
|
||||
|
||||
def compute(self,d[::1,:] timedata):
|
||||
cdef:
|
||||
us nchannels = timedata.shape[1]
|
||||
us nfft = timedata.shape[0]
|
||||
int rv
|
||||
dmat td
|
||||
cmat result_mat
|
||||
|
||||
|
||||
td = dmat_foreign_data(nfft,
|
||||
nchannels,
|
||||
&timedata[0,0],
|
||||
False)
|
||||
|
||||
# The array here is created in such a way that the strides
|
||||
# increase with increasing dimension. This is required for
|
||||
# interoperability with the C-code, that stores all
|
||||
# cross-spectra in a 2D matrix, where the first axis is the
|
||||
# frequency axis, and the second axis corresponds to a certain
|
||||
# cross-spectrum, as C_ij(f) = result[freq,i+j*nchannels]
|
||||
|
||||
result = np.empty((nfft//2+1,nchannels,nchannels),
|
||||
dtype = NUMPY_COMPLEX_TYPE,
|
||||
order='F')
|
||||
|
||||
cdef c[::1,:,:] result_view = result
|
||||
|
||||
result_mat = cmat_foreign_data(nfft//2+1,
|
||||
nchannels*nchannels,
|
||||
&result_view[0,0,0],
|
||||
False)
|
||||
|
||||
|
||||
with nogil:
|
||||
PowerSpectra_compute(self._ps,&td,&result_mat)
|
||||
|
||||
dmat_free(&td)
|
||||
cmat_free(&result_mat)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def __dealloc__(self):
|
||||
if self._ps != NULL:
|
||||
PowerSpectra_free(self._ps)
|
||||
|
||||
|
||||
cdef extern from "lasp_aps.h":
|
||||
ctypedef struct c_AvPowerSpectra "AvPowerSpectra"
|
||||
c_AvPowerSpectra* AvPowerSpectra_alloc(const us nfft,
|
||||
const us nchannels,
|
||||
d overlap_percentage,
|
||||
const WindowType wt,
|
||||
const vd* weighting)
|
||||
|
||||
cmat* AvPowerSpectra_addTimeData(const c_AvPowerSpectra* ps,
|
||||
const dmat * timedata) nogil
|
||||
|
||||
|
||||
void AvPowerSpectra_free(c_AvPowerSpectra*)
|
||||
us AvPowerSpectra_getAverages(const c_AvPowerSpectra*);
|
||||
|
||||
cdef class AvPowerSpectra:
|
||||
cdef:
|
||||
c_AvPowerSpectra* aps
|
||||
us nfft, nchannels
|
||||
|
||||
def __cinit__(self,us nfft,
|
||||
us nchannels,
|
||||
d overlap_percentage,
|
||||
us window=Window.hann,
|
||||
d[:] weighting = np.array([])):
|
||||
|
||||
|
||||
cdef vd weighting_vd
|
||||
cdef vd* weighting_ptr = NULL
|
||||
if(weighting.size != 0):
|
||||
weighting_vd = dmat_foreign_data(weighting.size,1,
|
||||
&weighting[0],False)
|
||||
weighting_ptr = &weighting_vd
|
||||
|
||||
self.aps = AvPowerSpectra_alloc(nfft,
|
||||
nchannels,
|
||||
overlap_percentage,
|
||||
<WindowType> window,
|
||||
weighting_ptr)
|
||||
self.nchannels = nchannels
|
||||
self.nfft = nfft
|
||||
|
||||
if self.aps == NULL:
|
||||
raise RuntimeError('AvPowerSpectra allocation failed')
|
||||
|
||||
def __dealloc__(self):
|
||||
if self.aps:
|
||||
AvPowerSpectra_free(self.aps)
|
||||
def getAverages(self):
|
||||
return AvPowerSpectra_getAverages(self.aps)
|
||||
|
||||
def addTimeData(self,d[::1,:] timedata):
|
||||
"""!
|
||||
Adds time data, returns current result
|
||||
"""
|
||||
cdef:
|
||||
us nsamples = timedata.shape[0]
|
||||
us nchannels = timedata.shape[1]
|
||||
dmat td
|
||||
cmat* result_ptr
|
||||
|
||||
if nchannels != self.nchannels:
|
||||
raise RuntimeError('Invalid number of channels')
|
||||
|
||||
td = dmat_foreign_data(nsamples,
|
||||
nchannels,
|
||||
&timedata[0,0],
|
||||
False)
|
||||
|
||||
result = np.empty((self.nfft//2+1,nchannels,nchannels),
|
||||
dtype = NUMPY_COMPLEX_TYPE,
|
||||
order='F')
|
||||
|
||||
cdef c[::1,:,:] result_view = result
|
||||
|
||||
cdef cmat res = cmat_foreign_data(self.nfft//2+1,
|
||||
nchannels*nchannels,
|
||||
&result_view[0,0,0],
|
||||
False)
|
||||
with nogil:
|
||||
result_ptr = AvPowerSpectra_addTimeData(self.aps,
|
||||
&td)
|
||||
|
||||
# The array here is created in such a way that the strides
|
||||
# increase with increasing dimension. This is required for
|
||||
# interoperability with the C-code, that stores all
|
||||
# cross-spectra in a 2D matrix, where the first axis is the
|
||||
# frequency axis, and the second axis corresponds to a certain
|
||||
# cross-spectrum, as C_ij(f) = result[freq,i+j*nchannels]
|
||||
|
||||
# Copy result
|
||||
cmat_copy(&res,result_ptr)
|
||||
|
||||
cmat_free(&res)
|
||||
dmat_free(&td)
|
||||
|
||||
return result
|
||||
|
||||
cdef extern from "lasp_firfilterbank.h":
|
||||
ctypedef struct c_Firfilterbank "Firfilterbank"
|
||||
c_Firfilterbank* Firfilterbank_create(const dmat* h,const us nfft) nogil
|
||||
dmat Firfilterbank_filter(c_Firfilterbank* fb,const vd* x) nogil
|
||||
void Firfilterbank_free(c_Firfilterbank* fb) nogil
|
||||
|
||||
|
||||
cdef class FilterBank:
|
||||
cdef:
|
||||
c_Firfilterbank* fb
|
||||
def __cinit__(self,d[::1,:] h, us nfft):
|
||||
cdef dmat hmat = dmat_foreign_data(h.shape[0],
|
||||
h.shape[1],
|
||||
&h[0,0],
|
||||
False)
|
||||
|
||||
self.fb = Firfilterbank_create(&hmat,nfft)
|
||||
dmat_free(&hmat)
|
||||
if not self.fb:
|
||||
raise RuntimeError('Error creating FilberBank')
|
||||
|
||||
def __dealloc__(self):
|
||||
if self.fb:
|
||||
Firfilterbank_free(self.fb)
|
||||
|
||||
def filter_(self,d[::1, :] input_):
|
||||
assert input_.shape[1] == 1
|
||||
cdef dmat input_vd = dmat_foreign_data(input_.shape[0],1,
|
||||
&input_[0, 0],False)
|
||||
|
||||
|
||||
cdef dmat output
|
||||
with nogil:
|
||||
output = Firfilterbank_filter(self.fb,&input_vd)
|
||||
|
||||
# Steal the pointer from output
|
||||
result = dmat_to_ndarray(&output,True)
|
||||
|
||||
dmat_free(&output)
|
||||
vd_free(&input_vd)
|
||||
|
||||
return result
|
||||
|
||||
cdef extern from "lasp_sosfilterbank.h":
|
||||
ctypedef struct c_Sosfilterbank "Sosfilterbank"
|
||||
c_Sosfilterbank* Sosfilterbank_create(const us nthreads, const us filterbank_size, const us nsections) nogil
|
||||
void Sosfilterbank_setFilter(c_Sosfilterbank* fb,const us filter_no, const vd filter_coefs)
|
||||
dmat Sosfilterbank_filter(c_Sosfilterbank* fb,const vd* x) nogil
|
||||
void Sosfilterbank_free(c_Sosfilterbank* fb) nogil
|
||||
|
||||
|
||||
cdef class SosFilterBank:
|
||||
cdef:
|
||||
c_Sosfilterbank* fb
|
||||
us nsections
|
||||
def __cinit__(self,const us filterbank_size, const us nsections):
|
||||
self.nsections = nsections
|
||||
|
||||
self.fb = Sosfilterbank_create(0, filterbank_size,nsections)
|
||||
|
||||
def setFilter(self,us filter_no, d[:, ::1] sos):
|
||||
"""
|
||||
|
||||
Args:
|
||||
filter_no: Filter number of the filterbank to set the
|
||||
filter for
|
||||
sos: Second axis are the filter coefficients, first axis
|
||||
is the section, second axis are the coefficients for that section.
|
||||
Storage is in agreement with specification from Scipy: first axis
|
||||
is the section, second axis are the coefficients for that section.
|
||||
|
||||
"""
|
||||
if sos.shape[0] != self.nsections:
|
||||
raise RuntimeError(f'Invalid number of sections in filter data, should be {self.nsections}.')
|
||||
elif sos.shape[1] != 6:
|
||||
raise RuntimeError('Illegal number of filter coefficients in section. Should be 6.')
|
||||
cdef dmat coefs = dmat_foreign_data(sos.size,1,
|
||||
&sos[0, 0],False # No copying
|
||||
)
|
||||
Sosfilterbank_setFilter(self.fb,filter_no, coefs)
|
||||
|
||||
def __dealloc__(self):
|
||||
if self.fb:
|
||||
Sosfilterbank_free(self.fb)
|
||||
|
||||
def filter_(self,d[::1, :] input_):
|
||||
# Only single channel input
|
||||
assert input_.shape[1] == 1
|
||||
cdef dmat input_vd = dmat_foreign_data(input_.shape[0],1,
|
||||
&input_[0, 0],False)
|
||||
|
||||
|
||||
cdef dmat output
|
||||
with nogil:
|
||||
output = Sosfilterbank_filter(self.fb,&input_vd)
|
||||
#printf('Came back from filter\n')
|
||||
# Steal the pointer from output
|
||||
result = dmat_to_ndarray(&output,True)
|
||||
#printf('Converted to array\n')
|
||||
dmat_free(&output)
|
||||
vd_free(&input_vd)
|
||||
#printf('Ready to return\n')
|
||||
return result
|
||||
|
||||
cdef extern from "lasp_decimation.h":
|
||||
ctypedef struct c_Decimator "Decimator"
|
||||
ctypedef enum DEC_FAC:
|
||||
DEC_FAC_4
|
||||
|
||||
c_Decimator* Decimator_create(us nchannels,DEC_FAC d) nogil
|
||||
dmat Decimator_decimate(c_Decimator* dec,const dmat* samples) nogil
|
||||
void Decimator_free(c_Decimator* dec) nogil
|
||||
|
||||
|
||||
cdef extern from "lasp_slm.h" nogil:
|
||||
ctypedef struct c_Slm "Slm"
|
||||
d TAU_FAST, TAU_SLOW, TAU_IMPULSE
|
||||
c_Slm* Slm_create(c_Sosfilterbank* prefilter,
|
||||
c_Sosfilterbank* bandpass,
|
||||
d fs, d tau, d ref_level,
|
||||
us* downsampling_fac)
|
||||
dmat Slm_run(c_Slm* slm,vd* input_data)
|
||||
void Slm_free(c_Slm* slm)
|
||||
vd Slm_Leq(c_Slm*)
|
||||
vd Slm_Lmax(c_Slm*)
|
||||
vd Slm_Lpeak(c_Slm*)
|
||||
|
||||
|
||||
tau_fast = TAU_FAST
|
||||
tau_slow = TAU_SLOW
|
||||
tau_impulse = TAU_IMPULSE
|
||||
|
||||
cdef class Slm:
|
||||
cdef:
|
||||
c_Slm* c_slm
|
||||
public us downsampling_fac
|
||||
|
||||
def __cinit__(self, d[::1] sos_prefilter,
|
||||
d[:, ::1] sos_bandpass,
|
||||
d fs, d tau, d ref_level):
|
||||
|
||||
cdef:
|
||||
us prefilter_nsections
|
||||
us bandpass_nsections
|
||||
us bandpass_nchannels
|
||||
c_Sosfilterbank* prefilter = NULL
|
||||
c_Sosfilterbank* bandpass = NULL
|
||||
vd coefs_vd
|
||||
d[:] coefs
|
||||
|
||||
if sos_prefilter is not None:
|
||||
assert sos_prefilter.size % 6 == 0
|
||||
prefilter_nsections = sos_prefilter.size // 6
|
||||
prefilter = Sosfilterbank_create(0, 1,prefilter_nsections)
|
||||
coefs = sos_prefilter
|
||||
coefs_vd = dmat_foreign_data(prefilter_nsections*6,1,
|
||||
&coefs[0],False)
|
||||
Sosfilterbank_setFilter(prefilter, 0, coefs_vd)
|
||||
|
||||
if prefilter is NULL:
|
||||
raise RuntimeError('Error creating pre-filter')
|
||||
|
||||
if sos_bandpass is not None:
|
||||
assert sos_bandpass.shape[1] % 6 == 0
|
||||
bandpass_nsections = sos_bandpass.shape[1] // 6
|
||||
bandpass_nchannels = sos_bandpass.shape[0]
|
||||
bandpass = Sosfilterbank_create(0,
|
||||
bandpass_nchannels,
|
||||
bandpass_nsections)
|
||||
if bandpass == NULL:
|
||||
if prefilter:
|
||||
Sosfilterbank_free(prefilter)
|
||||
raise RuntimeError('Error creating bandpass filter')
|
||||
|
||||
|
||||
for i in range(bandpass_nchannels):
|
||||
coefs = sos_bandpass[i, :]
|
||||
coefs_vd = dmat_foreign_data(bandpass_nsections*6,1,
|
||||
&coefs[0],False)
|
||||
|
||||
Sosfilterbank_setFilter(bandpass, i, coefs_vd)
|
||||
|
||||
self.c_slm = Slm_create(prefilter, bandpass,
|
||||
fs, tau, ref_level,
|
||||
&self.downsampling_fac)
|
||||
if self.c_slm is NULL:
|
||||
Sosfilterbank_free(prefilter)
|
||||
Sosfilterbank_free(bandpass)
|
||||
raise RuntimeError('Error creating sound level meter')
|
||||
|
||||
def run(self, d[:, ::1] data):
|
||||
assert data.shape[1] == 1
|
||||
cdef vd data_vd = dmat_foreign_data(data.shape[0], 1,
|
||||
&data[0,0], False)
|
||||
cdef dmat res
|
||||
with nogil:
|
||||
res = Slm_run(self.c_slm, &data_vd)
|
||||
result = dmat_to_ndarray(&res,True)
|
||||
return result
|
||||
|
||||
def Leq(self):
|
||||
cdef vd res
|
||||
res = Slm_Leq(self.c_slm)
|
||||
# True below, means transfer ownership of allocated data to Numpy
|
||||
return dmat_to_ndarray(&res,True)
|
||||
|
||||
def Lmax(self):
|
||||
cdef vd res
|
||||
res = Slm_Lmax(self.c_slm)
|
||||
# True below, means transfer ownership of allocated data to Numpy
|
||||
return dmat_to_ndarray(&res,True)
|
||||
|
||||
def Lpeak(self):
|
||||
cdef vd res
|
||||
res = Slm_Lpeak(self.c_slm)
|
||||
# True below, means transfer ownership of allocated data to Numpy
|
||||
return dmat_to_ndarray(&res,True)
|
||||
|
||||
def __dealloc__(self):
|
||||
if self.c_slm:
|
||||
Slm_free(self.c_slm)
|
||||
|
||||
|
||||
|
||||
|
||||
cdef class Decimator:
|
||||
cdef:
|
||||
c_Decimator* dec
|
||||
us nchannels
|
||||
def __cinit__(self, us nchannels,us dec_fac):
|
||||
assert dec_fac == 4, 'Invalid decimation factor'
|
||||
self.nchannels = nchannels
|
||||
self.dec = Decimator_create(nchannels,DEC_FAC_4)
|
||||
if not self.dec:
|
||||
raise RuntimeError('Error creating decimator')
|
||||
|
||||
def decimate(self,d[::1,:] samples):
|
||||
assert samples.shape[1] == self.nchannels,'Invalid number of channels'
|
||||
if samples.shape[0] == 0:
|
||||
return np.zeros((0, self.nchannels))
|
||||
|
||||
cdef dmat d_samples = dmat_foreign_data(samples.shape[0],
|
||||
samples.shape[1],
|
||||
&samples[0,0],
|
||||
False)
|
||||
|
||||
cdef dmat res = Decimator_decimate(self.dec,&d_samples)
|
||||
result = dmat_to_ndarray(&res,True)
|
||||
dmat_free(&res)
|
||||
return result
|
||||
|
||||
def __dealloc__(self):
|
||||
if self.dec != NULL:
|
||||
Decimator_free(self.dec)
|
||||
|
||||
|
||||
cdef extern from "lasp_siggen.h":
|
||||
ctypedef struct c_Siggen "Siggen"
|
||||
us SWEEP_FLAG_FORWARD, SWEEP_FLAG_BACKWARD, SWEEP_FLAG_LINEAR
|
||||
us SWEEP_FLAG_EXPONENTIAL
|
||||
|
||||
c_Siggen* Siggen_Noise_create(d fs, d level_dB, c_Sosfilterbank*
|
||||
colorfilter)
|
||||
c_Siggen* Siggen_Sinewave_create(d fs, d freq, d level_dB)
|
||||
c_Siggen* Siggen_Sweep_create(d fs, d fl,
|
||||
d fu, d Ts,d Tq, us sweep_flags,
|
||||
d level_dB)
|
||||
void Siggen_setLevel(c_Siggen*,d new_level_dB)
|
||||
us Siggen_getN(const c_Siggen*)
|
||||
void Siggen_genSignal(c_Siggen*, vd* samples) nogil
|
||||
void Siggen_free(c_Siggen*)
|
||||
|
||||
# Sweep flags
|
||||
sweep_flag_forward = SWEEP_FLAG_FORWARD
|
||||
sweep_flag_backward = SWEEP_FLAG_BACKWARD
|
||||
|
||||
sweep_flag_linear = SWEEP_FLAG_LINEAR
|
||||
sweep_flag_exponential = SWEEP_FLAG_EXPONENTIAL
|
||||
|
||||
from .filter import PinkNoise
|
||||
|
||||
cdef class Siggen:
|
||||
|
||||
cdef c_Siggen *_siggen
|
||||
|
||||
def __cinit__(self):
|
||||
self._siggen = NULL
|
||||
|
||||
def __dealloc__(self):
|
||||
if self._siggen:
|
||||
Siggen_free(self._siggen)
|
||||
|
||||
def setLevel(self,d level_dB):
|
||||
Siggen_setLevel(self._siggen, level_dB)
|
||||
|
||||
def genSignal(self, us nsamples):
|
||||
output = np.empty(nsamples, dtype=np.float)
|
||||
assert self._siggen != NULL
|
||||
|
||||
cdef d[:] output_view = output
|
||||
|
||||
cdef dmat output_dmat = dmat_foreign_data(nsamples,
|
||||
1,
|
||||
&output_view[0],
|
||||
False)
|
||||
with nogil:
|
||||
Siggen_genSignal(self._siggen,
|
||||
&output_dmat)
|
||||
|
||||
return output
|
||||
|
||||
def getN(self):
|
||||
return Siggen_getN(self._siggen)
|
||||
|
||||
def progress(self):
|
||||
"""
|
||||
TODO: Should be implemented to return the current position in the
|
||||
generator.
|
||||
|
||||
"""
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def sineWave(d fs,d freq,d level_dB):
|
||||
cdef c_Siggen* c_siggen = Siggen_Sinewave_create(fs, freq, level_dB)
|
||||
siggen = Siggen()
|
||||
siggen._siggen = c_siggen
|
||||
return siggen
|
||||
|
||||
|
||||
@staticmethod
|
||||
def noise(d fs, d level_dB, d[::1] colorfilter_coefs=None):
|
||||
cdef:
|
||||
c_Sosfilterbank* colorfilter = NULL
|
||||
if colorfilter_coefs is not None:
|
||||
assert colorfilter_coefs.size % 6 == 0
|
||||
colorfilter_nsections = colorfilter_coefs.size // 6
|
||||
colorfilter = Sosfilterbank_create(0, 1,colorfilter_nsections)
|
||||
coefs = colorfilter_coefs
|
||||
coefs_vd = dmat_foreign_data(colorfilter_nsections*6,1,
|
||||
&colorfilter_coefs[0],False)
|
||||
Sosfilterbank_setFilter(colorfilter, 0, coefs_vd)
|
||||
|
||||
if colorfilter is NULL:
|
||||
raise RuntimeError('Error creating pre-filter')
|
||||
cdef c_Siggen* c_siggen = Siggen_Noise_create(fs, level_dB, colorfilter)
|
||||
siggen = Siggen()
|
||||
siggen._siggen = c_siggen
|
||||
return siggen
|
||||
|
||||
|
||||
@staticmethod
|
||||
def sweep(d fs, d fl, d fu, d Ts, d Tq, us sweep_flags, d level_dB):
|
||||
cdef c_Siggen* c_siggen = Siggen_Sweep_create(fs,
|
||||
fl,
|
||||
fu,
|
||||
Ts,
|
||||
Tq,
|
||||
sweep_flags,
|
||||
level_dB)
|
||||
if c_siggen == NULL:
|
||||
raise ValueError('Failed creating signal generator')
|
||||
siggen = Siggen()
|
||||
siggen._siggen = c_siggen
|
||||
return siggen
|
||||
|
||||
cdef extern from "lasp_eq.h" nogil:
|
||||
ctypedef struct c_Eq "Eq"
|
||||
c_Eq* Eq_create(c_Sosfilterbank* fb)
|
||||
vd Eq_equalize(c_Eq* eq,const vd* input_data)
|
||||
us Eq_getNLevels(const c_Eq* eq)
|
||||
void Eq_setLevels(c_Eq* eq, const vd* levels)
|
||||
void Eq_free(c_Eq* eq)
|
||||
|
||||
cdef class Equalizer:
|
||||
cdef:
|
||||
c_Eq* ceq
|
||||
|
||||
def __cinit__(self, SosFilterBank cdef_fb):
|
||||
"""
|
||||
Initialize equalizer using given filterbank. Note: Steals pointer of
|
||||
underlying c_Sosfilterbank!!
|
||||
"""
|
||||
self.ceq = Eq_create(cdef_fb.fb)
|
||||
# Set this pointer to NULL, such that the underlying c_SosfilterBank is
|
||||
# not deallocated.
|
||||
cdef_fb.fb = NULL
|
||||
|
||||
def getNLevels(self):
|
||||
return Eq_getNLevels(self.ceq)
|
||||
|
||||
def setLevels(self,d[:] new_levels):
|
||||
cdef dmat dmat_new_levels = dmat_foreign_data(new_levels.shape[0],
|
||||
1,
|
||||
&new_levels[0],
|
||||
False)
|
||||
Eq_setLevels(self.ceq, &dmat_new_levels)
|
||||
dmat_free(&dmat_new_levels)
|
||||
|
||||
def equalize(self, d[::1] input_data):
|
||||
cdef:
|
||||
vd res
|
||||
cdef dmat input_dmat = dmat_foreign_data(input_data.size,
|
||||
1,
|
||||
&input_data[0],
|
||||
False)
|
||||
with nogil:
|
||||
res = Eq_equalize(self.ceq, &input_dmat)
|
||||
|
||||
# Steal the pointer from output
|
||||
py_res = dmat_to_ndarray(&res,True)[:,0]
|
||||
|
||||
dmat_free(&res)
|
||||
vd_free(&input_dmat)
|
||||
|
||||
return py_res
|
||||
|
||||
def __dealloc__(self):
|
||||
if self.ceq:
|
||||
Eq_free(self.ceq)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user