Files
kb-firmware/src/wpm.rs

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
}
}