Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rewrite of WPM.c (no float) #13359

Closed
wants to merge 3 commits into from
Closed
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 90 additions & 42 deletions quantum/wpm.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,6 @@

#include "wpm.h"

// WPM Stuff
static uint8_t current_wpm = 0;
static uint16_t wpm_timer = 0;

// This smoothing is 40 keystrokes
static const float wpm_smoothing = WPM_SMOOTHING;

void set_current_wpm(uint8_t new_wpm) { current_wpm = new_wpm; }

uint8_t get_current_wpm(void) { return current_wpm; }

bool wpm_keycode(uint16_t keycode) { return wpm_keycode_kb(keycode); }

__attribute__((weak)) bool wpm_keycode_kb(uint16_t keycode) { return wpm_keycode_user(keycode); }
Expand All @@ -45,46 +34,105 @@ __attribute__((weak)) bool wpm_keycode_user(uint16_t keycode) {
return false;
}

#ifdef WPM_ALLOW_COUNT_REGRESSION
__attribute__((weak)) uint8_t wpm_regress_count(uint16_t keycode) {
bool weak_modded = (keycode >= QK_LCTL && keycode < QK_LSFT) || (keycode >= QK_RCTL && keycode < QK_RSFT);
/* The shorter you make the capture duration the quicker the fall off once you stop typing. */
/* Comment this out to get a longer fall-off but also a longer ramp up time to display */
/* the actual WPM */
#define WPM_SHORT_WINDOW

if ((keycode >= QK_MOD_TAP && keycode <= QK_MOD_TAP_MAX) || (keycode >= QK_LAYER_TAP && keycode <= QK_LAYER_TAP_MAX) || (keycode >= QK_MODS && keycode <= QK_MODS_MAX)) {
keycode = keycode & 0xFF;
} else if (keycode > 0xFF) {
keycode = 0;
}
if (keycode == KC_DEL || keycode == KC_BSPC) {
if (((get_mods() | get_oneshot_mods()) & MOD_MASK_CTRL) || weak_modded) {
return WPM_ESTIMATED_WORD_SIZE;
} else {
return 1;
#ifndef WPM_SHORT_WINDOW
drashna marked this conversation as resolved.
Show resolved Hide resolved
/* Full range we capture here is 100 * 100 ms = 10 s, this also means that it will take */
/* ~10 seconds to display the actual WPM once you start typing. */
/* The computed WPM needs to be multiplied by 6 (6*10s=60s=1min) */
/* to get the actual WPM, see 'WPM_WINDOW_TO_WPM' (x*6 = x*4 + x*2) */
# define WPM_WINDOW_NUM 100
# define WPM_WINDOW_MS 100
# define WPM_WINDOW_TO_WPM(x) (((x) << 2) + ((x) << 1))

#else
/* Full range we capture here is 100 * 60 ms = 6 s, this also means that it will take */
/* ~6 seconds to display the actual WPM once you start typing. */
/* x*10 = x*8 + x*2 */
# define WPM_WINDOW_NUM 100
# define WPM_WINDOW_MS 60
# define WPM_WINDOW_TO_WPM(x) (((x) << 3) + ((x) << 1))
#endif

/* State names */
# define WPM_PROCESS_DECAY 0
# define WPM_PROCESS_KEYPRESS 1

typedef struct {
uint8_t windows[WPM_WINDOW_NUM];
uint8_t period;
uint8_t word;
uint8_t wpm;
uint8_t wpm_displayed;
uint16_t timer;
} wpm_state_t;

static wpm_state_t s_wpm_state = { .period = -1, .word = 0, .wpm = 0, .wpm_displayed = 0 };

/* This should only be called on the slave side (when receiving I2C data) */
void set_current_wpm(uint8_t new_wpm) {
s_wpm_state.wpm_displayed = new_wpm;
}

uint8_t get_current_wpm(void) {
return s_wpm_state.wpm_displayed;
}

void process_wpm(uint8_t mode) {
uint16_t elapsed = timer_elapsed(s_wpm_state.timer);

s_wpm_state.word += mode;
if (elapsed >= WPM_WINDOW_MS) {

/* TODO: Can we move this to some initialization step? */
if (s_wpm_state.period == -1) {
for (int8_t i=0; i<WPM_WINDOW_NUM; i++) {
s_wpm_state.windows[i] = 0;
}
}

s_wpm_state.timer = timer_read();
s_wpm_state.period++;
if (s_wpm_state.period == WPM_WINDOW_NUM) {
s_wpm_state.period = 0;
}

if (mode == WPM_PROCESS_DECAY) {
/* Have the wpm display value move towards the actual (lower) wpm value in a timely manner */
if ((s_wpm_state.period & 0x7) == 0) { /* Delaying by 8 * WPM_WINDOW_MS = 800 ms */
uint8_t wpm = WPM_WINDOW_TO_WPM(s_wpm_state.wpm);
if (wpm < s_wpm_state.wpm_displayed) {
s_wpm_state.wpm_displayed = s_wpm_state.wpm_displayed - (((s_wpm_state.wpm_displayed - wpm) >> 2) | 1);
}
}
}
} else {
return 0;

s_wpm_state.wpm -= s_wpm_state.windows[s_wpm_state.period];
s_wpm_state.windows[s_wpm_state.period] = 0;
}
if (s_wpm_state.word >= 5) {
s_wpm_state.windows[s_wpm_state.period] += 1;
s_wpm_state.wpm += 1;
s_wpm_state.word -= 5;
}
}
#endif

void update_wpm(uint16_t keycode) {
if (wpm_keycode(keycode)) {
if (wpm_timer > 0) {
current_wpm += ((60000 / timer_elapsed(wpm_timer) / WPM_ESTIMATED_WORD_SIZE) - current_wpm) * wpm_smoothing;
}
wpm_timer = timer_read();
process_wpm(WPM_PROCESS_KEYPRESS);
{
/* Have the wpm display value move towards the actual (higher) wpm value in a rapid manner */
uint8_t wpm = (s_wpm_state.wpm << 2) + (s_wpm_state.wpm << 1);
if (wpm > s_wpm_state.wpm_displayed) {
s_wpm_state.wpm_displayed = wpm;
}
}
}
#ifdef WPM_ALLOW_COUNT_REGRESSION
uint8_t regress = wpm_regress_count(keycode);
if (regress) {
current_wpm -= regress;
wpm_timer = timer_read();
}
#endif
}

void decay_wpm(void) {
if (timer_elapsed(wpm_timer) > 1000) {
current_wpm += (-current_wpm) * wpm_smoothing;
wpm_timer = timer_read();
}
process_wpm(WPM_PROCESS_DECAY);
}