55 lines
1.8 KiB
Rust
55 lines
1.8 KiB
Rust
use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, channel::Channel};
|
|
|
|
// Channel for sending WPM data
|
|
pub static WPM_CHANNEL: Channel<ThreadModeRawMutex, u64, 32> = Channel::new();
|
|
|
|
pub struct BucketWpmTracker {
|
|
buckets: [u8; 15], // 15 one-second buckets
|
|
current_second: u64,
|
|
}
|
|
|
|
impl BucketWpmTracker {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
buckets: [0; 15],
|
|
current_second: 0,
|
|
}
|
|
}
|
|
|
|
pub fn add_keypress(&mut self, timestamp_ms: u64) {
|
|
let second = timestamp_ms / 1000;
|
|
let bucket_idx = (second % 15) as usize;
|
|
|
|
// On new second, clear old buckets
|
|
if second != self.current_second {
|
|
let seconds_elapsed = second.saturating_sub(self.current_second).min(15);
|
|
for i in 0..seconds_elapsed {
|
|
let idx = ((self.current_second + i + 1) % 15) as usize;
|
|
self.buckets[idx] = 0;
|
|
}
|
|
self.current_second = second;
|
|
}
|
|
|
|
self.buckets[bucket_idx] = self.buckets[bucket_idx].saturating_add(1);
|
|
}
|
|
|
|
pub fn calculate_wpm(&mut self, current_time_ms: u64) -> u64 {
|
|
let current_second = current_time_ms / 1000;
|
|
|
|
// Clear buckets older than 15 seconds
|
|
if current_second != self.current_second {
|
|
let seconds_elapsed = current_second.saturating_sub(self.current_second).min(15);
|
|
for i in 0..seconds_elapsed {
|
|
let idx = ((self.current_second + i + 1) % 15) as usize;
|
|
self.buckets[idx] = 0;
|
|
}
|
|
self.current_second = current_second;
|
|
}
|
|
|
|
let total_chars: u32 = self.buckets.iter().map(|&x| x as u32).sum();
|
|
|
|
// extrapolate 15s of data to 60s
|
|
// WPM = (chars / 5 chars per word) * (60 seconds / 15 seconds)
|
|
((total_chars as f64 * 4f64) / 5f64) as u64
|
|
}
|
|
} |