use super::source::SourceImpl;
use crate::{
filter::{PoleOrZero, SeriesBiquad, TransferFunction, ZPKModel},
siggen::{InterruptTime, InvalidParameterSnafu, SiggenError},
*,
};
use rand::prelude::*;
use rand_distr::StandardNormal;
use snafu::prelude::*;
type Result<T> = std::result::Result<T, SiggenError>;
const PINKNOISE_ANALOG_ORDER: usize = 10;
#[derive(Clone, Debug)]
pub struct WhiteNoise {
fs: StrictlyPositive,
rng: SmallRng,
interrupt_state: Option<InterruptState>,
}
impl WhiteNoise {
pub fn new(fs: StrictlyPositive, interrupt_time: Option<InterruptTime>) -> Self {
let interrupt_state = interrupt_time.map(|period| InterruptState {
cur_idx: 0,
max_idx: (*period * *fs) as usize,
silence: false,
});
WhiteNoise {
fs,
rng: SmallRng::seed_from_u64(1),
interrupt_state,
}
}
}
impl SourceImpl for WhiteNoise {
fn fs(&self) -> StrictlyPositive {
self.fs
}
fn genSignal_unscaled(&mut self, sig: &mut dyn ExactSizeIterator<Item = &mut Flt>) {
let mut output = true;
if let Some(InterruptState {
cur_idx,
max_idx,
silence,
}) = &mut self.interrupt_state
{
if cur_idx > max_idx {
*cur_idx = 0;
*silence = !*silence;
}
output = !*silence;
*cur_idx += sig.len();
}
if output {
sig.for_each(|s| {
*s = self.rng.sample(StandardNormal);
});
} else {
sig.for_each(|s| {
*s = 0.;
});
}
}
}
#[derive(Debug, Clone)]
pub struct ColoredNoise {
wn: WhiteNoise,
tmp: Vec<Flt>,
filter: SeriesBiquad,
}
impl ColoredNoise {
pub fn newPinkNoise(
fs: StrictlyPositive,
rollOffPoint: StrictlyPositive,
interrupt_time: Option<InterruptTime>,
) -> Result<Self> {
ensure!(
*rollOffPoint < *fs / 2.,
InvalidParameterSnafu {
param: "roll off point for pink noise",
criterion: format!("must be less than the Nyquist frequency ({})", fs)
}
);
let fl = *rollOffPoint;
let fu = *fs / 2.5;
let fpoles_real: Vec<Flt> = (0..PINKNOISE_ANALOG_ORDER)
.map(|i| fl * (fu / fl).powf((i as Flt) / (PINKNOISE_ANALOG_ORDER as Flt - 1.)))
.collect();
let fzeros_real: Vec<Flt> = (0..PINKNOISE_ANALOG_ORDER - 1)
.map(|i| (fpoles_real[i] * fpoles_real[i + 1]).sqrt())
.collect();
let poles: Vec<PoleOrZero> = fpoles_real
.into_iter()
.map(|f| PoleOrZero::Real1(-twopi * f))
.collect();
let zeros: Vec<PoleOrZero> = fzeros_real
.into_iter()
.map(|f| PoleOrZero::Real1(-twopi * f))
.collect();
let gain = 1.;
let analogue_blueprint = ZPKModel::new(zeros, poles, gain);
let mut filter = analogue_blueprint.bilinear(fs);
let fnyq = *fs / 2.;
let N = 2400;
let freq = Array1::linspace(20., fnyq, 2000);
let tf_power_sum = filter.tf(fs, freq.view()).mapv(|t| t.abs().powi(2)).sum();
let gain = ((N as Flt) / tf_power_sum).sqrt();
filter.setGain(gain);
Ok(Self {
wn: WhiteNoise::new(fs, interrupt_time),
tmp: vec![],
filter,
})
}
}
impl SourceImpl for ColoredNoise {
fn genSignal_unscaled(&mut self, sig: &mut dyn ExactSizeIterator<Item = &mut Flt>) {
self.tmp.resize(sig.len(), 0.);
self.wn.genSignal_unscaled(&mut self.tmp.iter_mut());
self.filter.filter_inout(&mut self.tmp);
sig.zip(&self.tmp).for_each(|(sig, src)| {
*sig = *src;
});
}
fn fs(&self) -> StrictlyPositive {
self.wn.fs()
}
}
#[derive(Clone, Copy, Debug)]
struct InterruptState {
cur_idx: usize,
max_idx: usize,
silence: bool,
}