Intermediate commit. Ready for some serious testing.

This commit is contained in:
Anne de Jong 2022-06-29 12:25:32 +02:00
parent b561c83448
commit 7095f9d5e7
46 changed files with 1182 additions and 2193 deletions

1
.gitignore vendored
View File

@ -32,3 +32,4 @@ lasp/device/lasp_daq.cxx
lasp/c/lasp_config.h
compile_commands.json
.cache
lasp_config.h

3
.gitmodules vendored
View File

@ -7,3 +7,6 @@
[submodule "DebugTrace-cpp"]
path = DebugTrace-cpp
url = https://github.com/MasatoKokubo/DebugTrace-cpp
[submodule "armadillo-code"]
path = armadillo-code
url = https://gitlab.com/conradsnicta/armadillo-code

View File

@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 3.12)
cmake_minimum_required (VERSION 3.16)
# To allow linking to static libs from other directories
cmake_policy(SET CMP0079 NEW)
@ -6,28 +6,25 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/lasp/cma
include("BuildType")
# This is used for code completion in vim
set(CMAKE_EXPORT_COMPILE_COMMANDS=ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project(LASP LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED)
set(CMAKE_C_STANDARD 11)
# 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(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(PYBIND11_FINDPYTHON ON)
find_package(Python3 COMPONENTS Interpreter Development REQUIRED)
include_directories(${Python3_INCLUDE_DIRS})
find_package(pybind11 CONFIG REQUIRED)
# Required for PYBIND11
set(POSITION_INDEPENDENT_CODE True)
@ -44,11 +41,12 @@ else()
set(LASP_THREADING_LIBRARIES "")
endif()
if(LASP_USE_BLAS)
# link openblas
set(BLA_VENDOR OpenBLAS)
find_package(BLAS REQUIRED)
endif()
# Armadillo
include_directories(SYSTEM armadillo-code/include)
# Reasonable maximum to the nfft size, at 48kHz this is 700s of data...
@ -67,6 +65,7 @@ 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")
@ -98,7 +97,6 @@ else() # Linux compile
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.
@ -108,21 +106,12 @@ endif()
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 \
-fdata-sections -ffunction-sections -fomit-frame-pointer -finline-functions")
# ############################# End compilation flags
# 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)
@ -131,10 +120,10 @@ if(LASP_FFTPACK_BACKEND)
)
endif()
include_directories(lasp/c)
include_directories(STL-Threadsafe/include)
include_directories(gsl-lite/include)
include_directories(DebugTrace-cpp/include)
add_subdirectory(lasp)
add_subdirectory(gsl-lite)
add_subdirectory(test)
@ -148,21 +137,21 @@ set(DEPS "${CMAKE_CURRENT_SOURCE_DIR}/*.py"
# "lasp_device_lib")
)
set(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/build/timestamp")
# 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})
# # 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})
# add_custom_target(target ALL DEPENDS ${OUTPUT})
if(DEFINED INSTALL_DEBUG)
set(EXTRA_SETUP_ARG --user -e)
else()
set(EXTRA_SETUP_ARG "")
endif()
# 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} .)")
# install(CODE "execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip install ${EXTRA_SETUP_ARG} .)")

1
armadillo-code Submodule

@ -0,0 +1 @@
Subproject commit b5727433d342afca53aca0ee16ecf1caaa661821

View File

@ -1,14 +1,3 @@
configure_file(config.pxi.in config.pxi)
# This is used for code completion in vim
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
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)
add_subdirectory(dsp)

View File

@ -2,7 +2,6 @@
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

View File

@ -1,30 +1,29 @@
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})
# 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_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

@ -10,6 +10,9 @@
#define LASP_FFT_H
#include "lasp_types.h"
#include "lasp_mat.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Load wisdom data from a NULL-terminated character string. Note that at this
@ -99,5 +102,8 @@ void Fft_ifft(const Fft* fft,const cmat* freqdata,dmat* timedata);
*/
void Fft_free(Fft* fft);
#ifdef __cplusplus
}
#endif
#endif // LASP_FFT_H
//////////////////////////////////////////////////////////////////////

View File

@ -8,6 +8,10 @@
#pragma once
#ifndef LASP_MATH_RAW_H
#define LASP_MATH_RAW_H
#ifdef __cplusplus
extern "C" {
#endif
#include "lasp_config.h"
#include "lasp_assert.h"
#include "lasp_tracer.h"
@ -23,40 +27,6 @@
#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)
@ -502,6 +472,9 @@ static inline void c_conj_inplace(c res[],us size) {
}
#endif // LASP_USE_BLAS
}
#ifdef __cplusplus
}
#endif
#endif // LASP_MATH_RAW_H
//////////////////////////////////////////////////////////////////////

View File

@ -3,16 +3,23 @@
// Author: J.A. de Jong - ASCEE
//
// Description: Implemententation of a function to determine the number
// of processors.
// of processors. Useful for spreading out computational load over multiple
// cores.
//////////////////////////////////////////////////////////////////////
#pragma once
#ifndef LASP_NPROCS_H
#define LASP_NPROCS_H
#include "lasp_types.h"
#ifdef __cplusplus
extern C {
#endif
/**
* @return The number of SMP processors
*/
us getNumberOfProcs();
#ifdef __cplusplus
}
#endif
#endif // LASP_NPROCS_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,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,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,13 +1,23 @@
file(GLOB device_files *.cpp)
# set(cpp_daq_linklibs ${LASP_THREADING_LIBRARIES})
add_library(lasp_device_lib OBJECT
lasp_daq.cpp
lasp_daqconfig.cpp
lasp_daqdata.cpp
lasp_deviceinfo.cpp
lasp_rtaudiodaq.cpp
lasp_streammgr.cpp
lasp_uldaq.cpp
)
# if(win32)
# list(APPEND lasp_device_linklibs python${python_version_windll})
# endif(win32)
target_include_directories(lasp_device_lib PUBLIC ../dsp)
target_include_directories(lasp_device_lib PUBLIC ../c)
if(LASP_HAS_ULDAQ)
target_link_libraries(lasp_device_lib uldaq)
endif()
if(LASP_HAS_RTAUDIO)
target_link_libraries(lasp_device_lib rtaudio)
endif()
target_link_libraries(lasp_device_lib lasp_dsp_lib)
add_library(lasp_device_lib OBJECT ${device_files})
# pybind11_add_module(lasp_device lasp_pydevicepybid.cpp)
# target_link_libraries(lasp_device PRIVATE lasp_device_lib)
pybind11_add_module(lasp_device lasp_devicepybind.cpp)
target_link_libraries(lasp_device PRIVATE lasp_device_lib)

View File

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

View File

@ -70,6 +70,9 @@ double Daq::samplerate() const {
DataTypeDescriptor::DataType Daq::dataType() const {
return availableDataTypes.at(dataTypeIndex);
}
DataTypeDescriptor Daq::dtypeDescr() const {
return dtype_map.at(dataType());
}
double Daq::inputRangeForChannel(us ch) const {
if (!(ch < ninchannels)) {

View File

@ -1,11 +1,29 @@
#pragma once
#include <memory>
#include "lasp_config.h"
#include "lasp_daqdata.h"
#include "lasp_deviceinfo.h"
#include "lasp_types.h"
#include <functional>
#include <gsl/gsl-lite.hpp>
#include <memory>
/**
* @brief Information regarding a stream.
*/
class StreamStatus {
public:
bool isRunning = false;
bool error = false;
std::string error_msg{};
/**
* @brief Returns true if everything is OK with a certain stream and the
* stream is running.
*
* @return as described above.
*/
bool runningOK() const { return isRunning && !error; }
};
/**
* @brief Callback of DAQ for input data. Callback should return
@ -48,7 +66,7 @@ public:
* data is presented, the function is called with a nullptr as argument.
*/
virtual void start(std::optional<InDaqCallback> inCallback,
std::optional<OutDaqCallback> outCallback);
std::optional<OutDaqCallback> outCallback) = 0;
/**
* @brief Stop the Daq device. Throws an exception if the device is not
@ -63,7 +81,7 @@ public:
*/
virtual bool isRunning() const = 0;
virtual ~Daq(){};
virtual ~Daq() = default;
/**
* @brief Returns the number of enabled input channels
*
@ -118,6 +136,14 @@ public:
us framesPerBlock() const {
return availableFramesPerBlock.at(framesPerBlockIndex);
}
/**
* @brief Get stream status corresponding to current DAQ.
*
* @return StreamStatus object.
*/
virtual StreamStatus getStreamStatus() const = 0;
/**
* @brief Whether the device runs in duplex mode (both input and output), or
* false if only input / only output.

View File

@ -1,8 +1,8 @@
#pragma once
#include "lasp_config.h"
#include "lasp_types.h"
#include <map>
#include <vector>
#include "lasp_config.h"
#include "lasp_types.h"
using std::string;
using std::to_string;
@ -200,7 +200,7 @@ public:
* @param deviceinfo DeviceInfo structure
*/
DaqConfiguration(const DeviceInfo &DeviceInfo);
DaqConfiguration() = delete;
DaqConfiguration();
/**
* @brief Check to see whether the DAQ configuration matches with the device.

View File

@ -1,10 +1,10 @@
#pragma once
#include "lasp_daqconfig.h"
#include "lasp_types.h"
#include <functional>
#include <gsl/gsl-lite.hpp>
#include <memory>
#include <optional>
#include "lasp_daqconfig.h"
#include "lasp_types.h"
/**
* @brief Data coming from / going to DAQ. **Non-interleaved format**, which

View File

@ -1,31 +1,33 @@
#if 0
#include "lasp_devicepybind.h"
#include <algorithm>
#include <assert.h>
#include <numpy/arrayobject.h>
#include <pybind11/numpy.h>
#include <stdint.h>
#include <thread>
#include <iostream>
#include "lasp_daqconfig.h"
#include "lasp_deviceinfo.h"
using std::cerr;
namespace py = pybind11;
PYBIND11_MODULE(lasp_daq, m) {
PYBIND11_MODULE(lasp_device, m) {
m.doc() = "Lasp DAQ interface";
m.doc() = "Lasp device interface";
/// DataType
py::class_<DataType> datatype(m, "DataType");
datatype.def_readonly("name", &DataType::name);
datatype.def_readonly("sw", &DataType::sw);
datatype.def_readonly("is_floating", &DataType::is_floating);
py::class_<DataTypeDescriptor> dtype_desc(m, "DataTypeDescriptor");
dtype_desc.def_readonly("name", &DataTypeDescriptor::name);
dtype_desc.def_readonly("sw", &DataTypeDescriptor::sw);
dtype_desc.def_readonly("is_floating", &DataTypeDescriptor::is_floating);
py::enum_<DataTypeDescriptor::DataType>(dtype_desc, "DataType")
.value("dtype_fl32", DataTypeDescriptor::DataType::dtype_fl32)
.value("dtype_fl64", DataTypeDescriptor::DataType::dtype_fl64)
.value("dtype_int8", DataTypeDescriptor::DataType::dtype_int8)
.value("dtype_int16", DataTypeDescriptor::DataType::dtype_int16)
.value("dtype_int32", DataTypeDescriptor::DataType::dtype_int32).export_values();
dtype_desc.def_readonly("dtype", &DataTypeDescriptor::dtype);
m.attr("dtype_fl32") = dtype_fl32;
m.attr("dtype_fl64") = dtype_fl64;
m.attr("dtype_int8") = dtype_int8;
m.attr("dtype_int16") = dtype_int16;
m.attr("dtype_int24") = dtype_int24;
m.attr("dtype_int32") = dtype_int32;
/// DaqApi
py::class_<DaqApi> daqapi(m, "DaqApi");
@ -105,132 +107,5 @@ PYBIND11_MODULE(lasp_daq, m) {
daqconfig.def("match", &DaqConfiguration::match);
/// Daq
py::class_<PyDaq> daq(m, "Daq");
daq.def(py::init<DeviceInfo,DaqConfiguration>());
/* daq.def("start */
/* daqconfig.def(py::init< */
}
const d QUEUE_BUFFER_TIME = 0.1;
PyDaq::PyDaq(const DeviceInfo &devinfo, const DaqConfiguration &config) {
_daq = Daq::createDaq(devinfo, config);
_stopThread = false;
_threadReady = false;
}
PyDaq::~PyDaq() {
if (_daqThread) {
}
}
/* py::array arrayFromBuffer(void *buf, us n_rows, us n_cols, DataType dtype) { */
/* // https://github.com/pybind/pybind11/issues/27 */
/* py::size_t dims[] = {n_rows, n_cols}; */
/* py::buffer_info; */
/* switch (dtype) { */
/* case dtype_int8: */
/* break; */
/* case dtype_int16: */
/* break; */
/* case dtype_int24: */
/* break; */
/* case dtype_fl32: */
/* break; */
/* case dtype_fl64: */
/* break; */
/* } */
/* /1* buffer_info = py::buffer_info( *1/ */
/* /1* py::buffer_info(buf, sizeof(float), *1/ */
/* /1* py::format_descriptor<float>::format(), *1/ */
/* /1* 2, *1/ */
/* /1* dims, (float*) buf));; *1/ */
/* /1* } *1/ */
/* py::array res; */
/* } */
void PyDaq::start(py::function audioCallback) {
// Number of input channels, including monitor channel
const us neninchannels = _daq->neninchannels(true);
const us nenoutchannels = _daq->nenoutchannels();
const bool input = neninchannels > 0;
const bool output = nenoutchannels > 0;
SafeQueue<void *> *inQueue = input ? &_inQueue : nullptr;
SafeQueue<void *> *outQueue = output ? &_outQueue : nullptr;
_daq->start(inQueue, outQueue);
_audioCallback = audioCallback;
_daqThread = new std::thread(&PyDaq::threadFcn, this);
// Wait unitil thread is ready before returning
do {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
} while (!_threadReady);
}
void PyDaq::stop() {
_daq->stop();
_stopThread = true;
_threadReady = false;
_daqThread->join();
delete _daqThread;
_daqThread = nullptr;
}
void PyDaq::threadFcn() {
assert(_daq);
void *inbuffer = nullptr;
void *outbuffer = nullptr;
const double sleeptime = _daq->framesPerBlock() / (8 * _daq->samplerate());
// Sleep time in microseconds
const us sleeptime_us = (us)(sleeptime * 1e6);
const us nblocks_buffer = (us)std::max<d>(
1, QUEUE_BUFFER_TIME * _daq->samplerate() / _daq->framesPerBlock());
auto descr = _daq->dtypeDescr();
const us nBytesPerChan = _daq->framesPerBlock() * descr.sw;
_threadReady = true;
// Number of input channels, including monitor channel
const us neninchannels = _daq->neninchannels(true);
const us nenoutchannels = _daq->nenoutchannels();
const bool input = neninchannels > 0;
const bool output = nenoutchannels > 0;
cerr << "Check if it is required to call import_array()" << endl;
/* { */
/* py::gil_scoped_acquire aq; */
/* import_array(void); */
/* } */
_threadReady = true;
if (output) {
for (us i = 0; i < nblocks_buffer; i++) {
outbuffer = new uint8_t[nBytesPerChan * nenoutchannels];
memset(outbuffer, 0, nBytesPerChan * nenoutchannels);
_outQueue.enqueue(outbuffer);
}
outbuffer = nullptr;
}
while (!_stopThread) {
if (output) {
py::gil_scoped_acquire gil;
while (_outQueue.size() < nblocks_buffer) {
/* py::array( */
}
}
} // End of while loop, stopthread
}
#endif

View File

@ -1,44 +0,0 @@
#pragma once
#include "lasp_daq.h"
#include <atomic>
#include <vector>
#include <pybind11/pybind11.h>
namespace py = pybind11;
namespace std {
class thread;
};
class PyDaq {
Daq *_daq = nullptr;
std::thread *_daqThread = nullptr;
SafeQueue<void *> _inQueue;
SafeQueue<void *> _outQueue;
py::function _audioCallback;
std::atomic<bool> _stopThread;
std::atomic<bool> _threadReady;
public:
PyDaq(const DeviceInfo &devinfo, const DaqConfiguration &config);
~PyDaq();
/**
* @brief Start the Daq data acquisition
* @param audioCallback Python callback which is called with in and/or out
data of the daq
* as arguments
*/
void start(py::function audioCallback);
void stop();
bool isRunning() { return _daq->isRunning(); }
static std::vector<DeviceInfo> getDeviceInfo();
const Daq &daq() const { return *_daq; }
private:
void threadFcn();
};

View File

@ -300,14 +300,10 @@ public:
return 0;
}
~RtAudioDaq() {
if (isRunning()) {
stop();
}
if (rtaudio.isStreamOpen()) {
rtaudio.closeStream();
}
}
// RtAudio documentation says: if a stream is open, it will be stopped and
// closed automatically on deletion. Therefore the destructor here is a
// default one.
~RtAudioDaq() = default;
};
std::unique_ptr<Daq> createRtAudioDevice(const DeviceInfo &devinfo,

View File

@ -0,0 +1,151 @@
#include <assert.h>
#include <algorithm>
#include "lasp_streammgr.h"
InDataHandler::InDataHandler(StreamMgr &mgr) : _mgr(mgr) {
mgr.addInDataHandler(*this);
}
InDataHandler::~InDataHandler() { _mgr.removeInDataHandler(*this); }
StreamMgr &StreamMgr::getInstance() {
static StreamMgr mgr;
return mgr;
}
bool StreamMgr::inCallback(const DaqData &data) {
std::scoped_lock<std::mutex> lck(_inDataHandler_mtx);
for (auto &handler : _inDataHandlers) {
bool res = handler->inCallback(data);
if (!res)
return false;
}
return true;
}
/**
* @brief Converts from double precision floating point to output signal in
* non-interleaving format.
*
* @tparam T
* @param data
* @param signal
*
* @return
*/
template <typename T> bool fillData(DaqData &data, const vd &signal) {
assert(data.nframes == signal.size());
T* res = reinterpret_cast<T*>(data.raw_ptr());
if (std::is_floating_point<T>()) {
for (us ch = 0; ch < data.nchannels; ch++) {
for (us frame = 0; frame < data.nframes; frame++) {
res[ch * data.nframes + frame ] = signal[frame];
}
}
} else {
for (us ch = 0; ch < data.nchannels; ch++) {
for (us frame = 0; frame < data.nframes; frame++) {
res[ch * data.nframes + frame] =
(signal[frame] * std::numeric_limits<T>::max());
}
}
}
return true;
}
void StreamMgr::setSiggen(std::shared_ptr<Siggen> siggen) {
std::scoped_lock<std::mutex> lck(_siggen_mtx);
_siggen = siggen;
// If not set to nullptr, and a stream is running, we update the signal
// generator by resetting it.
if(isStreamRunning(StreamType::outputType) && siggen) {
const Daq* daq = getDaq(StreamType::outputType);
assert(daq != nullptr);
// Reset the signal generator.
_siggen->reset(daq->samplerate());
}
}
bool StreamMgr::outCallback(DaqData& data) {
std::scoped_lock<std::mutex> lck(_siggen_mtx);
if(_siggen) {
vd signal = _siggen->genSignal(data.nframes);
switch (data.dtype) {
case (DataTypeDescriptor::DataType::dtype_fl32):
fillData<float>(data, signal);
break;
case (DataTypeDescriptor::DataType::dtype_fl64):
fillData<double>(data, signal);
break;
case (DataTypeDescriptor::DataType::dtype_int8):
fillData<int8_t>(data, signal);
break;
case (DataTypeDescriptor::DataType::dtype_int16):
fillData<int16_t>(data, signal);
break;
case (DataTypeDescriptor::DataType::dtype_int32):
fillData<int32_t>(data, signal);
break;
}
} else {
// Set all values to 0.
std::fill(data.raw_ptr(), data.raw_ptr()+data.size_bytes(), 0);
}
return true;
}
StreamMgr::~StreamMgr() { stopAllStreams(); }
void StreamMgr::stopAllStreams() { _streams.clear(); }
void StreamMgr::startStream(const DeviceInfo &devinfo,
const DaqConfiguration &config) {
bool isInput = std::count(config.eninchannels.begin(),
config.eninchannels.end(), true) > 0;
bool isOutput = std::count(config.enoutchannels.begin(),
config.enoutchannels.end(), true) > 0;
bool isDuplex = isInput && isOutput;
if (!isInput && !isOutput) {
throw std::runtime_error(
"Neither input, nor output channels enabled for stream");
}
StreamType streamtype;
if (isDuplex) {
if (_streams.size()) {
throw std::runtime_error("Error: a stream is already running. Please "
"first stop existing stream");
}
streamtype = StreamType::duplex;
} else if (isInput) {
if (_streams[StreamType::input]) {
throw std::runtime_error("Error: input stream is already running. Please "
"first stop existing stream");
}
streamtype = StreamType::input;
} else if (isOutput) {
if (_streams[StreamType::output]) {
throw std::runtime_error(
"Error: output stream is already running. Please "
"first stop existing stream");
}
streamtype = StreamType::input;
}
_streams[streamtype] = Daq::createDaq(devinfo, config);
}
void StreamMgr::addInDataHandler(InDataHandler &handler) {
std::scoped_lock<std::mutex> lck(_inDataHandler_mtx);
_inDataHandlers.push_back(&handler);
}
void StreamMgr::removeInDataHandler(InDataHandler &handler) {
std::scoped_lock<std::mutex> lck(_inDataHandler_mtx);
_inDataHandlers.remove(&handler);
}

View File

@ -0,0 +1,187 @@
#pragma once
#include "lasp_daq.h"
#include "lasp_siggen.h"
#include <list>
#include <memory>
#include <mutex>
class StreamMgr;
/**
* @brief Information regarding a stream.
*/
class StreamStatus {
public:
bool isRunning = false;
bool error = false;
std::string error_msg{};
/**
* @brief Returns true if everything is OK with a certain stream and the
* stream is running.
*
* @return as described above.
*/
bool runningOK() const { return isRunning && !error; }
};
class InDataHandler {
protected:
StreamMgr &_mgr;
public:
virtual ~InDataHandler();
/**
* @brief When constructed, the handler is added to the stream manager, which
* will call the handlers's inCallback() until the point of destruction.
*
* @param mgr Stream manager.
*/
InDataHandler(StreamMgr &mgr);
/**
* @brief This function is called when input data from a DAQ is available.
*
* @param daqdata Input data from DAQ
*
* @return true if no error. False to stop the stream from running.
*/
virtual bool inCallback(const DaqData &daqdata) = 0;
};
/**
* @brief Stream manager. Used to manage the input and output streams.
* Implemented as a singleton: only one stream manager can be in existance for
* a given program.
*/
class StreamMgr {
enum class StreamType : us {
/**
* @brief Input-only stream
*/
input = 1 << 0,
/**
* @brief Output-only stream
*/
output = 1 << 1,
/**
* @brief Duplex stream (does both input and output)
*/
duplex = 1 << 2,
/**
* @brief Either input, or duplex
*/
inputType = 1 << 3,
/**
* @brief Either output, or duplex
*/
outputType = 1 << 4
};
/**
* @brief Storage for streams
*/
std::map<StreamType, std::unique_ptr<Daq>> _streams;
/**
* @brief All indata handlers are called when input data is available. Note
* that they can be called from different threads and should take care of
* thread-safety.
*/
std::list<InDataHandler *> _inDataHandlers;
std::mutex _inDataHandler_mtx;
/**
* @brief Signal generator in use to generate output data. Currently
* implemented as to generate the same data for all output channels.
*/
std::shared_ptr<Siggen> _siggen;
std::mutex _siggen_mtx;
StreamMgr();
friend class InDataHandler;
friend class Siggen;
public:
StreamMgr(const StreamMgr &) = delete;
StreamMgr &operator=(const StreamMgr &) = delete;
~StreamMgr();
/**
* @brief Get access to stream manager instance
*
* @return Reference to stream manager.
*/
static StreamMgr &getInstance();
/**
* @brief Start a stream.
*
* @param devinfo Device information of device
* @param config Configuration of device
*/
void startStream(const DeviceInfo &devinfo, const DaqConfiguration &config);
/**
* @brief Check if a certain stream is running. If running with no errors, it
* returns true. If an error occured, or the stream is not running, it gives
* false.
*
* @param type The type of stream to check for.
*/
bool isStreamRunningOK(const StreamType type) const {
return getStreamStatus(type).runningOK();
}
/**
* @brief Get the streamstatus object corresponding to a given stream.
*
* @param type Type of stream, input, inputType, etc.
*
* @return status object.
*/
StreamStatus getStreamStatus(const StreamType type) const;
/**
* @brief Get DAQ pointer for a given stream. Gives a nullptr if stream is
* not running.
*
* @param type The stream type to get a DAQ ptr for.
*
* @return Pointer to DAQ
*/
const Daq *getDaq(StreamType type) const;
/**
* @brief Stop stream of given type (input / output/ duplex);
*
* @param stype The stream type to stop.
*/
void stopStream(StreamType stype);
/**
* @brief Stop and delete all streams. Also called on destruction of the
* StreamMgr.
*/
void stopAllStreams();
private:
bool inCallback(const DaqData &data);
bool outCallback(DaqData &data);
void addInDataHandler(InDataHandler &handler);
void removeInDataHandler(InDataHandler &handler);
/**
* @brief Set active signal generator for output streams. Only one `Siggen'
* is active at the same time. Siggen controls its own data race protection
* using a mutex.
*
* @param s Siggen pointer
*/
void setSiggen(std::shared_ptr<Siggen> s);
};

View File

@ -8,7 +8,6 @@
#include <iostream>
#include <memory>
#include <mutex>
#include <span>
#include <stdexcept>
#include <thread>
#include <uldaq.h>

21
lasp/dsp/CMakeLists.txt Normal file
View File

@ -0,0 +1,21 @@
configure_file(lasp_config.h.in lasp_config.h)
set(lasp_dsp_files
lasp_filter.cpp
lasp_siggen.cpp
lasp_siggen_impl.cpp
lasp_window.cpp
)
SET_SOURCE_FILES_PROPERTIES(${lasp_dsp_files} PROPERTIES COMPILE_FLAGS
"-DARMA_DONT_USE_WRAPPER")
add_library(lasp_dsp_lib STATIC ${lasp_dsp_files})
pybind11_add_module(lasp_dsp lasp_dsp_pybind.cpp)
target_link_libraries(lasp_dsp PRIVATE lasp_dsp_lib ${BLAS_LIBRARIES})
target_include_directories(lasp_dsp_lib PUBLIC ${CMAKE_CURRENT_BINARY_DIR})

1
lasp/dsp/__init__.py Normal file
View File

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

View File

@ -0,0 +1,20 @@
#include "lasp_window.h"
#include <iostream>
#include <pybind11/pybind11.h>
using std::cerr;
namespace py = pybind11;
PYBIND11_MODULE(lasp_dsp, m) {
py::class_<Window> w(m, "Window");
py::enum_<Window::WindowType>(w, "WindowType")
.value("Hann", Window::WindowType::Hann)
.value("Hamming", Window::WindowType::Hamming)
.value("Bartlett", Window::WindowType::Bartlett)
.value("Blackman", Window::WindowType::Bartlett)
.value("Rectangular", Window::WindowType::Rectangular)
.export_values();
}

0
lasp/dsp/lasp_filter.cpp Normal file
View File

14
lasp/dsp/lasp_filter.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#include <vector>
#include "lasp_types.h"
#include "lasp_mathtypes.h"
/**
* @brief Filter used to pre-filter a double-precision floating point data
* stream.
*/
class Filter {
public:
virtual void filter(const vd &inout) = 0;
virtual ~Filter() = 0;
};

45
lasp/dsp/lasp_mathtypes.h Normal file
View File

@ -0,0 +1,45 @@
#pragma once
#include <armadillo>
#include "lasp_types.h"
#include <cmath>
#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
using vd = arma::Col<d>;
using dmat = arma::Mat<d>;
const d number_pi = arma::datum::pi;

42
lasp/dsp/lasp_siggen.cpp Normal file
View File

@ -0,0 +1,42 @@
#include "lasp_siggen.h"
#include "lasp_mathtypes.h"
#include <cassert>
#include <type_traits>
inline d level_amp(d level_dB){
return pow(10, level_dB/20);
}
using mutexlock = std::scoped_lock<std::mutex>;
vd Siggen::genSignal(const us nframes) {
mutexlock lck(_mtx);
vd signal(nframes, arma::fill::value(_dc_offset));
if (!_muted) {
vd signal_dynamic = _level_linear*genSignalUnscaled(nframes);
if(_filter) {
_filter->filter(signal_dynamic);
}
signal += signal_dynamic;
}
return signal;
}
void Siggen::setFilter(std::shared_ptr<Filter>& filter) {
mutexlock lck(_mtx);
_filter = filter;
}
void Siggen::setDCOffset(const d offset) {
mutexlock lck(_mtx);
_dc_offset = offset;
}
void Siggen::setLevel(const d level,bool dB) {
mutexlock lck(_mtx);
_level_linear = dB ? level_amp(level) : level;
}
void Siggen::reset(const d newFs) {
mutexlock lck(_mtx);
fs = newFs;
resetImpl();
}

77
lasp/dsp/lasp_siggen.h Normal file
View File

@ -0,0 +1,77 @@
#pragma once
#include <memory>
#include "lasp_types.h"
#include "lasp_mathtypes.h"
#include "lasp_filter.h"
class StreamMgr;
class DaqData;
/**
* @brief Signal generation base class
*/
class Siggen {
private:
std::shared_ptr<Filter> _filter;
d _dc_offset = 0, _level_linear = 1;
bool _muted = false;
std::mutex _mtx;
protected:
d fs = -1;
virtual void resetImpl() = 0;
virtual vd genSignalUnscaled(const us nframes) = 0;
public:
virtual ~Siggen() = default;
/**
* @brief Set a post-filter on the signal. For example to EQ the signal, or
* otherwise to shape the spectrum.
*
* @param f The filter to install.
*/
void setFilter(std::shared_ptr<Filter>& f);
/**
* @brief Set a linear DC offset value to the signal
*
* @param offset
*/
void setDCOffset(d offset);
/**
* @brief Mute the signal. Passes through the DC offset.
*
* @param mute if tre
*/
void setMute(bool mute = true) { _muted = mute; }
/**
* @brief Set the level of the signal generator
*
* @param level The new level. If dB == true, it is treated as a level, and
* pow(10, level/20) is installed as the linear gain.
* @param dB if false, level is treated as linear gain value.
*/
void setLevel(const d level, bool dB=true);
/**
* @brief Reset the signal generator. Should be called whenever the output is
* based on a different sampling frequency. Note that derived classes from
* this class should call the base class!
*
* @param newFs New sampling frequency to use.
*/
void reset(const d newFs);
/**
* @brief Called whenever the implementation needs to create new samples.
*
* @param nframes
*
* @return Array of samples with length nframes
*/
vd genSignal(const us nframes);
};

View File

@ -0,0 +1,245 @@
// lasp_siggen_impl.cpp
//
// Author: J.A. de Jong -ASCEE
//
// Description:
// Signal generators implementation
//////////////////////////////////////////////////////////////////////
/* #define TRACERPLUS (-5) */
#include "lasp_siggen_impl.h"
#include "debugtrace.hpp"
#include "lasp_mathtypes.h"
DEBUGTRACE_VARIABLES;
/** The fixed number of Newton iterations t.b.d. for tuning the sweep start and
* stop frequency in logarithmic sweeps */
#define NITER_NEWTON 20
Noise::Noise(){DEBUGTRACE_ENTER}
vd Noise::genSignalUnscaled(us nframes) {
return arma::randn<vd>(nframes);
}
void Noise::resetImpl() {}
Sine::Sine(const d freq) : omg(2 * arma::datum::pi * freq) {}
vd Sine::genSignalUnscaled(const us nframes) {
const d pi = arma::datum::pi;
vd phase_vec =
arma::linspace(phase, phase + omg * (nframes - 1) / fs, nframes);
phase += omg * nframes / fs;
while (phase > 2 * arma::datum::pi) {
phase -= 2 * pi;
}
return arma::sin(phase_vec);
}
vd Periodic::genSignalUnscaled(const us nframes) {
us signal_idx = 0;
vd res(nframes);
while (signal_idx < nframes) {
res[signal_idx] = _signal[_cur_pos];
_cur_pos++;
_cur_pos %= _signal.size();
}
return res;
}
Sweep::Sweep(const d fl, const d fu, const d Ts, const d Tq, const us flags)
: fl_(fl), fu_(fu), Ts(Ts), Tq(Tq), flags(flags) {
if (fl <= 0 || fu < fl || Ts <= 0) {
throw std::runtime_error("Invalid sweep parameters");
}
if ((flags & ForwardSweep) && (flags & BackwardSweep)) {
throw std::runtime_error(
"Both forward and backward sweep flag set. Please only set either one "
"or none for a continuous sweep");
}
}
void Sweep::resetImpl() {
_cur_pos = 0;
bool forward_sweep = flags & ForwardSweep;
bool backward_sweep = flags & BackwardSweep;
const d Dt = 1 / fs; // Deltat
// Estimate N, the number of samples in the sweep part (non-quiescent part):
const us Ns = (us)(Ts * fs);
const us Nq = (us)(Tq * fs);
const us N = Ns + Nq;
_signal = vd(N, arma::fill::zeros);
index = 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_;
}
d phase = 0;
/* Linear sweep */
if (flags & LinearSweep) {
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 < Ns; n++) {
_signal(n) = d_sin(phase);
d fn = fl + ((d)n) / N * (fu + eps - fl);
phase += 2 * arma::datum::pi * Dt * fn;
}
} else {
/* Continous sweep */
/* TRACE(15, "continuous sweep"); */
/* iVARTRACE(17, N); */
/* dVARTRACE(17, fl); */
/* dVARTRACE(17, fu); */
const us Nf = Ns / 2;
const us Nb = Ns - 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); */
d phase = 0;
for (us n = 0; n <= Ns; n++) {
/* iVARTRACE(17, n); */
if (n < N) {
_signal[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;
}
/* This should be a very small number!! */
/* dVARTRACE(15, phase); */
}
} else if (flags & LogSweep) {
DEBUGTRACE_PRINT("Exponential sweep");
if (forward_sweep || backward_sweep) {
/* Forward or backward sweep */
DEBUGTRACE_PRINT("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;
}
DEBUGTRACE_PRINT(K);
DEBUGTRACE_PRINT(K1);
DEBUGTRACE_PRINT(k);
DEBUGTRACE_PRINT(E);
for (us n = 0; n < Ns; n++) {
_signal[n] = d_sin(phase);
d fn = fl * d_pow(k, ((d)n) / N);
phase += 2 * number_pi * Dt * fn;
}
} else {
DEBUGTRACE_PRINT("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;
DEBUGTRACE_PRINT(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;
}
DEBUGTRACE_PRINT(K);
DEBUGTRACE_PRINT(K1);
DEBUGTRACE_PRINT(k);
DEBUGTRACE_PRINT(E);
for (us n = 0; n <= Ns; n++) {
/* iVARTRACE(17, n); */
if (n < Ns) {
_signal[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); */
}
/* This should be a very small number!! */
DEBUGTRACE_PRINT(phase);
}
}
}

View File

@ -0,0 +1,79 @@
#pragma once
#include "lasp_siggen.h"
#include "lasp_types.h"
class Noise : public Siggen {
d level_linear;
virtual vd genSignalUnscaled(const us nframes) override;
void resetImpl() override;
public:
/**
* @brief Constructs a noise generator. If no filter is used, the output will
* be white noise. By default, the output will be standard deviation = 1
* noise, which clips the output for standard audio devices, so make sure the
* level is set properly.
*/
Noise();
~Noise() = default;
};
class Sine : public Siggen {
d phase = 0;
d omg;
public:
/**
* @brief Create a sine wave generator
*
* @param freq_Hz
* @param level_dB
*/
Sine(const d freq_Hz);
~Sine() = default;
virtual vd genSignalUnscaled(const us nframes) override;
void setFreq(const d newFreq);
};
class Periodic: public Siggen {
protected:
vd _signal { 1, arma::fill::zeros};
us _cur_pos = 0;
public:
virtual vd genSignalUnscaled(const us nframes) override;
~Periodic() = default;
};
class Sweep : public Periodic {
d fl_, fu_, Ts, Tq;
us index;
us flags;
static constexpr int ForwardSweep = 1 << 0;
static constexpr int BackwardSweep = 1 << 1;
static constexpr int LinearSweep = 1 << 2;
static constexpr int LogSweep = 1 << 3;
void resetImpl() override;
public:
/**
* Create a sweep signal
*
* @param[in] fl: Lower frequency [Hz]
* @param[in] fu: 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]
*/
Sweep(const d fl, const d fu, const d Ts, const d Tq,
const us sweep_flags);
~Sweep() = default;
};

View File

@ -51,14 +51,6 @@ typedef double complex c;
#endif
/// I need these numbers so often, that they can be in the global
/// namespace.
#define LASP_SUCCESS 0
#define LASP_INTERRUPTED (-3)
#define LASP_MALLOC_FAILED (-1)
#define LASP_FAILURE (-2)
#endif // LASP_TYPES_H
//////////////////////////////////////////////////////////////////////

57
lasp/dsp/lasp_window.cpp Normal file
View File

@ -0,0 +1,57 @@
// lasp_window.cpp
//
// Author: J.A. de Jong - ASCEE
//
#include "lasp_window.h"
// Safe some typing. Linspace form 0 up to (and NOT including N).
#define lin0N arma::linspace(0, N - 1, N)
vd Window::hann(const us N) {
return arma::pow(arma::sin(number_pi * lin0N), 2);
}
vd Window::hamming(const us N) {
d alpha = 25.0 / 46.0;
return alpha - (1 - alpha) * arma::cos(2 * number_pi * lin0N / (N - 1));
}
vd Window::blackman(const us N) {
d a0 = 7938. / 18608.;
d a1 = 9240. / 18608.;
d a2 = 1430. / 18608.;
return a0 - a1 * d_cos(2 * number_pi * lin0N / (N - 1)) +
a2 * d_cos(4 * number_pi * lin0N / (N - 1));
}
vd Window::rectangular(const us N) { return arma::ones(N); }
vd Window::bartlett(const us N) {
return 1 - arma::abs(2 * (lin0N - (N - 1) / 2.) / (N - 1));
}
vd Window::create(const WindowType w, const us N) {
switch (w) {
case WindowType::Hann: {
return hann(N);
break;
}
case WindowType::Hamming: {
return hamming(N);
break;
}
case WindowType::Rectangular: {
return rectangular(N);
break;
}
case WindowType::Bartlett: {
return bartlett(N);
break;
}
case WindowType::Blackman: {
return blackman(N);
break;
}
default:
abort();
break;
}
}

73
lasp/dsp/lasp_window.h Normal file
View File

@ -0,0 +1,73 @@
#pragma once
#include "lasp_mathtypes.h"
/**
* @brief Window (aka taper) functions of a certain type
*/
class Window {
public:
enum class WindowType {
Hann = 0,
Hamming = 1,
Rectangular = 2,
Bartlett = 3,
Blackman = 4,
};
/**
* @brief Dispatcher: create a window based on enum type and len
*
* @param w Window type
* @param len Length of the window (typically, integer power of two).
*
* @return Window vector of values
*/
static vd create(const WindowType w,const us len);
/**
* @brief Hann window
*
* @param len Length of the window (typically, integer power of two).
*
* @return vector of values
*/
static vd hann(const us len);
/**
* @brief Hamming window
*
* @param len Length of the window (typically, integer power of two).
*
* @return vector of values
*/
static vd hamming(const us len);
/**
* @brief Rectangular (boxcar) window.
*
* @param len Length of the window (typically, integer power of two).
*
* @return vector of values
*/
static vd rectangular(const us len);
/**
* @brief Bartlett window.
*
* @param len Length of the window (typically, integer power of two).
*
* @return vector of values
*/
static vd bartlett(const us len);
/**
* @brief Blackman window.
*
* @param len Length of the window (typically, integer power of two).
*
* @return vector of values
*/
static vd blackman(const us len);
};

View File

@ -2,11 +2,11 @@
import shelve, logging, sys, appdirs, os, platform
import numpy as np
from .wrappers import Window as wWindow
from collections import namedtuple
from dataclasses import dataclass
from dataclasses_json import dataclass_json
from enum import Enum, unique, auto
from .dsp import Window as wWindow
"""
Common definitions used throughout the code.
@ -271,11 +271,11 @@ class this_lasp_shelve(Shelve):
@unique
class Window(Enum):
hann = (wWindow.hann, 'Hann')
hamming = (wWindow.hamming, 'Hamming')
rectangular = (wWindow.rectangular, 'Rectangular')
bartlett = (wWindow.bartlett, 'Bartlett')
blackman = (wWindow.blackman, 'Blackman')
hann = (wWindow.Hann, 'Hann')
hamming = (wWindow.Hamming, 'Hamming')
rectangular = (wWindow.Rectangular, 'Rectangular')
bartlett = (wWindow.Bartlett, 'Bartlett')
blackman = (wWindow.Blackman, 'Blackman')
@staticmethod
def fillComboBox(cb):

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)

0
setup.py Normal file → Executable file
View File