audio_lab/signal/test_signal/
impulse.rs1use 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}