Repo for designs & driver for a TA7642 powered lightning detector

refactor: Signal detection algorithm tweaks

authored by sachy.dev and committed by

Tangled 163e8938 e3d3de79

+111 -34
+20
Cargo.lock
··· 300 300 "embassy-time", 301 301 "heapless 0.9.2", 302 302 "pollster", 303 + "rand", 304 + "wyrand", 303 305 ] 304 306 305 307 [[package]] ··· 840 842 ] 841 843 842 844 [[package]] 845 + name = "rand" 846 + version = "0.9.2" 847 + source = "registry+https://github.com/rust-lang/crates.io-index" 848 + checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" 849 + dependencies = [ 850 + "rand_core 0.9.5", 851 + ] 852 + 853 + [[package]] 843 854 name = "rand_core" 844 855 version = "0.6.4" 845 856 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1135 1146 checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 1136 1147 dependencies = [ 1137 1148 "windows-link", 1149 + ] 1150 + 1151 + [[package]] 1152 + name = "wyrand" 1153 + version = "0.3.2" 1154 + source = "registry+https://github.com/rust-lang/crates.io-index" 1155 + checksum = "15e0359b0b8d9cdef235a1fd4a8c5d02e4c9204e9fac861c14c229a8e803d1a6" 1156 + dependencies = [ 1157 + "rand_core 0.9.5", 1138 1158 ] 1139 1159 1140 1160 [[package]]
+2
embassy-strike-driver/Cargo.toml
··· 25 25 embassy-time = { workspace = true, features = ["mock-driver", "generic-queue-8"] } 26 26 critical-section = { version = "1.1", features = ["std"] } 27 27 pollster = { version = "0.4", features = ["macro"] } 28 + rand = { version = "0.9", default-features = false } 29 + wyrand = "0.3"
+14 -5
embassy-strike-driver/src/analysis.rs
··· 1 1 use crate::{BLOCK_SIZE, traits::BufferMut}; 2 2 3 - pub fn analyse_buffer_by_stepped_windows<B: BufferMut<usize>>( 3 + pub fn analyse_buffer_by_stepped_windows<B: BufferMut<(usize, u16)>>( 4 4 threshold: u16, 5 5 buf: &[u16], 6 6 average: u16, ··· 13 13 let mut len = 0u32; 14 14 15 15 for (i, window) in buf.windows(CHUNK_SIZE).enumerate().step_by(CHUNK_STEP) { 16 - let window_total = window.iter().copied().sum::<u16>(); 17 - let window_avg = window_total / CHUNK_SIZE as u16; 18 - let diff = average.saturating_sub(window_avg); 16 + let (window_total, window_diff) = 17 + window 18 + .iter() 19 + .copied() 20 + .fold((0, 0), |(total, diff), sample| { 21 + ( 22 + total + sample, 23 + diff + ((average as i16) - sample as i16).unsigned_abs(), 24 + ) 25 + }); 26 + 27 + let diff = window_diff / CHUNK_SIZE as u16; 19 28 20 29 if diff > threshold { 21 - peaks.push(i); 30 + peaks.push((i, diff)); 22 31 } else { 23 32 total += window_total as u32; 24 33 len += CHUNK_SIZE as u32;
+75 -29
embassy-strike-driver/src/lib.rs
··· 67 67 Detection { 68 68 timestamp: i64, 69 69 samples: &'a [u16], 70 - peaks: &'a [usize], 70 + peaks: &'a [(usize, u16)], 71 71 }, 72 72 } 73 73 ··· 112 112 } 113 113 114 114 pub async fn tune(&mut self, samples: &mut [u16]) { 115 - // info!("Tuning Detector for correct voltage settings"); 116 115 let mut duty = 0; 117 116 self.pwm.set_duty(duty); 118 117 Timer::after_secs(2).await; 119 118 let mut act_value = self.adc.sample_average(samples).await; 120 - 121 - // info!("initial ACT: {}", act_value); 122 119 123 120 while act_value < 1364 { 124 121 duty += 2; ··· 127 124 self.pwm.set_duty(duty); 128 125 Timer::after_secs(2).await; 129 126 act_value = self.adc.sample_average(samples).await; 130 - // info!("Restarting tuning"); 131 127 continue; 132 128 } 133 129 self.pwm.set_duty(duty); 134 130 Timer::after_millis(250).await; 135 131 act_value = self.adc.sample_average(samples).await; 136 - // info!("ACT: {}, Duty: {}", act_value, duty); 137 132 } 138 133 self.state.max_duty.set(duty as u8); 139 - duty = (duty / 3) * 2; 134 + duty = (duty / 6) * 5; 140 135 self.pwm.set_duty(duty); 141 136 self.state.duty.set(duty as u8); 142 - // info!("Set detection duty to: {}", duty); 143 137 // Allow voltage level to stabilize after tuning 144 138 Timer::after_secs(2).await; 145 139 let avg = self.adc.sample_average(samples).await; ··· 166 160 peaks: &mut B, 167 161 update: F, 168 162 ) where 169 - B: BufferMut<usize>, 163 + B: BufferMut<(usize, u16)>, 170 164 F: Fn(DetectorUpdate<'_>), 171 165 { 172 166 peaks.clear(); ··· 201 195 peaks: &'d mut B, 202 196 update: F, 203 197 ) where 204 - B: BufferMut<usize>, 198 + B: BufferMut<(usize, u16)>, 205 199 F: Fn(DetectorUpdate<'_>), 206 200 { 207 201 loop { ··· 256 250 257 251 #[cfg(test)] 258 252 mod tests { 259 - use core::future::poll_fn; 253 + use core::{future::poll_fn, ops::RangeBounds, slice::SliceIndex}; 260 254 use embassy_time::MockDriver; 255 + use rand::{Rng, distr::uniform::SampleRange}; 261 256 262 257 #[cfg(not(feature = "alloc"))] 263 258 extern crate alloc; ··· 310 305 } 311 306 312 307 #[cfg(not(feature = "alloc"))] 313 - impl BufferMut<usize> for alloc::vec::Vec<usize> { 314 - fn push(&mut self, value: usize) { 308 + impl BufferMut<(usize, u16)> for alloc::vec::Vec<(usize, u16)> { 309 + fn push(&mut self, value: (usize, u16)) { 315 310 self.push(value); 316 311 } 317 312 ··· 327 322 self.is_empty() 328 323 } 329 324 330 - fn as_slice(&self) -> &[usize] { 325 + fn as_slice(&self) -> &[(usize, u16)] { 331 326 self 332 327 } 333 328 } ··· 351 346 .await; 352 347 } 353 348 354 - fn generate_signal(samples: &mut [u16]) { 349 + fn generate_noisy_signal< 350 + I: SliceIndex<[u16], Output = [u16]> + RangeBounds<usize>, 351 + S: RangeBounds<i16> + SampleRange<i16> + Clone, 352 + >( 353 + samples: &mut [u16], 354 + amplitude: S, 355 + range: I, 356 + ) { 357 + let mut noise = wyrand::WyRand::new(141); 358 + 359 + let samples = samples 360 + .get_mut(range) 361 + .expect("Range to be sized the same or smaller than the slice"); 362 + 363 + for sample in samples.iter_mut() { 364 + *sample = ((*sample as i16) - noise.random_range(amplitude.clone())) as u16; 365 + } 366 + } 367 + 368 + fn generate_drop_signal(samples: &mut [u16]) { 355 369 // Example voltage drop signal 356 370 samples[5] -= 60; 357 371 samples[6] -= 55; ··· 396 410 tune_detector_manually(&mut detector, &mut buf, driver).await; 397 411 398 412 assert_eq!(detector.state.max_duty.get(), 98); 399 - assert_eq!(detector.state.duty.get(), 64); 400 - assert_eq!(detector.adc.sample_average(&mut buf).await, 896); 413 + assert_eq!(detector.state.duty.get(), 80); 414 + assert_eq!(detector.adc.sample_average(&mut buf).await, 1120); 401 415 } 402 416 403 417 #[pollster::test] ··· 466 480 467 481 let mut peaks = alloc::vec::Vec::with_capacity(512); 468 482 469 - let update = |_update: DetectorUpdate<'_>| { 483 + detector.detect_from_sample(0, &samples, &mut peaks, |_| { 470 484 panic!("This update function shouldn't be called"); 471 - }; 472 - 473 - detector.detect_from_sample(0, &samples, &mut peaks, update); 485 + }); 474 486 475 487 assert_eq!(peaks.len(), 0); 476 488 477 - generate_signal(&mut samples); 489 + generate_drop_signal(&mut samples); 478 490 479 - let expected_peaks = alloc::vec![0, 8]; 491 + let expected_peaks = alloc::vec![(0, 25), (8, 21)]; 480 492 let called = Cell::new(false); 481 493 482 - let update = |update: DetectorUpdate<'_>| { 494 + detector.detect_from_sample(0, &samples, &mut peaks, |update: DetectorUpdate<'_>| { 483 495 called.set(true); 484 496 assert_eq!( 485 497 update, ··· 489 501 peaks: expected_peaks.as_slice() 490 502 } 491 503 ) 492 - }; 493 - 494 - detector.detect_from_sample(0, &samples, &mut peaks, update); 504 + }); 495 505 496 506 assert_eq!(peaks.len(), 2); 497 507 assert!(called.get()); 508 + 509 + called.set(false); 510 + 511 + let mut samples = alloc::vec![0; BLOCK_SIZE]; 512 + let expected_peaks = alloc::vec![(16, 15), (24, 19), (32, 18)]; 513 + 514 + detector.adc.sample(&mut samples).await; 515 + 516 + // Big noisy signal 517 + generate_noisy_signal(&mut samples, -40..=40, 20..55); 518 + 519 + detector.detect_from_sample(0, &samples, &mut peaks, |update| { 520 + called.set(true); 521 + assert_eq!( 522 + update, 523 + DetectorUpdate::Detection { 524 + timestamp: 0, 525 + samples: &samples, 526 + peaks: expected_peaks.as_slice() 527 + } 528 + ) 529 + }); 530 + 531 + assert_eq!(peaks.len(), 3); 532 + assert!(called.get()); 533 + 534 + detector.adc.sample(&mut samples).await; 535 + 536 + // Smaller noisy signal 537 + generate_noisy_signal(&mut samples, -15..=15, 10..75); 538 + 539 + detector.detect_from_sample(0, &samples, &mut peaks, |_| { 540 + panic!("This update function shouldn't be called"); 541 + }); 542 + 543 + assert_eq!(peaks.len(), 0); 498 544 } 499 545 500 546 #[pollster::test] ··· 512 558 detector.state.avg.set(896); 513 559 514 560 let mut samples = alloc::vec![0; BLOCK_SIZE]; 515 - let mut peaks: alloc::vec::Vec<usize> = alloc::vec::Vec::with_capacity(BLOCK_SIZE); 561 + let mut peaks = alloc::vec::Vec::with_capacity(BLOCK_SIZE); 516 562 517 563 // Require bigger size blips. 518 564 detector.config.blip_size.set(3); 519 565 520 566 detector.adc.sample(&mut samples).await; 521 - generate_signal(&mut samples); 567 + generate_drop_signal(&mut samples); 522 568 523 569 detector.detect_from_sample(0, &samples, &mut peaks, |_update| { 524 570 panic!("Update shouldn't be called");