1pub const SIN_TABLE: [i16; 256] = [
33 0, 101, 201, 301, 401, 501, 601, 700, 799, 897, 995, 1092, 1189, 1285, 1380, 1474, 1567, 1660,
34 1751, 1842, 1931, 2019, 2106, 2191, 2276, 2359, 2440, 2520, 2598, 2675, 2751, 2824, 2896, 2967,
35 3035, 3102, 3166, 3229, 3290, 3349, 3406, 3461, 3513, 3564, 3612, 3659, 3703, 3745, 3784, 3822,
36 3857, 3889, 3920, 3948, 3973, 3996, 4017, 4036, 4052, 4065, 4076, 4085, 4091, 4095, 4096, 4095,
37 4091, 4085, 4076, 4065, 4052, 4036, 4017, 3996, 3973, 3948, 3920, 3889, 3857, 3822, 3784, 3745,
38 3703, 3659, 3612, 3564, 3513, 3461, 3406, 3349, 3290, 3229, 3166, 3102, 3035, 2967, 2896, 2824,
39 2751, 2675, 2598, 2520, 2440, 2359, 2276, 2191, 2106, 2019, 1931, 1842, 1751, 1660, 1567, 1474,
40 1380, 1285, 1189, 1092, 995, 897, 799, 700, 601, 501, 401, 301, 201, 101, 0, -101, -201, -301,
41 -401, -501, -601, -700, -799, -897, -995, -1092, -1189, -1285, -1380, -1474, -1567, -1660,
42 -1751, -1842, -1931, -2019, -2106, -2191, -2276, -2359, -2440, -2520, -2598, -2675, -2751,
43 -2824, -2896, -2967, -3035, -3102, -3166, -3229, -3290, -3349, -3406, -3461, -3513, -3564,
44 -3612, -3659, -3703, -3745, -3784, -3822, -3857, -3889, -3920, -3948, -3973, -3996, -4017,
45 -4036, -4052, -4065, -4076, -4085, -4091, -4095, -4096, -4095, -4091, -4085, -4076, -4065,
46 -4052, -4036, -4017, -3996, -3973, -3948, -3920, -3889, -3857, -3822, -3784, -3745, -3703,
47 -3659, -3612, -3564, -3513, -3461, -3406, -3349, -3290, -3229, -3166, -3102, -3035, -2967,
48 -2896, -2824, -2751, -2675, -2598, -2520, -2440, -2359, -2276, -2191, -2106, -2019, -1931,
49 -1842, -1751, -1660, -1567, -1474, -1380, -1285, -1189, -1092, -995, -897, -799, -700, -601,
50 -501, -401, -301, -201, -101,
51];
52
53#[inline]
55pub fn sin_q12(angle_q12: u16) -> i32 {
56 let angle = angle_q12 & 0x0FFF;
57 let idx = (angle >> 4) as usize;
58 let frac = (angle & 0x000F) as i32;
59 let a = SIN_TABLE[idx] as i32;
60 let b = SIN_TABLE[(idx + 1) & 0xFF] as i32;
61 a + (((b - a) * frac) / 16)
62}
63
64#[inline]
67pub fn cos_q12(angle_q12: u16) -> i32 {
68 sin_q12(angle_q12.wrapping_add(1024))
70}
71
72pub fn atan2_q12(y: i32, x: i32) -> u16 {
80 if x == 0 && y == 0 {
81 return 0;
82 }
83 let (mut ax, mut ay) = (x.unsigned_abs(), y.unsigned_abs());
84 while ax >= (1 << 23) || ay >= (1 << 23) {
87 ax >>= 8;
88 ay >>= 8;
89 }
90 let q = if ax >= ay {
92 (ay * 512 / ax) as i32
93 } else {
94 1024 - (ax * 512 / ay) as i32
95 };
96 let a = match (x >= 0, y >= 0) {
97 (true, true) => q,
98 (false, true) => 2048 - q,
99 (false, false) => 2048 + q,
100 (true, false) => 4096 - q,
101 };
102 (a & 0xFFF) as u16
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn sin_anchors() {
111 assert_eq!(sin_q12(0), 0);
112 assert_eq!(sin_q12(1024), 4096); assert_eq!(sin_q12(2048), 0); assert_eq!(sin_q12(3072), -4096); }
116
117 #[test]
118 fn cos_anchors() {
119 assert_eq!(cos_q12(0), 4096);
120 assert_eq!(cos_q12(1024), 0);
121 assert_eq!(cos_q12(2048), -4096);
122 assert_eq!(cos_q12(3072), 0);
123 }
124
125 #[test]
126 fn wraps_modulo_4096() {
127 assert_eq!(sin_q12(4096), sin_q12(0));
129 assert_eq!(sin_q12(5120), sin_q12(1024));
130 }
131
132 #[test]
133 fn interpolates_low_angle_bits() {
134 assert_eq!(sin_q12(0), SIN_TABLE[0] as i32);
135 assert!(sin_q12(1) > sin_q12(0));
136 assert!(sin_q12(1) < SIN_TABLE[1] as i32);
137 assert_eq!(sin_q12(16), SIN_TABLE[1] as i32);
138 }
139
140 #[test]
141 fn atan2_cardinals_and_diagonals() {
142 assert_eq!(atan2_q12(0, 0), 0);
143 assert_eq!(atan2_q12(0, 100), 0); assert_eq!(atan2_q12(100, 100), 512); assert_eq!(atan2_q12(100, 0), 1024); assert_eq!(atan2_q12(100, -100), 1536);
147 assert_eq!(atan2_q12(0, -100), 2048); assert_eq!(atan2_q12(-100, -100), 2560);
149 assert_eq!(atan2_q12(-100, 0), 3072); assert_eq!(atan2_q12(-100, 100), 3584);
151 }
152
153 #[test]
154 fn atan2_roundtrips_sincos_within_octant_error() {
155 for &angle in &[0u16, 100, 512, 900, 1024, 2000, 2048, 3000, 3072, 4000] {
157 let (s, c) = (sin_q12(angle), cos_q12(angle));
158 let back = atan2_q12(s, c);
159 let diff = (back.wrapping_sub(angle) & 0xFFF).min(angle.wrapping_sub(back) & 0xFFF);
160 assert!(diff <= 46, "angle {angle} came back as {back}");
161 }
162 }
163
164 #[test]
165 fn atan2_large_coordinates_stay_in_u32() {
166 assert_eq!(atan2_q12(i32::MAX, i32::MAX), 512);
168 assert_eq!(atan2_q12(0, i32::MIN), 2048);
169 assert_eq!(atan2_q12(i32::MIN, 0), 3072);
170 assert_eq!(atan2_q12(1 << 28, 1 << 28), atan2_q12(1, 1));
171 }
172
173 #[test]
174 fn interpolates_across_table_wrap() {
175 assert!(sin_q12(4095) < 0);
176 assert!(sin_q12(4095) > SIN_TABLE[255] as i32);
177 assert_eq!(sin_q12(4096), 0);
178 }
179}