1use std::f32::consts::PI;
2
3use autd3_core::{
4 common::{Angle, Freq, rad},
5 derive::*,
6 firmware::SamplingConfig,
7};
8
9use super::sampling_mode::{Nearest, SamplingMode};
10
11#[derive(Clone, Copy, Debug, PartialEq)]
13pub struct SineOption {
14 pub intensity: u8,
16 pub offset: u8,
18 pub phase: Angle,
20 pub clamp: bool,
22 pub sampling_config: SamplingConfig,
24}
25
26impl Default for SineOption {
27 fn default() -> Self {
28 Self {
29 intensity: u8::MAX,
30 offset: 0x80,
31 phase: 0. * rad,
32 clamp: false,
33 sampling_config: SamplingConfig::FREQ_4K,
34 }
35 }
36}
37
38#[derive(Modulation, Clone, Copy, PartialEq, Debug)]
42pub struct Sine<S: Into<SamplingMode> + Clone + Copy + std::fmt::Debug> {
43 pub freq: S,
45 pub option: SineOption,
47}
48
49impl<S: Into<SamplingMode> + Clone + Copy + std::fmt::Debug> Sine<S> {
50 #[must_use]
52 pub const fn new(freq: S, option: SineOption) -> Self {
53 Self { freq, option }
54 }
55}
56
57impl Sine<Freq<f32>> {
58 #[must_use]
70 pub const fn into_nearest(self) -> Sine<Nearest> {
71 Sine {
72 freq: Nearest(self.freq),
73 option: self.option,
74 }
75 }
76}
77
78impl<S: Into<SamplingMode> + Clone + Copy + std::fmt::Debug> Sine<S> {
79 pub(super) fn calc_raw(&self) -> Result<impl Iterator<Item = f32>, ModulationError> {
80 let sampling_mode: SamplingMode = self.freq.into();
81 let (n, rep) = sampling_mode.validate(self.option.sampling_config)?;
82 let intensity = self.option.intensity;
83 let offset = self.option.offset;
84 let phase = self.option.phase.radian();
85 Ok((0..n).map(move |i| {
86 (intensity as f32 / 2. * (2.0 * PI * (rep * i) as f32 / n as f32 + phase).sin())
87 + offset as f32
88 }))
89 }
90}
91
92impl<S: Into<SamplingMode> + Clone + Copy + std::fmt::Debug> Modulation for Sine<S> {
93 fn calc(self) -> Result<Vec<u8>, ModulationError> {
94 self.calc_raw()?
95 .map(|v| v.floor() as i16)
96 .map(|v| {
97 if (u8::MIN as i16..=u8::MAX as _).contains(&v) {
98 Ok(v as _)
99 } else if self.option.clamp {
100 Ok(v.clamp(u8::MIN as _, u8::MAX as _) as _)
101 } else {
102 Err(ModulationError::new(format!(
103 "Sine modulation value ({}) is out of range [{}, {}]",
104 v,
105 u8::MIN,
106 u8::MAX,
107 )))?
108 }
109 })
110 .collect::<Result<Vec<_>, ModulationError>>()
111 }
112
113 fn sampling_config(&self) -> SamplingConfig {
114 self.option.sampling_config
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use autd3_driver::common::{Hz, rad};
121
122 use super::*;
123
124 #[rstest::rstest]
125 #[case(
126 Ok(vec![
127 128, 157, 185, 210, 231, 245, 253, 255, 249, 236, 218, 194, 167, 138, 108, 79, 53, 31, 14, 4, 0, 4, 14, 31, 53, 79, 108, 138, 167, 194, 218, 236, 249, 255, 253, 245, 231, 210, 185, 157, 128, 98, 70, 45, 24, 10, 2, 0, 6, 19, 37, 61, 88, 117, 147, 176, 202, 224, 241, 251, 255, 251, 241, 224, 202, 176, 147, 117, 88, 61, 37, 19, 6, 0, 2, 10, 24, 45, 70, 98,
128 ]),
129 150.*Hz
130 )]
131 #[case(
132 Ok(vec![
133 128, 157, 185, 210, 231, 245, 253, 255, 249, 236, 218, 194, 167, 138, 108, 79, 53, 31, 14, 4, 0, 4, 14, 31, 53, 79, 108, 138, 167, 194, 218, 236, 249, 255, 253, 245, 231, 210, 185, 157, 128, 98, 70, 45, 24, 10, 2, 0, 6, 19, 37, 61, 88, 117, 147, 176, 202, 224, 241, 251, 255, 251, 241, 224, 202, 176, 147, 117, 88, 61, 37, 19, 6, 0, 2, 10, 24, 45, 70, 98,
134 ]),
135 150*Hz
136 )]
137 #[case(
138 Ok(vec![128, 167, 202, 231, 249, 255, 249, 231, 202, 167, 127, 88, 53, 24, 6, 0, 6, 24, 53, 88]),
139 200.*Hz
140 )]
141 #[case(
142 Ok(vec![128, 167, 202, 231, 249, 255, 249, 231, 202, 167, 127, 88, 53, 24, 6, 0, 6, 24, 53, 88]),
143 200*Hz
144 )]
145 #[case(
146 Ok(vec![
147 128, 248, 208, 62, 2, 109, 240, 222, 79, 0, 90, 230, 234, 97, 1, 73, 218, 243, 115, 4, 57, 203, 250, 134, 10, 42, 188, 254, 152, 18, 29, 170, 255, 170, 29, 18, 152, 254, 188, 42, 10, 134, 250, 203, 57, 4, 115, 243, 218, 73, 1, 97, 234, 230, 90, 0, 79, 222, 240, 109, 2, 62, 208, 248, 127, 7, 47, 193, 253, 146, 15, 33, 176, 255, 165, 25, 21, 158, 254, 182, 37, 12, 140, 251, 198, 52, 5, 121, 245, 213, 67, 1, 103, 237, 226, 85, 0, 85, 226, 237, 103, 1, 67, 213, 245, 121, 5, 52, 198, 251, 140, 12, 37, 182, 254, 158, 21, 25, 165, 255, 176, 33, 15, 146, 253, 193, 47, 7
148 ]),
149 781.25*Hz
150 )]
151 #[case(
152 Err(ModulationError::new("Frequency (150.01 Hz) cannot be output with the sampling config (SamplingConfig::Freq(4000 Hz)).")),
153 150.01*Hz
154 )]
155 #[case(
156 Err(ModulationError::new("Frequency (2000 Hz) is equal to or greater than the Nyquist frequency (2000 Hz)")),
157 2000.*Hz
158 )]
159 #[case(
160 Err(ModulationError::new("Frequency (2000 Hz) is equal to or greater than the Nyquist frequency (2000 Hz)")),
161 2000*Hz
162 )]
163 #[case(
164 Err(ModulationError::new("Frequency (4000 Hz) is equal to or greater than the Nyquist frequency (2000 Hz)")),
165 4000.*Hz
166 )]
167 #[case(
168 Err(ModulationError::new("Frequency (4000 Hz) is equal to or greater than the Nyquist frequency (2000 Hz)")),
169 4000*Hz
170 )]
171 #[case(
172 Err(ModulationError::new("Frequency (-0.1 Hz) must be valid positive value")),
173 -0.1*Hz
174 )]
175 #[case(
176 Err(ModulationError::new("Frequency must not be zero. If intentional, use `Static` instead.")),
177 0*Hz
178 )]
179 #[case(
180 Err(ModulationError::new("Frequency must not be zero. If intentional, use `Static` instead.")),
181 0.*Hz
182 )]
183 fn new(
184 #[case] expect: Result<Vec<u8>, ModulationError>,
185 #[case] freq: impl Into<SamplingMode> + Copy + std::fmt::Debug,
186 ) {
187 let m = Sine::new(freq, SineOption::default());
188 assert_eq!(u8::MAX, m.option.intensity);
189 assert_eq!(0x80, m.option.offset);
190 assert_eq!(0. * rad, m.option.phase);
191 assert_eq!(SamplingConfig::FREQ_4K, m.sampling_config());
192 assert_eq!(expect, m.calc());
193 }
194
195 #[rstest::rstest]
196 #[case(
197 Ok(vec![
198 128, 157, 185, 209, 230, 245, 253, 255, 250, 238, 220, 198, 171, 142, 113, 84, 57, 35, 17, 5, 0, 2, 10, 25, 46, 70, 98,
199 ]),
200 150.*Hz
201 )]
202 #[case(
203 Ok(vec![128, 167, 202, 231, 249, 255, 249, 231, 202, 167, 127, 88, 53, 24, 6, 0, 6, 24, 53, 88]),
204 200.*Hz
205 )]
206 #[case(
207 Err(ModulationError::new("Frequency (NaN Hz) must be valid value")),
208 f32::NAN * Hz
209 )]
210 fn new_nearest(#[case] expect: Result<Vec<u8>, ModulationError>, #[case] freq: Freq<f32>) {
211 let m = Sine {
212 freq,
213 option: SineOption::default(),
214 }
215 .into_nearest();
216 assert_eq!(u8::MAX, m.option.intensity);
217 assert_eq!(0x80, m.option.offset);
218 assert_eq!(0. * rad, m.option.phase);
219 assert_eq!(SamplingConfig::FREQ_4K, m.sampling_config());
220 assert_eq!(expect, m.calc());
221 }
222
223 #[rstest::rstest]
224 #[case(
225 Err(ModulationError::new("Sine modulation value (-1) is out of range [0, 255]")),
226 0x00,
227 false
228 )]
229 #[case(
230 Ok(vec![0, 39, 74, 103, 121, 127, 121, 103, 74, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
231 0x00,
232 true
233 )]
234 #[test]
235 fn out_of_range(
236 #[case] expect: Result<Vec<u8>, ModulationError>,
237 #[case] offset: u8,
238 #[case] clamp: bool,
239 ) {
240 assert_eq!(
241 expect,
242 Sine {
243 freq: 200 * Hz,
244 option: SineOption {
245 offset,
246 clamp,
247 ..Default::default()
248 }
249 }
250 .calc()
251 );
252 }
253}