First part of signal generator implementation. White noise and sine waves are working.
This commit is contained in:
parent
3a86facb5a
commit
aa3581cf74
@ -14,6 +14,7 @@ add_library(lasp_lib
|
|||||||
lasp_aps.c
|
lasp_aps.c
|
||||||
lasp_ps.c
|
lasp_ps.c
|
||||||
lasp_mq.c
|
lasp_mq.c
|
||||||
|
lasp_siggen.c
|
||||||
lasp_worker.c
|
lasp_worker.c
|
||||||
lasp_dfifo.c
|
lasp_dfifo.c
|
||||||
lasp_filterbank.c
|
lasp_filterbank.c
|
||||||
|
@ -30,6 +30,8 @@ void DBG_AssertFailedExtImplementation(const char* file,
|
|||||||
DBG_AssertFailedExtImplementation(__FILE__, __LINE__, assert_string ); \
|
DBG_AssertFailedExtImplementation(__FILE__, __LINE__, assert_string ); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define assertvalidptr(ptr) dbgassert(ptr,NULLPTRDEREF)
|
||||||
|
|
||||||
#else // LASP_DEBUG not defined
|
#else // LASP_DEBUG not defined
|
||||||
|
|
||||||
#define dbgassert(assertion, assert_string)
|
#define dbgassert(assertion, assert_string)
|
||||||
|
141
lasp/c/lasp_siggen.c
Normal file
141
lasp/c/lasp_siggen.c
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
57
lasp/c/lasp_siggen.h
Normal file
57
lasp/c/lasp_siggen.h
Normal file
@ -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
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
@ -65,6 +65,8 @@ class AvStream:
|
|||||||
rtaudio_inputparams = None
|
rtaudio_inputparams = None
|
||||||
rtaudio_outputparams = None
|
rtaudio_outputparams = None
|
||||||
|
|
||||||
|
self.nframes_per_block = 2048
|
||||||
|
|
||||||
if self.duplex_mode or avtype == AvType.audio_output:
|
if self.duplex_mode or avtype == AvType.audio_output:
|
||||||
rtaudio_outputparams = {'deviceid': device.index,
|
rtaudio_outputparams = {'deviceid': device.index,
|
||||||
# TODO: Add option to specify the number of output channels to use
|
# TODO: Add option to specify the number of output channels to use
|
||||||
@ -92,7 +94,7 @@ class AvStream:
|
|||||||
rtaudio_inputparams, # Inputparams
|
rtaudio_inputparams, # Inputparams
|
||||||
self.sampleformat, # Sampleformat
|
self.sampleformat, # Sampleformat
|
||||||
self.samplerate,
|
self.samplerate,
|
||||||
2048, # Buffer size in frames
|
self.nframes_per_block, # Buffer size in frames
|
||||||
self._audioCallback)
|
self._audioCallback)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -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()]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -467,3 +467,54 @@ cdef class SPLowpass:
|
|||||||
vd_free(&input_vd)
|
vd_free(&input_vd)
|
||||||
|
|
||||||
return result
|
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
|
||||||
|
Loading…
Reference in New Issue
Block a user