Added first parts signal generator. Not yet for sweeps, not yet filters.

This commit is contained in:
Anne de Jong 2023-11-29 07:20:13 +01:00
parent 23ddf39a4a
commit ec010945c6
4 changed files with 194 additions and 4 deletions

View File

@ -14,24 +14,27 @@ categories = [
"mathematics"]
[lib]
name = "lasprs"
crate-type = ["cdylib", "lib"]
crate-type = ["cdylib", "rlib"]
[dependencies]
anyhow = "1.0.75"
pyo3 = { version = "0.20", features=["anyhow", "extension-module"]}
# Optional future feature for ndarray: blas
ndarray = { version = "0.15.3", features = ["rayon"] }
num = "0.4.1"
rayon = "1.8.0"
numpy = { version = "0.20" }
strum_macros = "0.25.3"
pyo3 = { version = "0.20", features=["anyhow", "extension-module"]}
rand = "0.8.5"
rand_distr = "0.4.3"
# blas-src = { version = "0.8", features = ["openblas"] }
# openblas-src = { version = "0.10", features = ["cblas", "system"] }
[features]
# default = ["f64"]
default = ["f64"]
# Use this for debugging extension
default = ["f64", "extension-module", "pyo3/extension-module"]
# default = ["f64", "extension-module", "pyo3/extension-module"]
f64 = []
f32 = []
extension-module = ["pyo3/extension-module"]

View File

@ -16,6 +16,9 @@ use numpy::ndarray::{Array1, Array2};
pub type Vd = Vec<Flt>;
pub type Vc = Vec<Cflt>;
pub type Dcol = Array1<Flt>;
pub type Ccol = Array1<Cflt>;
pub type Dmat = Array2<Flt>;
pub type Cmat = Array2<Cflt>;

View File

@ -10,6 +10,9 @@
mod config;
pub mod filter;
// pub mod window;
// pub mod ps;
pub mod siggen;
extern crate pyo3;
#[cfg(feature = "extension-module")]

181
src/siggen.rs Normal file
View File

@ -0,0 +1,181 @@
use super::config::*;
use super::filter::Filter;
use pyo3::prelude::*;
use rand::prelude::*;
use rand::rngs::ThreadRng;
use rand_distr::StandardNormal;
pub trait Source: Send {
fn genSignal_unscaled(&mut self, sig: &mut [Flt]);
fn reset(&mut self, fs: Flt);
fn clone_dyn(&self) -> Box<dyn Source>;
}
impl Clone for Box<dyn Source> {
fn clone(&self) -> Self {
self.clone_dyn()
}
}
/// White noise source
#[derive(Clone)]
struct WhiteNoise {}
impl WhiteNoise {
/// Generate new WhiteNoise generator
fn new() -> WhiteNoise {
WhiteNoise {}
}
}
impl Source for WhiteNoise {
fn genSignal_unscaled(&mut self, sig: &mut [Flt]) {
sig.iter_mut()
.for_each(|s| *s = thread_rng().sample(StandardNormal));
}
fn reset(&mut self,_fs: Flt) {}
fn clone_dyn(&self) -> Box<dyn Source> {
Box::new(self.clone())
}
}
/// Sine wave, with configurable frequency
#[derive(Clone)]
struct Sine {
// Sampling freq [Hz]
fs: Flt,
// current stored phase
phase: Flt,
// Signal frequency [rad/s]
omg: Flt,
}
impl Sine {
/// Create new sine source signal
///
/// Args:
///
/// * fs: Sampling freq [Hz]
/// *
fn new(freq: Flt) -> Sine {
Sine {
fs: -1.,
phase: 0.,
omg: 2. * pi * freq,
}
}
}
impl Source for Sine {
fn genSignal_unscaled(&mut self, sig: &mut [Flt]) {
if self.fs < 0. {
sig.iter_mut().for_each(|s| {
*s = 0.;
});
return;
}
sig.iter_mut().for_each(|s| {
*s = Flt::sin(self.phase);
self.phase += self.omg / self.fs;
});
while self.phase > 2. * pi {
self.phase -= 2. * pi;
}
}
fn reset(&mut self, fs: Flt) {
self.fs = fs;
self.phase = 0.;
}
fn clone_dyn(&self) -> Box<dyn Source> {
Box::new(self.clone())
}
}
/// Sweep signal
#[derive(Clone)]
/// Signal generator. Able to create acoustic output signals
pub struct Siggen {
source: Box<dyn Source>,
prefilter: Option<Box<dyn Filter>>,
muted: bool,
gain: Flt,
DCOffset: Flt,
}
/// A struct that implements the Siggen trait is able to generate a signal.
impl Siggen {
/// Set new pre-filter that filters the source signal
pub fn setPreFilter(&mut self, pref: Option<Box<dyn Filter>>) {
self.prefilter = pref.clone();
}
pub fn newWhiteNoise() -> Siggen {
Siggen::new(Box::new(WhiteNoise::new()))
}
pub fn newSineWave(freq: Flt) -> Siggen {
Siggen::new(Box::new(Sine::new(freq)))
}
/// Create a new signal generator wiht an arbitrary source.
pub fn new(source: Box<dyn Source>) -> Siggen {
Siggen {
source,
prefilter: None,
muted: false,
gain: 1.0,
DCOffset: 0.0,
}
}
/// Generate new signal data.
///
/// # Args
///
/// sig: Reference of array of float values to be filled with signal data.
///
/// # Details
///
/// - When muted, the DC offset is still applied
/// - The order of the generation is:
/// - First, the source is generated.
/// - If a prefilter is installed, this pre-filter is applied to the source signal.
/// - Gain is applied.
/// - Offset is applied (thus, no gain is applied to the DC offset).
///
fn genSignal(&mut self, sig: &mut [Flt]) {
if self.muted {
sig.iter_mut().for_each(|x| {
*x = 0.0;
});
} else {
self.source.genSignal_unscaled(sig);
if let Some(f) = &mut self.prefilter {
f.filter(sig);
}
}
sig.iter_mut().for_each(|x| {
// First apply gain, then offset
*x *= self.gain;
*x += self.DCOffset;
});
}
/// Reset signal generator. Applies any kind of cleanup necessary.
///
/// Args
///
/// * fs: (New) Sampling frequency [Hz]
///
fn reset(&mut self, fs: Flt) {
self.source.reset(fs);
if let Some(f) = self.prefilter {
f.reset();
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_whitenoise() {
println!("{:?}", WhiteNoise::new().genSignal(10));
}
}