1#![no_std]
8
9pub type Angle = u16; pub type Fixed = i16; #[cfg(feature = "lut")]
25mod lut_impl {
26 use super::{Angle, Fixed};
27 const QUADRANT_BITS: u32 = 14;
28 const LUT_SIZE: usize = 256;
29 const LUT_BITS: u32 = 8;
30 const LUT_MASK: u32 = (1 << (QUADRANT_BITS - LUT_BITS)) - 1;
31
32 static SIN_LUT: [Fixed; 257] = include!("sin_table.rs.inc");
34
35 #[inline(always)]
36 pub fn sin_fixed(angle: Angle) -> Fixed {
37 let quadrant = (angle >> QUADRANT_BITS) as usize;
38 let idx = (angle & 0x3FFF) as u32;
39 let lut_idx = (idx >> (QUADRANT_BITS - LUT_BITS)) as usize;
40 let frac = (idx & LUT_MASK) as i32;
41
42 match quadrant {
43 0 => interpolate(SIN_LUT[lut_idx], SIN_LUT[lut_idx + 1], frac),
44 1 => interpolate(SIN_LUT[LUT_SIZE - lut_idx], SIN_LUT[LUT_SIZE - lut_idx - 1], frac),
45 2 => -interpolate(SIN_LUT[lut_idx], SIN_LUT[lut_idx + 1], frac),
46 _ => -interpolate(SIN_LUT[LUT_SIZE - lut_idx], SIN_LUT[LUT_SIZE - lut_idx - 1], frac),
47 }
48 }
49
50 #[inline(always)]
51 fn interpolate(y0: Fixed, y1: Fixed, frac: i32) -> Fixed {
52 let y0_32 = y0 as i32;
53 let y1_32 = y1 as i32;
54 (y0_32 + (((y1_32 - y0_32) * frac) >> (QUADRANT_BITS - LUT_BITS))) as Fixed
55 }
56}
57
58#[cfg(feature = "lut")]
60pub use lut_impl::sin_fixed;
61
62#[cfg(feature = "lut")]
63#[inline(always)]
64pub fn cos_fixed(angle: Angle) -> Fixed {
65 sin_fixed(angle.wrapping_add(16384))
66}
67
68#[cfg(feature = "lut")]
69#[inline(always)]
70pub fn sin_cos(angle: Angle) -> (Fixed, Fixed) {
71 (sin_fixed(angle), cos_fixed(angle))
72}
73#[cfg(feature = "taylor")]
77pub mod taylor_impl {
78 use super::{Angle, Fixed};
79
80 pub fn sin_taylor(angle: Angle) -> Fixed {
81 let x_input = if angle > 32768 { 65536 - angle as i32 } else { angle as i32 };
82 let x = if x_input > 16384 { 32768 - x_input } else { x_input };
83
84 let x_rad = (x * 51472) >> 14;
85
86 let x2 = (x_rad * x_rad) >> 15;
87 let x3 = (x2 * x_rad) >> 15;
88 let x5 = (((x3 * x2) >> 15) * x2) >> 15;
89
90 let term3 = (x3 * 5461) >> 15;
91 let term5 = (x5 * 273) >> 15;
92
93 let res = (x_rad - term3 + term5) as Fixed;
95
96 if angle > 32768 { -res } else { res }
97 }
98
99 pub fn cos_taylor(angle: super::Angle) -> super::Fixed {
100 sin_taylor(angle.wrapping_add(16384))
101 }
102
103
104
105}
106
107
108#[cfg(feature = "fast-sin")]
112pub mod fast_impl {
113 use super::{Angle, Fixed};
114
115 pub fn sin_fast(angle: Angle) -> Fixed {
116 let x = (angle & 0x7FFF) as i32;
118 let pi = 32768i32;
119
120 let x_pi_x = (x * (pi - x)) >> 15; let num = (x_pi_x as i64) * 16;
126 let den = (5 * 32768) - ((4 * x_pi_x) >> 0); let res = (num * 32767) / den as i64;
130
131 let val = res as Fixed;
132 if angle > 32768 { -val } else { val }
133 }
134
135 pub fn cos_fast(angle: super::Angle) -> super::Fixed {
136 sin_fast(angle.wrapping_add(16384))
137 }
138
139
140}
141
142
143#[inline(always)]
148pub fn to_fixed(x: f32) -> Fixed { (x * 32767.0) as Fixed }
149
150#[inline(always)]
151pub fn from_fixed(x: Fixed) -> f32 { (x as f32) / 32767.0 }
152
153#[inline(always)]
154pub fn radians_to_angle(rads: f32) -> Angle {
155 let scale = 65536.0 / (2.0 * core::f32::consts::PI);
156 (rads * scale) as i32 as u16
157}
158
159#[cfg(test)]
163mod tests {
164 extern crate std;
165 use super::*;
166 use core::f32::consts::PI;
167
168 #[cfg(feature = "lut")]
169 #[test]
170 fn test_sin_fixed_precision() {
171 assert!((sin_fixed(0) - 0).abs() <= 1);
173 assert!((sin_fixed(16384) - 32767).abs() <= 1); assert!((sin_fixed(32768) - 0).abs() <= 1); assert!((sin_fixed(49152) - (-32767)).abs() <= 1); let res_raw = sin_fixed(8192);
179 let expected_raw = 23203;
180 assert_eq!(res_raw, expected_raw, "Erreur de précision à 45°");
181 }
182
183 #[cfg(feature = "lut")]
184 #[test]
185 fn test_cos_fixed() {
186 assert!((cos_fixed(0) - 32767).abs() <= 1);
188 assert!(cos_fixed(16384).abs() <= 1);
189 assert!((cos_fixed(32768) - (-32767)).abs() <= 1);
190 }
191
192 #[cfg(feature = "taylor")]
193 #[test]
194 fn test_taylor_accuracy() {
195 let res = taylor_impl::sin_taylor(8192); let expected = 23170;
197 assert!((res - expected).abs() < 1000);
198 }
199
200 #[cfg(feature = "fast-sin")]
201 #[test]
202 fn test_fast_sin_approximation() {
203 let res = fast_impl::sin_fast(5461); let expected = 16384;
205 assert!((res - expected).abs() < 1500);
206 }
207
208 #[test]
209 fn test_radians_to_angle_wrapping() {
210 assert_eq!(radians_to_angle(0.0), 0);
211 assert_eq!(radians_to_angle(2.0 * PI), 0);
212 let a = radians_to_angle(-PI / 2.0);
213 assert!(a == 49152 || a == 49151);
214 }
215
216 #[test]
217 fn test_fixed_conversion_roundtrip() {
218 let original = 0.5f32;
219 let fixed = to_fixed(original);
220 let back = from_fixed(fixed);
221 assert!((original - back).abs() < 0.0001);
222 }
223
224 #[test]
225 fn test_sin_cos_simultaneous() {
226 #[cfg(feature = "lut")]
227 {
228 let (s, c) = sin_cos(0);
229 assert_eq!(s, 0);
230 assert_eq!(c, 32767);
231 }
232 }
233
234 #[test]
235 fn test_cos_consistency() {
236 let angle_45 = 8192;
237
238 #[cfg(feature = "lut")]
239 assert!((cos_fixed(0) - 32767).abs() <= 1);
240
241 #[cfg(feature = "taylor")]
242 {
243 let res = taylor_impl::cos_taylor(angle_45);
245 assert!((res - 23170).abs() < 1000);
246 }
247
248 #[cfg(feature = "fast-sin")]
249 {
250 let res = fast_impl::cos_fast(0);
252 assert!((res - 32767).abs() < 1500);
253 }
254 }
255}