Merged Pybind11 branch. It is more ~ less ready

This commit is contained in:
Anne de Jong 2022-10-06 21:45:36 +02:00
commit a581226f3d
211 changed files with 10033 additions and 15302 deletions

28
.gitignore vendored
View File

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

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

View File

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

684
Doxyfile

File diff suppressed because it is too large Load Diff

View File

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

@ -0,0 +1,29 @@
if(WIN32)
set(home $ENV{USERPROFILE})
# set(miniconda_dir ${home}\\Miniconda3)
message("Building for Windows")
include_directories(
..\\rtaudio
C:\\mingw\\mingw64\\include\\OpenBLAS
link_directories(${home}\\miniconda3\\Library\\include)
)
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH} $miniconda_dir\\Lib\\cmake")
# include(
add_definitions(-DMS_WIN64)
link_directories(C:\\mingw\\mingw64\\lib)
link_directories(C:\\mingw\\mingw64\\bin)
link_directories(..\\rtaudio)
link_directories(${home}\\Miniconda3)
add_definitions(-DHAS_RTAUDIO_WIN_WASAPI_API)
else() # Linux compile
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wfatal-errors")
include_directories(/usr/local/include/rtaudio)
include_directories(/usr/include/rtaudio)
link_directories(/usr/local/lib)
# This should become optional later on, and be added to the windows list as
# well.
endif()
# The last argument here takes care of calling SIGABRT when an integer overflow
# occures.

View File

@ -0,0 +1,26 @@
# First tries to find Python 3, then tries to import the pybind11 module to
# query the CMake config location, and finally imports pybind11 using
# find_package(pybind11 REQUIRED CONFIG).
function(find_pybind11_python_first)
# Query Python to see if it knows where the headers are
find_package(Python3 REQUIRED COMPONENTS Interpreter Development)
if (NOT pybind11_ROOT OR NOT EXISTS ${pybind11_ROOT})
execute_process(COMMAND ${Python3_EXECUTABLE}
-m pybind11 --cmakedir
OUTPUT_VARIABLE PY_BUILD_PYBIND11_CMAKE
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE PY_BUILD_CMAKE_PYBIND11_RESULT)
# If it was successful
if (PY_BUILD_CMAKE_PYBIND11_RESULT EQUAL 0)
message(STATUS "Found pybind11: ${PY_BUILD_PYBIND11_CMAKE}")
set(pybind11_ROOT ${PY_BUILD_PYBIND11_CMAKE}
CACHE PATH "Path to the pybind11 CMake configuration." FORCE)
else()
unset(pybind11_ROOT CACHE)
endif()
endif()
find_package(pybind11 REQUIRED CONFIG)
endfunction()

12
cmake/rtaudio.cmake Normal file
View 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
View 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()

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

View 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

File diff suppressed because one or more lines are too long

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

Binary file not shown.

BIN
img/LASP_200px.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +0,0 @@
from .lasp_device_common import *
from .lasp_deviceinfo import *
from .lasp_daqconfig import *
from .lasp_daq import *

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +0,0 @@
include "lasp_common_decls.pxd"
from .lasp_deviceinfo cimport DeviceInfo
cdef class DaqConfiguration:
cdef:
cppDaqConfiguration config

View File

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

View File

@ -1,5 +0,0 @@
include "lasp_common_decls.pxd"
cdef class DeviceInfo:
cdef:
cppDeviceInfo devinfo

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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