tangled
alpha
login
or
join now
sachy.dev
/
strike-sensor
0
fork
atom
Repo for designs & driver for a TA7642 powered lightning detector
0
fork
atom
overview
issues
pulls
pipelines
refactor: Signal detection algorithm tweaks
sachy.dev
1 month ago
221dca30
e3d3de79
1/1
test.yml
success
19s
+154
-34
4 changed files
expand all
collapse all
unified
split
Cargo.lock
embassy-strike-driver
Cargo.toml
src
analysis.rs
lib.rs
+76
Cargo.lock
···
300
300
"embassy-time",
301
301
"heapless 0.9.2",
302
302
"pollster",
303
303
+
"rand",
304
304
+
"wyrand",
303
305
]
304
306
305
307
[[package]]
···
479
481
dependencies = [
480
482
"typenum",
481
483
"version_check",
484
484
+
]
485
485
+
486
486
+
[[package]]
487
487
+
name = "getrandom"
488
488
+
version = "0.3.4"
489
489
+
source = "registry+https://github.com/rust-lang/crates.io-index"
490
490
+
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
491
491
+
dependencies = [
492
492
+
"cfg-if",
493
493
+
"libc",
494
494
+
"r-efi",
495
495
+
"wasip2",
482
496
]
483
497
484
498
[[package]]
···
794
808
]
795
809
796
810
[[package]]
811
811
+
name = "ppv-lite86"
812
812
+
version = "0.2.21"
813
813
+
source = "registry+https://github.com/rust-lang/crates.io-index"
814
814
+
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
815
815
+
dependencies = [
816
816
+
"zerocopy",
817
817
+
]
818
818
+
819
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
866
+
name = "r-efi"
867
867
+
version = "5.3.0"
868
868
+
source = "registry+https://github.com/rust-lang/crates.io-index"
869
869
+
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
870
870
+
871
871
+
[[package]]
872
872
+
name = "rand"
873
873
+
version = "0.9.2"
874
874
+
source = "registry+https://github.com/rust-lang/crates.io-index"
875
875
+
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
876
876
+
dependencies = [
877
877
+
"rand_chacha",
878
878
+
"rand_core 0.9.5",
879
879
+
]
880
880
+
881
881
+
[[package]]
882
882
+
name = "rand_chacha"
883
883
+
version = "0.9.0"
884
884
+
source = "registry+https://github.com/rust-lang/crates.io-index"
885
885
+
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
886
886
+
dependencies = [
887
887
+
"ppv-lite86",
888
888
+
"rand_core 0.9.5",
889
889
+
]
890
890
+
891
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
902
+
dependencies = [
903
903
+
"getrandom",
904
904
+
]
853
905
854
906
[[package]]
855
907
name = "redox_syscall"
···
1114
1166
]
1115
1167
1116
1168
[[package]]
1169
1169
+
name = "wasip2"
1170
1170
+
version = "1.0.2+wasi-0.2.9"
1171
1171
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1172
1172
+
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
1173
1173
+
dependencies = [
1174
1174
+
"wit-bindgen",
1175
1175
+
]
1176
1176
+
1177
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
1199
+
]
1200
1200
+
1201
1201
+
[[package]]
1202
1202
+
name = "wit-bindgen"
1203
1203
+
version = "0.51.0"
1204
1204
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1205
1205
+
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
1206
1206
+
1207
1207
+
[[package]]
1208
1208
+
name = "wyrand"
1209
1209
+
version = "0.3.2"
1210
1210
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1211
1211
+
checksum = "15e0359b0b8d9cdef235a1fd4a8c5d02e4c9204e9fac861c14c229a8e803d1a6"
1212
1212
+
dependencies = [
1213
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
28
+
rand = "0.9"
29
29
+
wyrand = "0.3"
+14
-5
embassy-strike-driver/src/analysis.rs
···
1
1
use crate::{BLOCK_SIZE, traits::BufferMut};
2
2
3
3
-
pub fn analyse_buffer_by_stepped_windows<B: BufferMut<usize>>(
3
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
16
-
let window_total = window.iter().copied().sum::<u16>();
17
17
-
let window_avg = window_total / CHUNK_SIZE as u16;
18
18
-
let diff = average.saturating_sub(window_avg);
16
16
+
let (window_total, window_diff) =
17
17
+
window
18
18
+
.iter()
19
19
+
.copied()
20
20
+
.fold((0, 0), |(total, diff), sample| {
21
21
+
(
22
22
+
total + sample,
23
23
+
diff + ((average as i16) - sample as i16).unsigned_abs(),
24
24
+
)
25
25
+
});
26
26
+
27
27
+
let diff = window_diff / CHUNK_SIZE as u16;
19
28
20
29
if diff > threshold {
21
21
-
peaks.push(i);
30
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
70
-
peaks: &'a [usize],
70
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
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
120
-
121
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
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
136
-
// info!("ACT: {}, Duty: {}", act_value, duty);
137
132
}
138
133
self.state.max_duty.set(duty as u8);
139
139
-
duty = (duty / 3) * 2;
134
134
+
duty = (duty / 6) * 5;
140
135
self.pwm.set_duty(duty);
141
136
self.state.duty.set(duty as u8);
142
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
169
-
B: BufferMut<usize>,
163
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
204
-
B: BufferMut<usize>,
198
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
259
-
use core::future::poll_fn;
253
253
+
use core::{future::poll_fn, ops::RangeBounds, slice::SliceIndex};
260
254
use embassy_time::MockDriver;
255
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
313
-
impl BufferMut<usize> for alloc::vec::Vec<usize> {
314
314
-
fn push(&mut self, value: usize) {
308
308
+
impl BufferMut<(usize, u16)> for alloc::vec::Vec<(usize, u16)> {
309
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
330
-
fn as_slice(&self) -> &[usize] {
325
325
+
fn as_slice(&self) -> &[(usize, u16)] {
331
326
self
332
327
}
333
328
}
···
351
346
.await;
352
347
}
353
348
354
354
-
fn generate_signal(samples: &mut [u16]) {
349
349
+
fn generate_noisy_signal<
350
350
+
I: SliceIndex<[u16], Output = [u16]> + RangeBounds<usize>,
351
351
+
S: RangeBounds<i16> + SampleRange<i16> + Clone,
352
352
+
>(
353
353
+
samples: &mut [u16],
354
354
+
amplitude: S,
355
355
+
range: I,
356
356
+
) {
357
357
+
let mut noise = wyrand::WyRand::new(141);
358
358
+
359
359
+
let samples = samples
360
360
+
.get_mut(range)
361
361
+
.expect("Range to be sized the same or smaller than the slice");
362
362
+
363
363
+
for sample in samples.iter_mut() {
364
364
+
*sample = ((*sample as i16) - noise.random_range(amplitude.clone())) as u16;
365
365
+
}
366
366
+
}
367
367
+
368
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
399
-
assert_eq!(detector.state.duty.get(), 64);
400
400
-
assert_eq!(detector.adc.sample_average(&mut buf).await, 896);
413
413
+
assert_eq!(detector.state.duty.get(), 80);
414
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
469
-
let update = |_update: DetectorUpdate<'_>| {
483
483
+
detector.detect_from_sample(0, &samples, &mut peaks, |_| {
470
484
panic!("This update function shouldn't be called");
471
471
-
};
472
472
-
473
473
-
detector.detect_from_sample(0, &samples, &mut peaks, update);
485
485
+
});
474
486
475
487
assert_eq!(peaks.len(), 0);
476
488
477
477
-
generate_signal(&mut samples);
489
489
+
generate_drop_signal(&mut samples);
478
490
479
479
-
let expected_peaks = alloc::vec![0, 8];
491
491
+
let expected_peaks = alloc::vec![(0, 25), (8, 21)];
480
492
let called = Cell::new(false);
481
493
482
482
-
let update = |update: DetectorUpdate<'_>| {
494
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
492
-
};
504
504
+
});
505
505
+
506
506
+
assert_eq!(peaks.len(), 2);
507
507
+
assert!(called.get());
508
508
+
509
509
+
called.set(false);
510
510
+
511
511
+
let mut samples = alloc::vec![0; BLOCK_SIZE];
512
512
+
let expected_peaks = alloc::vec![(16, 15), (24, 19), (32, 18)];
493
513
494
494
-
detector.detect_from_sample(0, &samples, &mut peaks, update);
514
514
+
detector.adc.sample(&mut samples).await;
515
515
+
generate_noisy_signal(&mut samples, -40..=40, 20..55);
495
516
496
496
-
assert_eq!(peaks.len(), 2);
517
517
+
detector.detect_from_sample(0, &samples, &mut peaks, |update| {
518
518
+
called.set(true);
519
519
+
assert_eq!(
520
520
+
update,
521
521
+
DetectorUpdate::Detection {
522
522
+
timestamp: 0,
523
523
+
samples: &samples,
524
524
+
peaks: expected_peaks.as_slice()
525
525
+
}
526
526
+
)
527
527
+
});
528
528
+
529
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
515
-
let mut peaks: alloc::vec::Vec<usize> = alloc::vec::Vec::with_capacity(BLOCK_SIZE);
548
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
521
-
generate_signal(&mut samples);
554
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");