-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfaults.py
More file actions
135 lines (120 loc) · 4.72 KB
/
Copy pathfaults.py
File metadata and controls
135 lines (120 loc) · 4.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#!/usr/bin/env python3
"""Fault taxonomy + classification for KeySurgeon.
Turns a raw trial stats dict into a human verdict: what's wrong, in plain
words, how sure we are, and a 0-100 health score for the key. The headline is
for a person ("This key is double-typing"); the numbers are evidence.
"""
# fault ids used to look up fix ladders in fixes.py
OK = "ok"
CHATTER = "chatter"
DEAD = "dead"
INTERMITTENT = "intermittent"
STICKY = "sticky"
EXTRA = "extra"
# board-health bands by score
BANDS = [
(90, "HEALTHY"),
(70, "WATCH"),
(40, "DEGRADING"),
(0, "FAILING"),
]
def band(score):
for floor, name in BANDS:
if score >= floor:
return name
return "FAILING"
def _confidence(registered):
if registered >= 60:
return "high"
if registered >= 12:
return "medium"
return "low"
def classify(stats):
"""stats -> verdict dict:
{fault, label, headline, detail, evidence[list], score, confidence}
"""
label = stats["label"]
reg = stats["registered"]
exp = stats["expected"]
chatter = stats["chatter"]
conf = _confidence(reg)
ev = []
# CHATTER - bounce wins; it's the most actionable and most common failure.
# Score scales with how often it bounces: a lone bounce is milder than
# bouncing on half your presses.
if chatter:
if stats["min_regap"] is not None:
ev.append(f"re-press {stats['min_regap']}ms (human floor ~60ms)")
ev.append(f"{chatter} bounce{'s' if chatter != 1 else ''} in {reg} presses")
ev.append(f"confidence: {conf}")
score = max(10, 60 - round(60 * chatter / max(reg, 1)))
return {
"fault": CHATTER, "label": label,
"headline": "This key is double-typing.",
"detail": ("The switch is bouncing - that's hardware wear, not "
"anything you're doing wrong."),
"evidence": ev, "score": score, "confidence": conf,
}
# DEAD - nothing registered at all
if reg == 0:
ev.append("0 presses registered")
ev.append(f"confidence: {conf}")
return {
"fault": DEAD, "label": label,
"headline": "This key isn't responding.",
"detail": ("Nothing registered. Could be debris in the switch, a "
"dirty contact, or a dead switch - often cleanable."),
"evidence": ev, "score": 0, "confidence": conf,
}
# INTERMITTENT - some presses didn't land
if reg < exp:
miss = exp - reg
rate = round(100 * miss / exp)
ev.append(f"{reg}/{exp} registered ({miss} missed)")
if stats["short_holds"]:
ev.append(f"also {stats['short_holds']} very short holds "
f"(may be sticking too)")
ev.append(f"confidence: {conf}")
return {
"fault": INTERMITTENT, "label": label,
"headline": "This key skips sometimes.",
"detail": ("Some presses didn't register. Usually a dirty or worn "
"contact - cleaning often brings it back."),
"evidence": ev, "score": max(5, 100 - rate), "confidence": conf,
}
# EXTRA - more registered than asked: late bounces after the last press
if reg > exp:
extra = reg - exp
ev.append(f"{extra} extra press{'es' if extra != 1 else ''} after you stopped")
if stats["short_holds"]:
ev.append(f"also {stats['short_holds']} very short holds "
f"(may be sticking too)")
ev.append(f"confidence: {conf}")
return {
"fault": EXTRA, "label": label,
"headline": "This key fires extra presses.",
"detail": ("It registered bounces after you let go - early-stage "
"chatter. Worth keeping an eye on."),
"evidence": ev, "score": 60, "confidence": conf,
}
# STICKY - registered fine but holds look abnormal
if stats["short_holds"]:
ev.append(f"{stats['short_holds']} suspiciously short holds "
f"(<8ms)")
ev.append(f"confidence: {conf}")
return {
"fault": STICKY, "label": label,
"headline": "This key feels off.",
"detail": ("Holds registered oddly short - can mean a sticky or "
"mushy switch. Cleaning or lube usually helps."),
"evidence": ev, "score": 70, "confidence": conf,
}
# OK
ev.append(f"{reg}/{exp} clean, no bounce")
ev.append(f"confidence: {conf}")
return {
"fault": OK, "label": label,
"headline": "This key is healthy.",
"detail": "Clean presses, no double-firing, no misses. Nothing to fix.",
"evidence": ev, "score": 100, "confidence": conf,
}