Repo for designs & driver for a TA7642 powered lightning detector
at main 640 lines 22 kB view raw
1//! A generalised Embassy driver for the Strike Sensor v1 2//! ⁢⁤⁢⁢⁢⁢⁢⁤⁢⁤⁢⁢⁤⁤⁤⁢⁢⁤⁢⁤⁢⁤⁢⁢⁢⁤⁢⁢⁤⁢⁢⁢⁢⁤⁢⁤⁢⁢⁤⁢⁢⁤⁢⁢⁤⁤⁤⁤⁢⁤⁢⁤⁢⁢⁢⁢⁢⁤⁢⁢⁤⁢⁢⁤⁢⁤⁢⁢⁢⁢⁤⁤⁢⁤⁢⁤⁤⁤⁤⁤⁢⁤⁢⁢⁤⁤⁢⁤⁢⁤⁢⁢⁢⁢⁢⁤⁢⁤⁢⁢⁢⁤⁤⁤⁢⁤⁢⁢⁤⁢⁢⁤⁢⁤⁢⁢⁢⁢⁤⁤⁢⁤⁢⁤⁤⁤⁤⁤⁢⁤⁢⁤⁢⁢⁤⁤⁢⁤⁢⁤⁢⁤⁢⁢⁢⁤⁢⁤⁢⁢⁤⁢⁢⁤⁢⁢⁤⁢⁢⁤⁢⁤⁢⁢⁤⁤⁤⁢⁢⁤⁢⁢⁢⁤⁤⁤⁢⁤⁢⁤⁤⁤⁤⁤⁢⁤⁢⁤⁢⁤⁢⁢⁢⁤⁢⁤⁢⁢⁤⁢⁢⁤⁢⁢⁤⁢⁢⁤⁢⁤⁢⁢⁢⁤⁤⁤⁢⁤⁢⁢⁢⁤⁤⁤⁢⁤⁢⁢⁢⁤⁢⁤⁢⁤⁢⁤⁢⁢⁤⁢⁢⁤⁢⁤⁤⁤⁤⁤⁢⁤⁢⁤⁢⁢⁤⁢⁢⁤⁢⁢⁢⁤⁢⁤⁢⁤⁢⁢⁢⁤⁢⁢⁢⁤⁢⁢⁢⁢⁢⁤⁢⁤⁢⁢⁢⁢⁤⁤⁢⁤⁢⁤⁢⁤⁢⁢⁢⁤⁢⁢⁢⁤⁢⁤⁢⁤⁢⁢⁢⁤⁢⁢⁢⁤⁢⁤⁤⁤⁤⁤⁢⁤⁢⁤⁢⁤⁢⁢⁢⁤⁢⁢⁤⁢⁢⁢⁢⁤⁢⁢⁤⁢⁢⁤⁢⁤⁢⁢⁤⁤⁤⁢⁢⁤⁢⁢⁤⁢⁤⁤⁢⁤⁢⁢⁤⁢⁢⁤⁢⁤⁢⁢⁤⁤⁤⁢⁢⁤⁢⁢⁢⁤⁤⁤⁢⁤⁢⁤⁤⁤⁤⁤⁢⁢⁤⁤⁢⁤⁢⁢⁢⁢⁤⁤⁢⁤⁤⁢⁢⁤⁢⁢⁢⁢⁤⁤⁢⁢⁤⁤⁤⁢⁢⁤⁢⁤⁢⁢⁢⁢⁢⁤⁢⁢⁤⁤⁢⁢⁢⁤⁢⁢⁤⁤⁢⁢⁤⁤⁢⁤⁢⁢⁢⁤⁢⁤⁢⁢⁤⁤⁢⁢⁢⁤⁢⁢⁤⁤⁤⁢⁢⁤⁢⁢⁤⁤⁢⁢⁤⁤⁢⁤⁢⁢⁢⁢⁤⁤⁢⁢⁤⁤⁢⁢⁢⁤⁢⁢⁤⁤⁢⁤⁤⁤⁢⁢⁤⁤⁢⁤⁤⁤⁢⁢⁤⁤⁢⁤⁤⁢⁢⁢⁤⁤⁢⁤⁢⁢⁢⁢⁤⁤⁢⁤⁤⁢⁢⁤⁢⁢⁢⁢⁤⁤⁢⁢⁤⁤⁢⁤⁤⁤⁢⁢⁤⁤⁢⁢⁤⁤⁢⁢⁤⁤⁤⁢⁢⁤⁢⁢⁤⁤⁤⁢⁢⁢⁢⁤⁢⁢⁢⁢⁢⁤⁢⁢⁤⁤⁤⁢⁢⁤⁢⁢⁤⁤⁤⁢⁢⁢⁢⁢⁤⁤⁢⁤⁢⁢⁢⁢⁤⁤⁢⁢⁤⁤⁢⁢⁤⁤⁢⁢⁤⁢⁢⁤⁢⁢⁢⁤⁢⁤⁢⁤⁢⁢⁢⁢⁤⁤⁢⁤⁢⁢⁢⁢⁤⁤⁢⁤⁢⁢⁢⁢⁤⁤⁢⁤⁢⁢⁢⁤⁢⁤⁢⁢⁤⁤⁢⁤⁢⁢⁢⁤⁢⁢⁢⁢⁤⁤⁢⁢⁤⁤⁢⁢⁢⁤⁢⁢⁤⁤⁢⁢⁤⁢⁢⁢⁤⁤⁢⁤⁢⁤⁢⁢⁤⁤⁢⁢⁤⁤⁢⁤⁢⁢⁢⁤⁢⁢⁢⁢⁤⁤⁢⁤⁢⁤⁢⁤⁢⁢⁢⁤⁢⁤⁢⁢⁤⁤⁢⁢⁤⁢⁢⁤⁢⁢⁢⁤⁢⁢⁢⁢⁤⁤⁤⁢⁢⁢⁢⁢⁤⁤⁢⁢⁤⁢⁢⁢⁤⁤⁢⁤⁤⁢⁢⁢⁤⁤⁢⁤⁢⁢⁢⁢⁤⁤⁢⁢⁢⁤⁢⁤⁢⁢⁢⁢⁢⁤⁢⁤⁢⁢⁢⁢⁤⁤⁢⁢⁤⁤⁢⁢⁢⁢⁢⁤⁢⁢⁢⁤⁢⁤⁢⁢⁤⁤⁢⁤⁢⁤⁢⁢⁤⁤⁢⁢⁤⁢⁢⁤⁢⁢⁢⁢⁤⁤⁢⁤⁢⁢⁢⁢⁤⁤⁢⁢⁤⁤⁢⁢⁤⁢⁢⁢⁤⁤⁤⁢⁢⁢⁢⁢⁤⁤⁢⁤⁤⁤⁢⁢⁤⁤⁢⁤⁤⁢⁢⁤⁢⁢⁢⁢⁤⁤⁢⁤⁢⁢⁢⁢⁤⁢⁢⁢⁢⁢⁤⁢⁤⁢⁢⁤⁢⁤⁤⁢⁢⁢⁢⁢⁤⁤⁢⁤⁢⁤⁢⁤⁢⁢⁤⁤⁤⁤⁢⁢⁤⁢⁢⁢⁢⁤⁢⁤⁢⁤⁢⁢⁢⁢⁢⁢⁤⁢⁢⁤⁢⁤⁢⁤⁢⁢⁢⁢⁢⁢⁢⁤⁢⁢⁢⁢⁢⁤⁢⁤⁢⁤⁢⁢⁢⁢⁢⁤⁢⁤⁤⁢⁤⁤⁢⁢⁤⁤⁢⁤⁢⁢⁢⁤⁢⁤⁤⁤⁢⁢⁢⁤⁢⁤⁢⁢⁢⁢⁢⁤⁢⁤⁤⁢⁤⁢⁢⁤⁢⁤⁤⁢⁢⁢⁢⁢⁤⁤⁢⁤⁢⁤⁢⁢⁤⁤⁢⁤⁢⁢⁢⁢⁤⁢⁤⁢⁢⁢⁢⁤⁢⁤⁢⁢⁢⁢⁢⁤⁢⁤⁤⁤⁤⁢⁢⁢⁤⁢⁤⁢⁢⁤⁢⁢⁤⁤⁢⁤⁤⁤⁢⁤⁢⁢⁢⁢⁤⁤⁢⁤⁢⁢⁢⁢⁤⁤⁢⁢⁤⁢⁤⁢⁢⁤⁢⁢⁤⁤⁢⁤⁤⁤⁢⁤⁤⁤⁤⁤⁢⁤⁢⁢⁤⁢⁢⁤⁢⁢⁢⁤⁢⁢⁢⁤⁢⁤⁢⁤⁢⁢⁤⁢⁢⁤⁢⁤⁢⁢⁢⁢⁤⁤⁢⁤⁢⁢⁢⁢⁢⁤⁢⁤⁢⁤⁢⁢⁤⁢⁢⁢⁤⁢⁤⁤⁢⁤⁢⁤⁢⁤⁢⁢⁤⁤⁢⁤⁢⁤⁢⁤⁢⁢⁢⁤⁢⁢⁢⁢⁢⁤⁢⁤⁢⁢⁤⁤⁤⁢⁢⁤⁢⁢⁢⁤⁢⁢⁢⁤⁢⁢⁢⁢⁢⁤⁢⁤⁢⁤⁢⁢⁤⁢⁢⁤⁢⁢⁢⁤⁢⁢⁢⁢⁤⁢⁤⁤⁢⁤⁢⁤⁢⁢⁢⁢⁢⁤⁢⁤⁢⁢⁤⁤⁤⁢⁢⁤⁢⁤⁢⁤⁢⁢⁢⁤⁢⁢⁤⁢⁢⁤⁢⁤⁢⁤⁢⁤⁤⁢⁢⁤⁢⁢⁤⁢⁢⁤⁢⁤⁢⁤⁢⁢⁤⁢⁢⁤⁢⁤⁢⁤⁢⁤⁢⁤⁢⁤⁢⁢⁤⁤⁢⁢⁤⁢⁤⁤⁢⁤⁢⁤⁢⁤⁢⁤⁢⁢⁢⁤⁢⁢⁢⁤⁢⁤⁢⁤⁢⁤⁢⁢⁤⁤⁢⁤⁢⁤⁢⁤⁢⁢⁢⁢⁤⁢⁤⁤⁢⁤⁢⁤⁢⁢⁢⁤⁤⁢⁢⁤⁢⁢⁤⁢⁢⁤⁢⁤⁢⁢⁤⁤⁢⁢⁢⁤⁢⁢⁢⁤⁢⁤⁢⁢⁤⁢⁢⁢⁢⁤⁢⁢⁤⁢⁢⁤⁢⁢⁢⁤⁢⁢⁤⁢⁢⁢⁢⁢⁤⁢⁤⁢⁤⁤⁢⁤⁢⁢⁤⁢⁢⁢⁢⁢⁤⁢⁤⁢⁤⁢ 3#![no_std] 4 5#[cfg(feature = "alloc")] 6extern crate alloc; 7 8mod analysis; 9pub mod drivers; 10pub mod traits; 11 12use core::cell::Cell; 13 14#[cfg(feature = "debug")] 15use defmt::info; 16use embassy_sync::{ 17 blocking_mutex::raw::NoopRawMutex, 18 zerocopy_channel::{Channel, Receiver, Sender}, 19}; 20use embassy_time::{Duration, Instant, Ticker, Timer}; 21 22use crate::traits::{AdcSource, BufferMut, PwmSource, TimeSource}; 23 24pub const BLOCK_SIZE: usize = 512; 25pub type ZeroCopyChannel<'device> = Channel<'device, NoopRawMutex, (i64, [u16; BLOCK_SIZE])>; 26 27#[derive(Debug)] 28#[cfg_attr(feature = "defmt", derive(defmt::Format))] 29struct DetectorState { 30 max_duty: Cell<u8>, 31 duty: Cell<u8>, 32 avg: Cell<u16>, 33 strikes: Cell<u16>, 34 warn_level: Cell<u16>, 35} 36 37impl Default for DetectorState { 38 fn default() -> Self { 39 Self { 40 max_duty: Cell::new(0), 41 duty: Cell::new(0), 42 avg: Cell::new(0), 43 strikes: Cell::new(0), 44 warn_level: Cell::new(255), 45 } 46 } 47} 48 49#[derive(Debug, Clone)] 50#[cfg_attr(feature = "defmt", derive(defmt::Format))] 51pub struct DetectorConfig { 52 blip_threshold: Cell<u16>, 53 blip_size: Cell<usize>, 54} 55 56impl DetectorConfig { 57 pub const fn new(blip_threshold: u16, blip_size: usize) -> Self { 58 Self { 59 blip_threshold: Cell::new(blip_threshold), 60 blip_size: Cell::new(blip_size), 61 } 62 } 63 64 pub const fn blip_threshold(&self) -> u16 { 65 self.blip_threshold.get() 66 } 67 68 pub const fn blip_size(&self) -> usize { 69 self.blip_size.get() 70 } 71 72 pub fn set_blip_threshold(&self, blip_threshold: u16) { 73 self.blip_threshold.set(blip_threshold); 74 } 75 76 pub fn set_blip_size(&self, blip_size: usize) { 77 self.blip_size.set(blip_size); 78 } 79} 80 81impl Default for DetectorConfig { 82 fn default() -> Self { 83 Self::new(14, 2) 84 } 85} 86 87#[derive(Debug, Clone, Copy, PartialEq, Eq)] 88#[cfg_attr(feature = "defmt", derive(defmt::Format))] 89pub enum DetectorUpdate<'a> { 90 Tick { 91 timestamp: i64, 92 level: u16, 93 }, 94 Detection { 95 timestamp: i64, 96 average: u16, 97 samples: &'a [u16], 98 peaks: &'a [(usize, u16)], 99 }, 100} 101 102pub struct DetectorDriver<T: TimeSource, P: PwmSource, A: AdcSource> { 103 state: DetectorState, 104 config: DetectorConfig, 105 timer: T, 106 pwm: P, 107 adc: A, 108} 109 110impl<T, P, A> DetectorDriver<T, P, A> 111where 112 T: TimeSource, 113 P: PwmSource, 114 A: AdcSource, 115{ 116 pub fn new(config: DetectorConfig, timer: T, pwm: P, adc: A) -> Self { 117 Self { 118 state: Default::default(), 119 config, 120 timer, 121 pwm, 122 adc, 123 } 124 } 125 126 pub async fn reset_timer_source(&mut self, timer_source: T::Source) { 127 self.timer.reset_from_source(timer_source).await; 128 } 129 130 pub fn get_timestamp(&self) -> i64 { 131 self.timer.timestamp() 132 } 133 134 pub fn set_blip_threshold(&self, threshold: u16) { 135 self.config.blip_threshold.set(threshold); 136 } 137 138 pub fn set_blip_size(&self, size: usize) { 139 self.config.blip_size.set(size); 140 } 141 142 pub fn set_config(&self, config: DetectorConfig) { 143 self.set_blip_size(config.blip_size()); 144 self.set_blip_threshold(config.blip_threshold()); 145 } 146 147 pub fn get_config(&self) -> DetectorConfig { 148 self.config.clone() 149 } 150 151 pub async fn tune(&mut self, samples: &mut [u16]) { 152 let mut duty = 0; 153 let max_duty = self.pwm.max_duty(); 154 self.pwm.set_duty(duty); 155 Timer::after_secs(2).await; 156 let mut act_value = self.adc.sample_average(samples).await; 157 #[cfg(feature = "debug")] 158 info!("START: {}", act_value); 159 160 while act_value < 1500 { 161 duty += 2; 162 if duty >= max_duty { 163 #[cfg(feature = "debug")] 164 info!("RESET: {} @ Duty {}", act_value, duty); 165 duty = 0; 166 self.pwm.set_duty(duty); 167 Timer::after_secs(2).await; 168 act_value = self.adc.sample_average(samples).await; 169 continue; 170 } 171 self.pwm.set_duty(duty); 172 Timer::after_millis(250).await; 173 act_value = self.adc.sample_average(samples).await; 174 #[cfg(feature = "debug")] 175 info!("UPDATE: {} @ Duty {}", act_value, duty); 176 } 177 self.state.max_duty.set(duty as u8); 178 duty = (duty / 6) * 5; 179 self.pwm.set_duty(duty); 180 self.state.duty.set(duty as u8); 181 // Allow voltage level to stabilize after tuning 182 Timer::after_secs(2).await; 183 let avg = self.adc.sample_average(samples).await; 184 #[cfg(feature = "debug")] 185 info!("SET: {} @ Duty {}/{}", avg, duty, self.state.max_duty.get()); 186 self.state.avg.set(avg); 187 } 188 189 /// Samples and returns an `i64` timestamp. 190 pub async fn sample_with_zerocopy<'a>( 191 &self, 192 dma: &mut Sender<'a, NoopRawMutex, (i64, [u16; BLOCK_SIZE])>, 193 ) { 194 loop { 195 let (time, samples) = dma.send().await; 196 self.adc.sample(samples).await; 197 *time = self.get_timestamp(); 198 dma.send_done(); 199 } 200 } 201 202 pub fn detect_from_sample<B, F>( 203 &self, 204 timestamp: i64, 205 samples: &[u16], 206 peaks: &mut B, 207 update: F, 208 ) where 209 B: BufferMut<(usize, u16)>, 210 F: Fn(DetectorUpdate<'_>), 211 { 212 peaks.clear(); 213 214 let new_avg = analysis::analyse_buffer_by_stepped_windows( 215 self.config.blip_threshold.get(), 216 samples, 217 self.state.avg.get(), 218 peaks, 219 ); 220 221 let blips = peaks.len(); 222 223 if blips >= self.config.blip_size.get() { 224 self.state 225 .strikes 226 .update(|strike| strike.saturating_add(32)); 227 228 update(DetectorUpdate::Detection { 229 timestamp, 230 average: self.state.avg.get(), 231 samples, 232 peaks: peaks.as_slice(), 233 }); 234 } 235 236 self.state.avg.set(new_avg); 237 } 238 239 pub async fn detect_with_zerocopy<'a, 'd, B, F>( 240 &self, 241 dma: &mut Receiver<'a, NoopRawMutex, (i64, [u16; BLOCK_SIZE])>, 242 peaks: &'d mut B, 243 update: F, 244 ) where 245 B: BufferMut<(usize, u16)>, 246 F: Fn(DetectorUpdate<'_>), 247 { 248 loop { 249 let (timestamp, samples) = dma.receive().await; 250 251 self.detect_from_sample(*timestamp, samples, peaks, &update); 252 253 dma.receive_done(); 254 } 255 } 256 257 pub async fn tick<F>(&self, update: F) 258 where 259 F: Fn(DetectorUpdate), 260 { 261 let mut inactive: Option<Instant> = None; 262 let mut interval = Ticker::every(Duration::from_secs(1)); 263 264 loop { 265 interval.next().await; 266 let strikes = self.state.strikes.get(); 267 let mut warn_level = self.state.warn_level.get(); 268 269 if strikes > 32 { 270 warn_level = warn_level.saturating_add(strikes); 271 } 272 273 let decay = warn_level >> 8; 274 275 self.state.strikes.set(0); 276 self.state.warn_level.set(warn_level - decay); 277 278 match inactive { 279 Some(_) if decay > 0 => { 280 inactive = None; 281 } 282 Some(val) if val.elapsed() >= Duration::from_secs(3600) => break, 283 None if decay == 0 => { 284 inactive = Some(Instant::now()); 285 } 286 None => { 287 update(DetectorUpdate::Tick { 288 timestamp: self.get_timestamp(), 289 level: warn_level - 255, 290 }); 291 } 292 _ => continue, 293 } 294 } 295 } 296} 297 298#[cfg(test)] 299mod tests { 300 use core::{future::poll_fn, ops::RangeBounds, slice::SliceIndex}; 301 use embassy_time::MockDriver; 302 use rand::{Rng, distr::uniform::SampleRange}; 303 304 #[cfg(not(feature = "alloc"))] 305 extern crate alloc; 306 use super::*; 307 308 #[derive(Debug, Default)] 309 struct MockMachine { 310 pwm: Cell<u16>, 311 } 312 313 impl MockMachine { 314 fn adc_sample_avg(&self) -> u16 { 315 self.pwm.get().saturating_mul(14) 316 } 317 } 318 319 struct MockPwm<'a>(&'a MockMachine); 320 321 impl PwmSource for MockPwm<'_> { 322 fn max_duty(&self) -> u16 { 323 255 324 } 325 326 fn set_duty(&mut self, duty: u16) { 327 self.0.pwm.set(duty); 328 } 329 330 // These do nothing 331 fn enable(&mut self) {} 332 fn disable(&mut self) {} 333 } 334 335 struct MockAdc<'a>(&'a MockMachine); 336 337 impl AdcSource for MockAdc<'_> { 338 async fn sample(&self, samples: &mut [u16]) { 339 samples.fill(self.0.adc_sample_avg()); 340 } 341 342 async fn sample_average(&self, _samples: &mut [u16]) -> u16 { 343 self.0.adc_sample_avg() 344 } 345 } 346 347 struct MockTimeSource; 348 349 impl TimeSource for MockTimeSource { 350 type Source = (); 351 352 async fn reset_from_source(&mut self, _source: Self::Source) {} 353 354 fn timestamp(&self) -> i64 { 355 0 356 } 357 } 358 359 #[cfg(not(feature = "alloc"))] 360 impl BufferMut<(usize, u16)> for alloc::vec::Vec<(usize, u16)> { 361 fn push(&mut self, value: (usize, u16)) { 362 self.push(value); 363 } 364 365 fn clear(&mut self) { 366 self.clear(); 367 } 368 369 fn len(&self) -> usize { 370 self.len() 371 } 372 373 fn is_empty(&self) -> bool { 374 self.is_empty() 375 } 376 377 fn as_slice(&self) -> &[(usize, u16)] { 378 self 379 } 380 } 381 382 async fn tune_detector_manually<'a>( 383 detector: &mut DetectorDriver<MockTimeSource, MockPwm<'a>, MockAdc<'a>>, 384 buf: &mut [u16], 385 driver: &MockDriver, 386 ) { 387 let mut tuning = core::pin::pin!(detector.tune(buf)); 388 389 poll_fn(|cx| match tuning.as_mut().poll(cx) { 390 core::task::Poll::Ready(_) => core::task::Poll::Ready(()), 391 core::task::Poll::Pending => { 392 cx.waker().wake_by_ref(); 393 driver.advance(Duration::from_secs(2)); 394 395 core::task::Poll::Pending 396 } 397 }) 398 .await; 399 } 400 401 fn generate_noisy_signal< 402 I: SliceIndex<[u16], Output = [u16]> + RangeBounds<usize>, 403 S: RangeBounds<i16> + SampleRange<i16> + Clone, 404 >( 405 samples: &mut [u16], 406 amplitude: S, 407 range: I, 408 ) { 409 let mut noise = wyrand::WyRand::new(141); 410 411 let samples = samples 412 .get_mut(range) 413 .expect("Range to be sized the same or smaller than the slice"); 414 415 for sample in samples.iter_mut() { 416 *sample = ((*sample as i16) - noise.random_range(amplitude.clone())) as u16; 417 } 418 } 419 420 fn generate_drop_signal(samples: &mut [u16]) { 421 // Example voltage drop signal 422 samples[5] -= 60; 423 samples[6] -= 55; 424 samples[7] -= 50; 425 samples[8] -= 45; 426 samples[9] -= 40; 427 samples[10] -= 35; 428 samples[11] -= 30; 429 samples[12] -= 28; 430 samples[13] -= 26; 431 samples[14] -= 24; 432 samples[15] -= 22; 433 samples[16] -= 20; 434 samples[17] -= 18; 435 samples[18] -= 16; 436 samples[19] -= 14; 437 samples[20] -= 12; 438 samples[21] -= 11; 439 samples[22] -= 10; 440 samples[23] -= 9; 441 samples[24] -= 8; 442 samples[25] -= 7; 443 samples[26] -= 6; 444 samples[27] -= 5; 445 samples[28] -= 4; 446 samples[29] -= 3; 447 samples[30] -= 2; 448 samples[31] -= 1; 449 } 450 451 #[pollster::test] 452 async fn tuning_cycle_completes() { 453 let driver = embassy_time::MockDriver::get(); 454 driver.reset(); 455 let device = MockMachine::default(); 456 let pwm = MockPwm(&device); 457 let adc = MockAdc(&device); 458 459 let mut detector = DetectorDriver::new(Default::default(), MockTimeSource, pwm, adc); 460 let mut buf = [0; 16]; 461 462 tune_detector_manually(&mut detector, &mut buf, driver).await; 463 464 assert_eq!(detector.state.max_duty.get(), 108); 465 assert_eq!(detector.state.duty.get(), 90); 466 assert_eq!(detector.adc.sample_average(&mut buf).await, 1260); 467 } 468 469 #[pollster::test] 470 async fn tick_updates_only_on_raised_levels() { 471 let driver = embassy_time::MockDriver::get(); 472 driver.reset(); 473 let device = MockMachine::default(); 474 let pwm = MockPwm(&device); 475 let adc = MockAdc(&device); 476 477 let detector = DetectorDriver::new(Default::default(), MockTimeSource, pwm, adc); 478 479 detector.state.max_duty.set(98); 480 detector.state.duty.set(64); 481 detector.state.avg.set(896); 482 483 { 484 let mut tick = core::pin::pin!(detector.tick(|a| { 485 assert_eq!( 486 a, 487 DetectorUpdate::Tick { 488 timestamp: 0, 489 level: 300 490 } 491 ); 492 })); 493 494 poll_fn(|cx| match tick.as_mut().poll(cx) { 495 core::task::Poll::Ready(_) => core::task::Poll::Ready(()), 496 core::task::Poll::Pending => { 497 if detector.state.warn_level.get() > 255 { 498 return core::task::Poll::Ready(()); 499 } 500 detector.state.strikes.set(300); 501 driver.advance(Duration::from_secs(1)); 502 cx.waker().wake_by_ref(); 503 core::task::Poll::Pending 504 } 505 }) 506 .await; 507 } 508 509 assert_eq!(detector.state.warn_level.get(), 553); 510 } 511 512 #[pollster::test] 513 async fn detection_updates_only_on_large_enough_signals() { 514 let driver = embassy_time::MockDriver::get(); 515 driver.reset(); 516 let device = MockMachine::default(); 517 let pwm = MockPwm(&device); 518 let adc = MockAdc(&device); 519 520 let detector = DetectorDriver::new(Default::default(), MockTimeSource, pwm, adc); 521 detector.pwm.0.pwm.set(64); 522 detector.state.max_duty.set(98); 523 detector.state.duty.set(64); 524 detector.state.avg.set(896); 525 526 let mut samples = alloc::vec![0; BLOCK_SIZE]; 527 528 detector.adc.sample(&mut samples).await; 529 530 samples[5] -= 3; 531 samples[6] -= 1; 532 533 let mut peaks = alloc::vec::Vec::with_capacity(512); 534 535 detector.detect_from_sample(0, &samples, &mut peaks, |_| { 536 panic!("This update function shouldn't be called"); 537 }); 538 539 assert_eq!(peaks.len(), 0); 540 541 generate_drop_signal(&mut samples); 542 543 let expected_peaks = alloc::vec![(0, 25), (8, 21)]; 544 let called = Cell::new(false); 545 546 detector.detect_from_sample(0, &samples, &mut peaks, |update: DetectorUpdate<'_>| { 547 called.set(true); 548 assert_eq!( 549 update, 550 DetectorUpdate::Detection { 551 timestamp: 0, 552 average: detector.state.avg.get(), 553 samples: &samples, 554 peaks: expected_peaks.as_slice() 555 } 556 ) 557 }); 558 559 assert_eq!(peaks.len(), 2); 560 assert!(called.get()); 561 562 called.set(false); 563 564 let mut samples = alloc::vec![0; BLOCK_SIZE]; 565 let expected_peaks = alloc::vec![(16, 15), (24, 19), (32, 18)]; 566 567 detector.adc.sample(&mut samples).await; 568 569 // Big noisy signal 570 generate_noisy_signal(&mut samples, -40..=40, 20..55); 571 572 detector.detect_from_sample(0, &samples, &mut peaks, |update| { 573 called.set(true); 574 assert_eq!( 575 update, 576 DetectorUpdate::Detection { 577 timestamp: 0, 578 average: detector.state.avg.get(), 579 samples: &samples, 580 peaks: expected_peaks.as_slice() 581 } 582 ) 583 }); 584 585 assert!(called.get()); 586 assert_eq!(peaks.len(), 3); 587 588 detector.adc.sample(&mut samples).await; 589 590 // Smaller noisy signal 591 generate_noisy_signal(&mut samples, -15..=15, 10..75); 592 593 detector.detect_from_sample(0, &samples, &mut peaks, |_| { 594 panic!("This update function shouldn't be called"); 595 }); 596 597 assert_eq!(peaks.len(), 0); 598 } 599 600 #[pollster::test] 601 async fn detection_sensitivity_can_be_configured() { 602 let driver = embassy_time::MockDriver::get(); 603 driver.reset(); 604 let device = MockMachine::default(); 605 let pwm = MockPwm(&device); 606 let adc = MockAdc(&device); 607 608 let detector = DetectorDriver::new(Default::default(), MockTimeSource, pwm, adc); 609 detector.pwm.0.pwm.set(64); 610 detector.state.max_duty.set(98); 611 detector.state.duty.set(64); 612 detector.state.avg.set(896); 613 614 let mut samples = alloc::vec![0; BLOCK_SIZE]; 615 let mut peaks = alloc::vec::Vec::with_capacity(BLOCK_SIZE); 616 617 // Require bigger size blips. 618 detector.config.blip_size.set(3); 619 620 detector.adc.sample(&mut samples).await; 621 generate_drop_signal(&mut samples); 622 623 detector.detect_from_sample(0, &samples, &mut peaks, |_update| { 624 panic!("Update shouldn't be called"); 625 }); 626 627 assert_eq!(peaks.len(), 2); 628 629 // Require bigger threshold for blip detection 630 detector.config.blip_size.set(2); 631 detector.config.blip_threshold.set(24); 632 633 detector.detect_from_sample(0, &samples, &mut peaks, |_update| { 634 panic!("Update shouldn't be called"); 635 }); 636 637 // Update didn't call because detected blip wasn't big enough 638 assert_eq!(peaks.len(), 1); 639 } 640}