1use core::ops::AddAssign;
2
3use num_traits::{AsPrimitive, Num, Pow, WrappingAdd, WrappingSub};
4
5use dsp_process::Process;
6
7#[derive(Clone, Debug)]
13pub struct Cic<T, const N: usize, const M: usize = 1> {
14 rate: u32,
18 index: u32,
20 zoh: T,
24 combs: [[T; M]; N],
26 integrators: [T; N],
28}
29
30impl<T, const N: usize, const M: usize> Cic<T, N, M>
31where
32 T: Num + AddAssign + WrappingAdd + WrappingSub + Pow<usize, Output = T> + Copy + 'static,
33 u32: AsPrimitive<T>,
34 usize: AsPrimitive<T>,
35{
36 const _M: () = assert!(M > 0, "Comb delay must be non-zero");
37
38 pub fn new(rate: u32) -> Self {
40 Self {
41 rate,
42 index: 0,
43 zoh: T::zero(),
44 combs: [[T::zero(); M]; N],
45 integrators: [T::zero(); N],
46 }
47 }
48
49 pub const fn order(&self) -> usize {
58 N
59 }
60
61 pub const fn comb_delay(&self) -> usize {
63 M
64 }
65
66 pub const fn rate(&self) -> u32 {
70 self.rate
71 }
72
73 pub fn set_rate(&mut self, rate: u32) {
77 self.rate = rate;
78 }
79
80 pub fn clear(&mut self) {
82 *self = Self::new(self.rate);
83 }
84
85 pub const fn tick(&self) -> bool {
90 self.index == 0
91 }
92
93 pub fn get_interpolate(&self) -> T {
95 self.integrators.last().copied().unwrap_or(self.zoh)
96 }
97
98 pub fn get_decimate(&self) -> T {
100 self.zoh
101 }
102
103 pub fn gain(&self) -> T {
105 (M.as_() * (self.rate.as_() + T::one())).pow(N)
106 }
107
108 pub const fn gain_log2(&self) -> u32 {
113 (u32::BITS - (M as u32 * self.rate + (M - 1) as u32).leading_zeros()) * N as u32
114 }
115
116 pub const fn response_length(&self) -> usize {
118 self.rate as usize * N
119 }
120
121 pub fn settle_interpolate(&mut self, x: T) {
123 self.clear();
124 if let Some(c) = self.combs.first_mut() {
125 *c = [x; M];
126 } else {
127 self.zoh = x;
128 }
129 let g = self.gain();
130 if let Some(i) = self.integrators.last_mut() {
131 *i = x * g;
132 }
133 }
134
135 pub fn settle_decimate(&mut self, x: T) {
139 self.clear();
140 self.zoh = x * self.gain();
141 unimplemented!();
142 }
143}
144
145impl<T, const N: usize, const M: usize> Process<Option<T>, T> for Cic<T, N, M>
150where
151 T: Num + AddAssign + Copy,
152{
153 fn process(&mut self, x: Option<T>) -> T {
154 if let Some(x) = x {
155 debug_assert_eq!(self.index, 0);
156 self.index = self.rate;
157 self.zoh = self.combs.iter_mut().fold(x, |x, c| {
158 let y = x - c[0];
159 c.copy_within(1.., 0);
160 c[M - 1] = x;
161 y
162 });
163 } else {
164 self.index -= 1;
165 }
166 self.integrators.iter_mut().fold(self.zoh, |x, i| {
167 *i += x;
169 *i
170 })
171 }
172}
173
174impl<T, const N: usize, const M: usize> Process<T, Option<T>> for Cic<T, N, M>
176where
177 T: WrappingAdd + WrappingSub + Copy,
178{
179 fn process(&mut self, x: T) -> Option<T> {
180 let x = self.integrators.iter_mut().fold(x, |x, i| {
181 *i = i.wrapping_add(&x);
183 *i
184 });
185 if let Some(index) = self.index.checked_sub(1) {
186 self.index = index;
187 None
188 } else {
189 self.index = self.rate;
190 self.zoh = self.combs.iter_mut().fold(x, |x, c| {
191 let y = x.wrapping_sub(&c[0]);
193 c.copy_within(1.., 0);
194 c[M - 1] = x;
195 y
196 });
197 Some(self.zoh)
198 }
199 }
200}
201
202#[cfg(test)]
203mod test {
204 use core::cmp::Ordering;
205
206 use super::*;
207 use quickcheck_macros::quickcheck;
208
209 #[quickcheck]
210 fn new(rate: u32) {
211 let _ = Cic::<i64, 3>::new(rate);
212 }
213
214 #[quickcheck]
215 fn identity_dec(x: Vec<i64>) {
216 let mut dec = Cic::<_, 3>::new(0);
217 for x in x {
218 assert_eq!(Some(x), dec.process(x));
219 assert_eq!(x, dec.get_decimate());
220 }
221 }
222
223 #[quickcheck]
224 fn identity_int(x: Vec<i64>) {
225 const N: usize = 3;
226 let mut int = Cic::<_, N>::new(0);
227 for x in x {
228 assert_eq!(x >> N, int.process(Some(x >> N)));
229 assert_eq!(x >> N, int.get_interpolate());
230 }
231 }
232
233 #[quickcheck]
234 fn response_length_gain_settle(x: Vec<i32>, rate: u32) {
235 let mut int = Cic::<_, 3>::new(rate);
236 let shift = int.gain_log2();
237 if shift >= 32 {
238 return;
239 }
240 assert!(int.gain() <= 1 << shift);
241 for x in x {
242 while !int.tick() {
243 int.process(None);
244 }
245 let y_last = int.get_interpolate();
246 let y_want = x as i64 * int.gain();
247 for i in 0..2 * int.response_length() {
248 let y = int.process(if int.tick() { Some(x as i64) } else { None });
249 assert_eq!(y, int.get_interpolate());
250 if i < int.response_length() {
251 match y_want.cmp(&y_last) {
252 Ordering::Greater => assert!((y_last..y_want).contains(&y)),
253 Ordering::Less => assert!((y_want..y_last).contains(&(y - 1))),
254 Ordering::Equal => assert_eq!(y_want, y),
255 }
256 } else {
257 assert_eq!(y, y_want);
258 }
259 }
260 }
261 }
262
263 #[quickcheck]
264 fn settle(rate: u32, x: i32) {
265 let mut int = Cic::<i64, 3>::new(rate);
266 if int.gain_log2() >= 32 {
267 return;
268 }
269 int.settle_interpolate(x as _);
270 for _ in 0..100 {
273 let y = int.process(if int.tick() { Some(x as _) } else { None });
274 assert_eq!(y, x as i64 * int.gain());
275 assert_eq!(y, int.get_interpolate());
276 }
281 }
282
283 #[quickcheck]
284 fn unit_rate(x: (i32, i32, i32, i32, i32)) {
285 let x: [i32; 5] = x.into();
286 let mut cic = Cic::<i64, 3, 3>::new(0);
287 assert!(cic.gain_log2() == 6);
288 assert!(cic.gain() == (cic.comb_delay() as i64).pow(cic.order() as _));
289 for x in x {
290 assert!(cic.tick());
291 let y: Option<_> = cic.process(x as _);
292 println!("{x:11} {:11}", y.unwrap());
293 }
294 for _ in 0..100 {
295 let y: Option<_> = cic.process(0 as _);
296 assert_eq!(y, Some(cic.get_decimate()));
297 println!("{:11}", y.unwrap());
298 println!("");
299 }
300 }
301}