Skip to main content

embedded_dsp/
window.rs

1//! Window functions (Hanning, Hamming, Blackman, Blackman-Harris, Bartlett, Welch, Flat-top window generators).
2
3#[allow(unused_imports)]
4use crate::math::FloatMath;
5use crate::types::q15;
6
7#[inline]
8fn q15_from_unit(v: f32) -> q15 {
9    (v * 32767.0).clamp(-32768.0, 32767.0) as q15
10}
11
12/// Generate Hanning window of length `n`.
13pub fn hanning_f32(dst: &mut [f32]) {
14    let n = dst.len();
15    if n == 0 {
16        return;
17    }
18    if n == 1 {
19        dst[0] = 1.0;
20        return;
21    }
22    let factor = 2.0 * core::f32::consts::PI / ((n - 1) as f32);
23    for i in 0..n {
24        dst[i] = 0.5 * (1.0 - ((i as f32) * factor).cos());
25    }
26}
27
28/// Generate Hamming window of length `n`.
29pub fn hamming_f32(dst: &mut [f32]) {
30    let n = dst.len();
31    if n == 0 {
32        return;
33    }
34    if n == 1 {
35        dst[0] = 1.0;
36        return;
37    }
38    let factor = 2.0 * core::f32::consts::PI / ((n - 1) as f32);
39    for i in 0..n {
40        dst[i] = 0.54 - 0.46 * ((i as f32) * factor).cos();
41    }
42}
43
44/// Generate Blackman window of length `n`.
45pub fn blackman_f32(dst: &mut [f32]) {
46    let n = dst.len();
47    if n == 0 {
48        return;
49    }
50    if n == 1 {
51        dst[0] = 1.0;
52        return;
53    }
54    let factor = 2.0 * core::f32::consts::PI / ((n - 1) as f32);
55    for i in 0..n {
56        let a = (i as f32) * factor;
57        dst[i] = 0.42 - 0.5 * a.cos() + 0.08 * (2.0 * a).cos();
58    }
59}
60
61/// Generate 4-term Blackman-Harris window of length `n` (>92 dB sidelobe rejection).
62pub fn blackman_harris_f32(dst: &mut [f32]) {
63    let n = dst.len();
64    if n == 0 {
65        return;
66    }
67    if n == 1 {
68        dst[0] = 1.0;
69        return;
70    }
71    let factor = 2.0 * core::f32::consts::PI / ((n - 1) as f32);
72    for i in 0..n {
73        let a = (i as f32) * factor;
74        dst[i] =
75            0.35875 - 0.48829 * a.cos() + 0.14128 * (2.0 * a).cos() - 0.01168 * (3.0 * a).cos();
76    }
77}
78
79/// Generate Bartlett (Triangular) window of length `n`.
80pub fn bartlett_f32(dst: &mut [f32]) {
81    let n = dst.len();
82    if n == 0 {
83        return;
84    }
85    if n == 1 {
86        dst[0] = 1.0;
87        return;
88    }
89    let half = ((n - 1) as f32) / 2.0;
90    for i in 0..n {
91        dst[i] = 1.0 - ((i as f32 - half) / half).abs();
92    }
93}
94
95/// Generate Welch parabolic window of length `n`.
96pub fn welch_f32(dst: &mut [f32]) {
97    let n = dst.len();
98    if n == 0 {
99        return;
100    }
101    if n == 1 {
102        dst[0] = 1.0;
103        return;
104    }
105    let half = ((n - 1) as f32) / 2.0;
106    for i in 0..n {
107        let term = (i as f32 - half) / half;
108        dst[i] = 1.0 - term * term;
109    }
110}
111
112/// Generate Flat-top window of length `n`.
113pub fn flattop_f32(dst: &mut [f32]) {
114    let n = dst.len();
115    if n == 0 {
116        return;
117    }
118    if n == 1 {
119        dst[0] = 1.0;
120        return;
121    }
122    let factor = 2.0 * core::f32::consts::PI / ((n - 1) as f32);
123    for i in 0..n {
124        let a = (i as f32) * factor;
125        dst[i] = 0.21557895 - 0.41663158 * a.cos() + 0.277_263_16 * (2.0 * a).cos()
126            - 0.08357895 * (3.0 * a).cos()
127            + 0.006947368 * (4.0 * a).cos();
128    }
129}
130
131/// Multiply signal elements in-place by window array.
132pub fn apply_window_f32(signal: &mut [f32], window: &[f32]) {
133    let len = signal.len().min(window.len());
134    for i in 0..len {
135        signal[i] *= window[i];
136    }
137}
138
139fn fill_window_q15(dst: &mut [q15], fill: impl Fn(usize, usize) -> f32) {
140    let n = dst.len();
141    if n == 0 {
142        return;
143    }
144    if n == 1 {
145        dst[0] = 32767;
146        return;
147    }
148    for i in 0..n {
149        dst[i] = q15_from_unit(fill(i, n));
150    }
151}
152
153/// Q15 Hanning window.
154pub fn hanning_q15(dst: &mut [q15]) {
155    fill_window_q15(dst, |i, n| {
156        let factor = 2.0 * core::f32::consts::PI / ((n - 1) as f32);
157        0.5 * (1.0 - ((i as f32) * factor).cos())
158    });
159}
160
161/// Q15 Hamming window.
162pub fn hamming_q15(dst: &mut [q15]) {
163    fill_window_q15(dst, |i, n| {
164        let factor = 2.0 * core::f32::consts::PI / ((n - 1) as f32);
165        0.54 - 0.46 * ((i as f32) * factor).cos()
166    });
167}
168
169/// Q15 Blackman window.
170pub fn blackman_q15(dst: &mut [q15]) {
171    fill_window_q15(dst, |i, n| {
172        let factor = 2.0 * core::f32::consts::PI / ((n - 1) as f32);
173        let a = (i as f32) * factor;
174        0.42 - 0.5 * a.cos() + 0.08 * (2.0 * a).cos()
175    });
176}
177
178/// Q15 Bartlett (triangular) window.
179pub fn bartlett_q15(dst: &mut [q15]) {
180    fill_window_q15(dst, |i, n| {
181        let half = (n - 1) as f32 / 2.0;
182        1.0 - ((i as f32 - half) / half).abs()
183    });
184}
185
186/// Multiply Q15 signal in-place by a Q15 window (`>> 15`).
187pub fn apply_window_q15(signal: &mut [q15], window: &[q15]) {
188    let len = signal.len().min(window.len());
189    for i in 0..len {
190        signal[i] = (((signal[i] as i32) * (window[i] as i32)) >> 15) as q15;
191    }
192}