From aa3581cf7460600f345a324980c84b8eaea343fb Mon Sep 17 00:00:00 2001 From: "J.A. de Jong - ASCEE" Date: Sat, 28 Dec 2019 16:58:13 +0100 Subject: [PATCH] First part of signal generator implementation. White noise and sine waves are working. --- lasp/c/CMakeLists.txt | 1 + lasp/c/lasp_assert.h | 2 + lasp/c/lasp_siggen.c | 141 ++++++++++++++++++++++++++++++++++++++++++ lasp/c/lasp_siggen.h | 57 +++++++++++++++++ lasp/lasp_avstream.py | 4 +- lasp/lasp_siggen.py | 86 -------------------------- lasp/wrappers.pyx | 51 +++++++++++++++ 7 files changed, 255 insertions(+), 87 deletions(-) create mode 100644 lasp/c/lasp_siggen.c create mode 100644 lasp/c/lasp_siggen.h delete mode 100644 lasp/lasp_siggen.py diff --git a/lasp/c/CMakeLists.txt b/lasp/c/CMakeLists.txt index bf34cea..3551e6b 100644 --- a/lasp/c/CMakeLists.txt +++ b/lasp/c/CMakeLists.txt @@ -14,6 +14,7 @@ add_library(lasp_lib lasp_aps.c lasp_ps.c lasp_mq.c + lasp_siggen.c lasp_worker.c lasp_dfifo.c lasp_filterbank.c diff --git a/lasp/c/lasp_assert.h b/lasp/c/lasp_assert.h index e9584e9..2524c43 100644 --- a/lasp/c/lasp_assert.h +++ b/lasp/c/lasp_assert.h @@ -30,6 +30,8 @@ void DBG_AssertFailedExtImplementation(const char* file, DBG_AssertFailedExtImplementation(__FILE__, __LINE__, assert_string ); \ } +#define assertvalidptr(ptr) dbgassert(ptr,NULLPTRDEREF) + #else // LASP_DEBUG not defined #define dbgassert(assertion, assert_string) diff --git a/lasp/c/lasp_siggen.c b/lasp/c/lasp_siggen.c new file mode 100644 index 0000000..a2e8de1 --- /dev/null +++ b/lasp/c/lasp_siggen.c @@ -0,0 +1,141 @@ +// lasp_siggen.c +// +// Author: J.A. de Jong -ASCEE +// +// Description: +// Signal generator implementation +////////////////////////////////////////////////////////////////////// +#define TRACERPLUS (-5) +#include "lasp_alloc.h" +#include "lasp_assert.h" +#include "lasp_mat.h" + +#define PRIVATE_SIZE 32 + +typedef enum { + SINEWAVE = 0, + WHITENOISE, + PINKNOISE, + SWEEP, + +} SignalType; + + +typedef struct { + SignalType signaltype; + d fs; // Sampling frequency [Hz] + void* private; + + char private_data[PRIVATE_SIZE]; + +} Siggen; + +typedef struct { + d curtime; + d omg; +} SinewaveSpecific; + + +Siggen* Siggen_Sinewave_create(const d fs, const d freq) { + fsTRACE(15); + + Siggen* sinesiggen = a_malloc(sizeof(Siggen)); + sinesiggen->signaltype = SINEWAVE; + sinesiggen->fs = fs; + sinesiggen->private = sinesiggen->private_data; + SinewaveSpecific* sp = (SinewaveSpecific*) sinesiggen->private; + sp->curtime = 0; + sp->omg = 2*number_pi*freq; + + + feTRACE(15); + return sinesiggen; +} +Siggen* Siggen_Whitenoise_create() { + fsTRACE(15); + + Siggen* whitenoise = a_malloc(sizeof(Siggen)); + whitenoise->signaltype = WHITENOISE; + + feTRACE(15); + return whitenoise; +} +void Siggen_free(Siggen* siggen) { + fsTRACE(15); + assertvalidptr(siggen); + + if(siggen->signaltype == SWEEP) { + /* Sweep specific stuff here */ + } + + a_free(siggen); + feTRACE(15); +} +static void Sinewave_genSignal(Siggen* siggen, SinewaveSpecific* sine, vd* samples) { + fsTRACE(15); + 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, sin(omg*curtime)); + curtime = curtime + ts; + } + sine->curtime = curtime; + feTRACE(15); +} + +static d gaussrand() { + static d V1, V2, S; + static int phase = 0; + d X; + + if(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 * log(S) / S); + } else + X = V2 * sqrt(-2 * log(S) / S); + + phase = 1 - phase; + + return X; +} +static void Whitenoise_genSignal(Siggen* siggen, vd* samples) { + for(us i =0; i< samples->n_rows; i++) { + d rn = gaussrand(); + setvecval(samples, i, rn); + } + +} +void Siggen_genSignal(Siggen* siggen,vd* samples) { + fsTRACE(15); + assertvalidptr(siggen); + assert_vx(samples); + d fs = siggen->fs; + + switch(siggen->signaltype) { + case SINEWAVE: + Sinewave_genSignal(siggen, (SinewaveSpecific*) siggen->private, + samples); + + break; + case WHITENOISE: + Whitenoise_genSignal(siggen, samples); + + } + + feTRACE(15); +} + + + +////////////////////////////////////////////////////////////////////// diff --git a/lasp/c/lasp_siggen.h b/lasp/c/lasp_siggen.h new file mode 100644 index 0000000..ab210a8 --- /dev/null +++ b/lasp/c/lasp_siggen.h @@ -0,0 +1,57 @@ +// 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" + +typedef struct {} Siggen; + +/** + * Create a sine wave signal generator + * + * @param[in] fs: Sampling frequency [Hz] + * @param[freq] Sine wave frequency [Hz] + */ +Siggen* Siggen_Sinewave_create(const d fs,const d freq); + +/** + * Create a white noise signal generator + * + * @return Siggen* handle + */ +Siggen* Siggen_Whitenoise_create(); + +/** + * Create a pink (1/f) noise signal generator + * + * @param[in] fs: Sampling frequency [Hz] + * @return Siggen* handle + */ +Siggen* Siggen_Pinknoise_create(const us fs); + +/* Siggen* Siggen_ForwardSweep_create(const d fs,; */ +/* Siggen* Siggen_(const d fs,; */ + +/** + * Obtain a new piece of signal + * + * @param[in] Siggen* Signal generator private data + * @param[out] samples Samples to fill. Vector should be pre-allocated + */ +void Siggen_genSignal(Siggen*,vd* samples); +/** + * Free Siggen data + * + * @param[in] Siggen* Signal generator private data + */ +void Siggen_free(Siggen*); + +#endif LASP_SIGGEN_H +////////////////////////////////////////////////////////////////////// diff --git a/lasp/lasp_avstream.py b/lasp/lasp_avstream.py index 2105655..788ec86 100644 --- a/lasp/lasp_avstream.py +++ b/lasp/lasp_avstream.py @@ -65,6 +65,8 @@ class AvStream: rtaudio_inputparams = None rtaudio_outputparams = None + self.nframes_per_block = 2048 + if self.duplex_mode or avtype == AvType.audio_output: rtaudio_outputparams = {'deviceid': device.index, # TODO: Add option to specify the number of output channels to use @@ -92,7 +94,7 @@ class AvStream: rtaudio_inputparams, # Inputparams self.sampleformat, # Sampleformat self.samplerate, - 2048, # Buffer size in frames + self.nframes_per_block, # Buffer size in frames self._audioCallback) except Exception as e: diff --git a/lasp/lasp_siggen.py b/lasp/lasp_siggen.py deleted file mode 100644 index 084d3d4..0000000 --- a/lasp/lasp_siggen.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python3.6 -# -*- coding: utf-8 -*- -""" -Description: Read data from image stream and record sound at the same time -""" -from .lasp_atomic import Atomic -from threading import Thread, Condition, Lock -from .lasp_avstream import AvType, AvStream -import numpy as np - -class SignalGenerator: - """ - Base class for all signal generator types - """ - - def __init__(self, stream): - - if not (stream.avtype == AvType.audio_output or (stream.avtype == - AvType.audio_input and - stream.duplex_mode)): - raise RuntimeError('Invalid stream type. Does not support audio' - 'output') - - self.stream = stream - self._samplerate = stream.samplerate - stream.addCallback(self.streamCallback, AvType.audio_output) - - def stop(self): - """ - Stop generating the signal - """ - self.stream.removeCallback(self.streamCallback, AvType.audio_output) - - def streamCallback(self, indata, outdata, blockctr): - """ - Callback from AvStream. - """ - signal = self.getSignal(blockctr*outdata.shape[0], outdata.shape[0]) - dtype = self.stream.numpy_dtype - - if dtype == np.float32 or dtype == np.float64: - fac = 1 - else: - bitdepth_fixed = self.stream.sampwidth*8 - fac = 2**(bitdepth_fixed-1) - - outdata[:, :] = (fac*signal).astype(dtype)[:, np.newaxis] - - -class SineGenerator(SignalGenerator): - def __init__(self, stream, freq): - self._omg = 2*freq*np.pi - super().__init__(stream) - - def getSignal(self, startidx, frames): - samplerate = self._samplerate - streamtime = startidx/samplerate - t = np.linspace(streamtime, streamtime + frames/samplerate, frames) - - return 0.1*np.sin(self._omg*t) - - -class NoiseGenerator(SignalGenerator): - def __init__(self, stream, noisetype): - super().__init__(stream) - - def getSignal(self, startidx, frames): - return 0.1*np.random.randn(frames) - -class NoiseType: - white = (0, 'White noise', ) - pink = (1, 'Pink noise') - types = (white, pink) - - @staticmethod - def fillComboBox(combo): - for type_ in NoiseType.types: - combo.addItem(type_[1]) - - @staticmethod - def getCurrent(cb): - return NoiseType.types[cb.currentIndex()] - - - - diff --git a/lasp/wrappers.pyx b/lasp/wrappers.pyx index 877ec3b..0e2bd42 100644 --- a/lasp/wrappers.pyx +++ b/lasp/wrappers.pyx @@ -467,3 +467,54 @@ cdef class SPLowpass: vd_free(&input_vd) return result + + +cdef extern from "lasp_siggen.h": + ctypedef struct c_Siggen "Siggen" + c_Siggen* Siggen_Whitenoise_create() + c_Siggen* Siggen_Sinewave_create(d fs, d freq) + void Siggen_genSignal(c_Siggen*, vd* samples) nogil + void Siggen_free(c_Siggen*) + + +cdef class Siggen: + cdef c_Siggen *_siggen + + def __cinit__(self): + self._siggen = NULL + + def __dealloc__(self): + if self._siggen: + Siggen_free(self._siggen) + + 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 + + + @staticmethod + def sineWave(fs, freq): + cdef c_Siggen* c_siggen = Siggen_Sinewave_create(fs, freq) + siggen = Siggen() + siggen._siggen = c_siggen + return siggen + + + @staticmethod + def whiteNoise(): + cdef c_Siggen* c_siggen = Siggen_Whitenoise_create() + siggen = Siggen() + siggen._siggen = c_siggen + return siggen