Repo for designs & driver for a TA7642 powered lightning detector

refactor: Signal detection algorithm tweaks

+154 -34
+76
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]] ··· 479 481 dependencies = [ 480 482 "typenum", 481 483 "version_check", 484 + ] 485 + 486 + [[package]] 487 + name = "getrandom" 488 + version = "0.3.4" 489 + source = "registry+https://github.com/rust-lang/crates.io-index" 490 + checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" 491 + dependencies = [ 492 + "cfg-if", 493 + "libc", 494 + "r-efi", 495 + "wasip2", 482 496 ] 483 497 484 498 [[package]] ··· 794 808 ] 795 809 796 810 [[package]] 811 + name = "ppv-lite86" 812 + version = "0.2.21" 813 + source = "registry+https://github.com/rust-lang/crates.io-index" 814 + checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 815 + dependencies = [ 816 + "zerocopy", 817 + ] 818 + 819 + [[package]] 797 820 name = "precomputed-hash" 798 821 version = "0.1.1" 799 822 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 840 863 ] 841 864 842 865 [[package]] 866 + name = "r-efi" 867 + version = "5.3.0" 868 + source = "registry+https://github.com/rust-lang/crates.io-index" 869 + checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 870 + 871 + [[package]] 872 + name = "rand" 873 + version = "0.9.2" 874 + source = "registry+https://github.com/rust-lang/crates.io-index" 875 + checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" 876 + dependencies = [ 877 + "rand_chacha", 878 + "rand_core 0.9.5", 879 + ] 880 + 881 + [[package]] 882 + name = "rand_chacha" 883 + version = "0.9.0" 884 + source = "registry+https://github.com/rust-lang/crates.io-index" 885 + checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 886 + dependencies = [ 887 + "ppv-lite86", 888 + "rand_core 0.9.5", 889 + ] 890 + 891 + [[package]] 843 892 name = "rand_core" 844 893 version = "0.6.4" 845 894 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 850 899 version = "0.9.5" 851 900 source = "registry+https://github.com/rust-lang/crates.io-index" 852 901 checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" 902 + dependencies = [ 903 + "getrandom", 904 + ] 853 905 854 906 [[package]] 855 907 name = "redox_syscall" ··· 1114 1166 ] 1115 1167 1116 1168 [[package]] 1169 + name = "wasip2" 1170 + version = "1.0.2+wasi-0.2.9" 1171 + source = "registry+https://github.com/rust-lang/crates.io-index" 1172 + checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" 1173 + dependencies = [ 1174 + "wit-bindgen", 1175 + ] 1176 + 1177 + [[package]] 1117 1178 name = "winapi-util" 1118 1179 version = "0.1.11" 1119 1180 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1135 1196 checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 1136 1197 dependencies = [ 1137 1198 "windows-link", 1199 + ] 1200 + 1201 + [[package]] 1202 + name = "wit-bindgen" 1203 + version = "0.51.0" 1204 + source = "registry+https://github.com/rust-lang/crates.io-index" 1205 + checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" 1206 + 1207 + [[package]] 1208 + name = "wyrand" 1209 + version = "0.3.2" 1210 + source = "registry+https://github.com/rust-lang/crates.io-index" 1211 + checksum = "15e0359b0b8d9cdef235a1fd4a8c5d02e4c9204e9fac861c14c229a8e803d1a6" 1212 + dependencies = [ 1213 + "rand_core 0.9.5", 1138 1214 ] 1139 1215 1140 1216 [[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 = "0.9" 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;
+62 -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 - }; 504 + }); 505 + 506 + assert_eq!(peaks.len(), 2); 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)]; 493 513 494 - detector.detect_from_sample(0, &samples, &mut peaks, update); 514 + detector.adc.sample(&mut samples).await; 515 + generate_noisy_signal(&mut samples, -40..=40, 20..55); 495 516 496 - assert_eq!(peaks.len(), 2); 517 + detector.detect_from_sample(0, &samples, &mut peaks, |update| { 518 + called.set(true); 519 + assert_eq!( 520 + update, 521 + DetectorUpdate::Detection { 522 + timestamp: 0, 523 + samples: &samples, 524 + peaks: expected_peaks.as_slice() 525 + } 526 + ) 527 + }); 528 + 529 + assert_eq!(peaks.len(), 3); 497 530 assert!(called.get()); 498 531 } 499 532 ··· 512 545 detector.state.avg.set(896); 513 546 514 547 let mut samples = alloc::vec![0; BLOCK_SIZE]; 515 - let mut peaks: alloc::vec::Vec<usize> = alloc::vec::Vec::with_capacity(BLOCK_SIZE); 548 + let mut peaks = alloc::vec::Vec::with_capacity(BLOCK_SIZE); 516 549 517 550 // Require bigger size blips. 518 551 detector.config.blip_size.set(3); 519 552 520 553 detector.adc.sample(&mut samples).await; 521 - generate_signal(&mut samples); 554 + generate_drop_signal(&mut samples); 522 555 523 556 detector.detect_from_sample(0, &samples, &mut peaks, |_update| { 524 557 panic!("Update shouldn't be called");