kizzasi_io/
generator.rs

1//! Signal generators for testing and development
2//!
3//! Provides various signal generators:
4//! - Sine waves, square waves, sawtooth
5//! - White noise, pink noise
6//! - Impulse, step functions
7//! - Chirp (frequency sweep)
8
9use scirs2_core::ndarray::Array1;
10use scirs2_core::random::thread_rng;
11use std::f32::consts::PI;
12
13/// Signal generator trait
14pub trait SignalGenerator {
15    /// Generate the next sample
16    fn next_sample(&mut self) -> f32;
17
18    /// Generate n samples
19    fn generate(&mut self, n: usize) -> Array1<f32> {
20        let samples: Vec<f32> = (0..n).map(|_| self.next_sample()).collect();
21        Array1::from_vec(samples)
22    }
23
24    /// Reset the generator to initial state
25    fn reset(&mut self);
26}
27
28/// Sine wave generator
29#[derive(Debug, Clone)]
30pub struct SineGenerator {
31    frequency: f32,
32    amplitude: f32,
33    phase: f32,
34    sample_rate: f32,
35    sample_index: usize,
36}
37
38impl SineGenerator {
39    /// Create a new sine generator
40    pub fn new(frequency: f32, amplitude: f32, sample_rate: f32) -> Self {
41        Self {
42            frequency,
43            amplitude,
44            phase: 0.0,
45            sample_rate,
46            sample_index: 0,
47        }
48    }
49
50    /// Set initial phase (in radians)
51    pub fn with_phase(mut self, phase: f32) -> Self {
52        self.phase = phase;
53        self
54    }
55}
56
57impl SignalGenerator for SineGenerator {
58    fn next_sample(&mut self) -> f32 {
59        let t = self.sample_index as f32 / self.sample_rate;
60        let sample = self.amplitude * (2.0 * PI * self.frequency * t + self.phase).sin();
61        self.sample_index += 1;
62        sample
63    }
64
65    fn reset(&mut self) {
66        self.sample_index = 0;
67    }
68}
69
70/// Square wave generator
71#[derive(Debug, Clone)]
72pub struct SquareGenerator {
73    frequency: f32,
74    amplitude: f32,
75    duty_cycle: f32,
76    sample_rate: f32,
77    sample_index: usize,
78}
79
80impl SquareGenerator {
81    /// Create a new square wave generator
82    pub fn new(frequency: f32, amplitude: f32, sample_rate: f32) -> Self {
83        Self {
84            frequency,
85            amplitude,
86            duty_cycle: 0.5,
87            sample_rate,
88            sample_index: 0,
89        }
90    }
91
92    /// Set duty cycle (0.0 to 1.0)
93    pub fn with_duty_cycle(mut self, duty: f32) -> Self {
94        self.duty_cycle = duty.clamp(0.0, 1.0);
95        self
96    }
97}
98
99impl SignalGenerator for SquareGenerator {
100    fn next_sample(&mut self) -> f32 {
101        let t = self.sample_index as f32 / self.sample_rate;
102        let period = 1.0 / self.frequency;
103        let phase = (t % period) / period;
104        let sample = if phase < self.duty_cycle {
105            self.amplitude
106        } else {
107            -self.amplitude
108        };
109        self.sample_index += 1;
110        sample
111    }
112
113    fn reset(&mut self) {
114        self.sample_index = 0;
115    }
116}
117
118/// Sawtooth wave generator
119#[derive(Debug, Clone)]
120pub struct SawtoothGenerator {
121    frequency: f32,
122    amplitude: f32,
123    sample_rate: f32,
124    sample_index: usize,
125}
126
127impl SawtoothGenerator {
128    /// Create a new sawtooth generator
129    pub fn new(frequency: f32, amplitude: f32, sample_rate: f32) -> Self {
130        Self {
131            frequency,
132            amplitude,
133            sample_rate,
134            sample_index: 0,
135        }
136    }
137}
138
139impl SignalGenerator for SawtoothGenerator {
140    fn next_sample(&mut self) -> f32 {
141        let t = self.sample_index as f32 / self.sample_rate;
142        let period = 1.0 / self.frequency;
143        let phase = (t % period) / period;
144        let sample = self.amplitude * (2.0 * phase - 1.0);
145        self.sample_index += 1;
146        sample
147    }
148
149    fn reset(&mut self) {
150        self.sample_index = 0;
151    }
152}
153
154/// Triangle wave generator
155#[derive(Debug, Clone)]
156pub struct TriangleGenerator {
157    frequency: f32,
158    amplitude: f32,
159    sample_rate: f32,
160    sample_index: usize,
161}
162
163impl TriangleGenerator {
164    /// Create a new triangle wave generator
165    pub fn new(frequency: f32, amplitude: f32, sample_rate: f32) -> Self {
166        Self {
167            frequency,
168            amplitude,
169            sample_rate,
170            sample_index: 0,
171        }
172    }
173}
174
175impl SignalGenerator for TriangleGenerator {
176    fn next_sample(&mut self) -> f32 {
177        let t = self.sample_index as f32 / self.sample_rate;
178        let period = 1.0 / self.frequency;
179        let phase = (t % period) / period;
180        let sample = self.amplitude * (4.0 * (phase - (phase + 0.5).floor()).abs() - 1.0);
181        self.sample_index += 1;
182        sample
183    }
184
185    fn reset(&mut self) {
186        self.sample_index = 0;
187    }
188}
189
190/// White noise generator using a simple LCG for reproducibility
191#[derive(Debug, Clone)]
192pub struct WhiteNoiseGenerator {
193    amplitude: f32,
194    seed: u64,
195    state: u64,
196}
197
198impl WhiteNoiseGenerator {
199    /// Create a new white noise generator with random seed
200    pub fn new(amplitude: f32) -> Self {
201        let seed = thread_rng().random::<u64>();
202        Self {
203            amplitude,
204            seed,
205            state: seed,
206        }
207    }
208
209    /// Create with a specific seed for reproducibility
210    pub fn with_seed(amplitude: f32, seed: u64) -> Self {
211        Self {
212            amplitude,
213            seed,
214            state: seed,
215        }
216    }
217
218    /// Simple LCG random number generator
219    fn next_random(&mut self) -> f32 {
220        // LCG parameters (same as glibc)
221        self.state = self.state.wrapping_mul(1103515245).wrapping_add(12345);
222        // Extract bits and normalize to [0, 1)
223        ((self.state >> 16) & 0x7fff) as f32 / 32768.0
224    }
225}
226
227impl SignalGenerator for WhiteNoiseGenerator {
228    fn next_sample(&mut self) -> f32 {
229        self.amplitude * (self.next_random() * 2.0 - 1.0)
230    }
231
232    fn reset(&mut self) {
233        self.state = self.seed;
234    }
235}
236
237/// Pink noise generator (1/f spectrum)
238#[derive(Debug, Clone)]
239pub struct PinkNoiseGenerator {
240    amplitude: f32,
241    seed: u64,
242    state: u64,
243    // Voss-McCartney algorithm state
244    rows: [f32; 16],
245    running_sum: f32,
246    index: usize,
247}
248
249impl PinkNoiseGenerator {
250    /// Create a new pink noise generator
251    pub fn new(amplitude: f32) -> Self {
252        let seed = thread_rng().random::<u64>();
253        Self {
254            amplitude,
255            seed,
256            state: seed,
257            rows: [0.0; 16],
258            running_sum: 0.0,
259            index: 0,
260        }
261    }
262
263    /// Create with a specific seed for reproducibility
264    pub fn with_seed(amplitude: f32, seed: u64) -> Self {
265        Self {
266            amplitude,
267            seed,
268            state: seed,
269            rows: [0.0; 16],
270            running_sum: 0.0,
271            index: 0,
272        }
273    }
274
275    /// Simple LCG random number generator
276    fn next_random(&mut self) -> f32 {
277        self.state = self.state.wrapping_mul(1103515245).wrapping_add(12345);
278        ((self.state >> 16) & 0x7fff) as f32 / 32768.0
279    }
280}
281
282impl SignalGenerator for PinkNoiseGenerator {
283    fn next_sample(&mut self) -> f32 {
284        // Voss-McCartney algorithm
285        let last_index = self.index;
286        self.index = (self.index + 1) % (1 << 16);
287
288        // Find which rows to update (trailing zeros)
289        let diff = self.index ^ last_index;
290        for i in 0..16 {
291            if (diff >> i) & 1 == 1 {
292                self.running_sum -= self.rows[i];
293                self.rows[i] = self.next_random() * 2.0 - 1.0;
294                self.running_sum += self.rows[i];
295            }
296        }
297
298        self.amplitude * self.running_sum / 16.0
299    }
300
301    fn reset(&mut self) {
302        self.state = self.seed;
303        self.rows = [0.0; 16];
304        self.running_sum = 0.0;
305        self.index = 0;
306    }
307}
308
309/// Impulse generator (single spike)
310#[derive(Debug, Clone)]
311pub struct ImpulseGenerator {
312    amplitude: f32,
313    delay_samples: usize,
314    sample_index: usize,
315}
316
317impl ImpulseGenerator {
318    /// Create a new impulse generator
319    pub fn new(amplitude: f32, delay_samples: usize) -> Self {
320        Self {
321            amplitude,
322            delay_samples,
323            sample_index: 0,
324        }
325    }
326}
327
328impl SignalGenerator for ImpulseGenerator {
329    fn next_sample(&mut self) -> f32 {
330        let sample = if self.sample_index == self.delay_samples {
331            self.amplitude
332        } else {
333            0.0
334        };
335        self.sample_index += 1;
336        sample
337    }
338
339    fn reset(&mut self) {
340        self.sample_index = 0;
341    }
342}
343
344/// Step function generator
345#[derive(Debug, Clone)]
346pub struct StepGenerator {
347    amplitude: f32,
348    step_sample: usize,
349    sample_index: usize,
350}
351
352impl StepGenerator {
353    /// Create a new step function generator
354    pub fn new(amplitude: f32, step_sample: usize) -> Self {
355        Self {
356            amplitude,
357            step_sample,
358            sample_index: 0,
359        }
360    }
361}
362
363impl SignalGenerator for StepGenerator {
364    fn next_sample(&mut self) -> f32 {
365        let sample = if self.sample_index >= self.step_sample {
366            self.amplitude
367        } else {
368            0.0
369        };
370        self.sample_index += 1;
371        sample
372    }
373
374    fn reset(&mut self) {
375        self.sample_index = 0;
376    }
377}
378
379/// Chirp (frequency sweep) generator
380#[derive(Debug, Clone)]
381pub struct ChirpGenerator {
382    start_freq: f32,
383    end_freq: f32,
384    amplitude: f32,
385    duration: f32,
386    sample_rate: f32,
387    sample_index: usize,
388}
389
390impl ChirpGenerator {
391    /// Create a new chirp generator
392    pub fn new(
393        start_freq: f32,
394        end_freq: f32,
395        amplitude: f32,
396        duration: f32,
397        sample_rate: f32,
398    ) -> Self {
399        Self {
400            start_freq,
401            end_freq,
402            amplitude,
403            duration,
404            sample_rate,
405            sample_index: 0,
406        }
407    }
408}
409
410impl SignalGenerator for ChirpGenerator {
411    fn next_sample(&mut self) -> f32 {
412        let t = self.sample_index as f32 / self.sample_rate;
413        if t > self.duration {
414            return 0.0;
415        }
416
417        // Linear frequency sweep
418        let k = (self.end_freq - self.start_freq) / self.duration;
419        // Instantaneous frequency = start_freq + k * t
420        // Phase integral = start_freq * t + 0.5 * k * t^2
421        let phase = 2.0 * PI * (self.start_freq * t + 0.5 * k * t * t);
422        let sample = self.amplitude * phase.sin();
423
424        self.sample_index += 1;
425        sample
426    }
427
428    fn reset(&mut self) {
429        self.sample_index = 0;
430    }
431}
432
433/// Multi-tone generator (sum of multiple sine waves)
434#[derive(Debug, Clone)]
435pub struct MultiToneGenerator {
436    frequencies: Vec<f32>,
437    amplitudes: Vec<f32>,
438    sample_rate: f32,
439    sample_index: usize,
440}
441
442impl MultiToneGenerator {
443    /// Create a new multi-tone generator
444    pub fn new(frequencies: Vec<f32>, amplitudes: Vec<f32>, sample_rate: f32) -> Self {
445        assert_eq!(
446            frequencies.len(),
447            amplitudes.len(),
448            "Frequencies and amplitudes must have same length"
449        );
450        Self {
451            frequencies,
452            amplitudes,
453            sample_rate,
454            sample_index: 0,
455        }
456    }
457
458    /// Create with uniform amplitude
459    pub fn uniform(frequencies: Vec<f32>, amplitude: f32, sample_rate: f32) -> Self {
460        let amplitudes = vec![amplitude / frequencies.len() as f32; frequencies.len()];
461        Self::new(frequencies, amplitudes, sample_rate)
462    }
463}
464
465impl SignalGenerator for MultiToneGenerator {
466    fn next_sample(&mut self) -> f32 {
467        let t = self.sample_index as f32 / self.sample_rate;
468        let sample: f32 = self
469            .frequencies
470            .iter()
471            .zip(self.amplitudes.iter())
472            .map(|(&f, &a)| a * (2.0 * PI * f * t).sin())
473            .sum();
474        self.sample_index += 1;
475        sample
476    }
477
478    fn reset(&mut self) {
479        self.sample_index = 0;
480    }
481}
482
483#[cfg(test)]
484mod tests {
485    use super::*;
486
487    #[test]
488    fn test_sine_generator() {
489        let mut gen = SineGenerator::new(1.0, 1.0, 4.0);
490        let samples = gen.generate(4);
491        assert_eq!(samples.len(), 4);
492        // At t=0, sin(0) = 0
493        assert!(samples[0].abs() < 0.01);
494        // At t=0.25, sin(pi/2) = 1
495        assert!((samples[1] - 1.0).abs() < 0.01);
496    }
497
498    #[test]
499    fn test_square_generator() {
500        let mut gen = SquareGenerator::new(1.0, 1.0, 4.0);
501        let samples = gen.generate(4);
502        assert_eq!(samples.len(), 4);
503        // First half of period should be positive
504        assert_eq!(samples[0], 1.0);
505        assert_eq!(samples[1], 1.0);
506        // Second half should be negative
507        assert_eq!(samples[2], -1.0);
508        assert_eq!(samples[3], -1.0);
509    }
510
511    #[test]
512    fn test_white_noise() {
513        let mut gen = WhiteNoiseGenerator::new(1.0);
514        let samples = gen.generate(1000);
515        assert_eq!(samples.len(), 1000);
516        // Mean should be close to 0
517        let mean: f32 = samples.iter().sum::<f32>() / samples.len() as f32;
518        assert!(mean.abs() < 0.1);
519    }
520
521    #[test]
522    fn test_impulse_generator() {
523        let mut gen = ImpulseGenerator::new(1.0, 2);
524        let samples = gen.generate(5);
525        assert_eq!(samples[0], 0.0);
526        assert_eq!(samples[1], 0.0);
527        assert_eq!(samples[2], 1.0);
528        assert_eq!(samples[3], 0.0);
529    }
530
531    #[test]
532    fn test_step_generator() {
533        let mut gen = StepGenerator::new(1.0, 2);
534        let samples = gen.generate(5);
535        assert_eq!(samples[0], 0.0);
536        assert_eq!(samples[1], 0.0);
537        assert_eq!(samples[2], 1.0);
538        assert_eq!(samples[3], 1.0);
539    }
540
541    #[test]
542    fn test_chirp_generator() {
543        let mut gen = ChirpGenerator::new(1.0, 10.0, 1.0, 1.0, 100.0);
544        let samples = gen.generate(100);
545        assert_eq!(samples.len(), 100);
546        // All samples should be in [-1, 1]
547        for s in samples.iter() {
548            assert!(*s >= -1.0 && *s <= 1.0);
549        }
550    }
551
552    #[test]
553    fn test_multi_tone() {
554        let mut gen = MultiToneGenerator::uniform(vec![1.0, 2.0], 1.0, 100.0);
555        let samples = gen.generate(100);
556        assert_eq!(samples.len(), 100);
557    }
558
559    #[test]
560    fn test_reset() {
561        let mut gen = SineGenerator::new(1.0, 1.0, 4.0);
562        let first = gen.generate(4);
563        gen.reset();
564        let second = gen.generate(4);
565
566        for i in 0..4 {
567            assert!((first[i] - second[i]).abs() < 1e-6);
568        }
569    }
570}