Skip to main content

audio_lab/signal/test_signal/
impulse.rs

1use ndarray::Array2;
2
3use crate::signal::{SignalError, TimeSignal};
4
5#[derive(Debug, thiserror::Error)]
6pub enum ImpulseError {
7    #[error("delay ({delay}) must be less than num_time_steps ({num_time_steps})")]
8    DelayOutOfRange { delay: usize, num_time_steps: usize },
9    #[error(transparent)]
10    Signal(#[from] SignalError),
11}
12
13#[derive(Debug, Clone, Copy)]
14pub struct ImpulseConfig {
15    pub delay: usize,
16    pub amplitude: f64,
17    pub sample_rate: f64,
18    pub num_channels: usize,
19}
20
21impl Default for ImpulseConfig {
22    fn default() -> Self {
23        Self {
24            delay: 0,
25            amplitude: 1.0,
26            sample_rate: 48_000.0,
27            num_channels: 1,
28        }
29    }
30}
31
32pub fn generate_impulse(
33    num_time_steps: usize,
34    config: ImpulseConfig,
35) -> Result<TimeSignal, ImpulseError> {
36    let ImpulseConfig {
37        delay,
38        amplitude,
39        sample_rate,
40        num_channels,
41    } = config;
42
43    if delay >= num_time_steps {
44        return Err(ImpulseError::DelayOutOfRange {
45            delay,
46            num_time_steps,
47        });
48    }
49
50    let mut data = Array2::zeros((num_channels, num_time_steps));
51    for ch in 0..num_channels {
52        data[[ch, delay]] = amplitude;
53    }
54
55    let mut signal = TimeSignal::new(data, sample_rate)?;
56    let comment = format!("Impulse signal (delay = {delay} samples, amplitude = {amplitude})");
57    signal.set_comment(Some(&comment));
58    Ok(signal)
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64
65    #[test]
66    fn generates_impulse_at_zero() {
67        let signal = generate_impulse(8, ImpulseConfig::default()).unwrap();
68        assert_eq!(signal.channel(0)[0], 1.0);
69        assert_eq!(signal.channel(0)[1], 0.0);
70    }
71
72    #[test]
73    fn generates_impulse_with_delay() {
74        let signal = generate_impulse(
75            8,
76            ImpulseConfig {
77                delay: 3,
78                amplitude: 0.5,
79                num_channels: 2,
80                ..Default::default()
81            },
82        )
83        .unwrap();
84        assert_eq!(signal.channel(0)[3], 0.5);
85        assert_eq!(signal.channel(1)[3], 0.5);
86        assert_eq!(signal.channel(0)[0], 0.0);
87    }
88
89    #[test]
90    fn validates_delay() {
91        assert!(matches!(
92            generate_impulse(
93                8,
94                ImpulseConfig {
95                    delay: 8,
96                    ..Default::default()
97                }
98            ),
99            Err(ImpulseError::DelayOutOfRange { .. })
100        ));
101    }
102}