Detect the finger at 1.5x the floor, not 2x -- catch the landing frame

Three loop changes have now failed to move quick-tap accuracy: the frame gap
from 500 to 40 ms, the rising edge from two matcher runs to one (3x cheaper
frame), and waking on the touch edge instead of polling. All three left every
press at exactly one frame and the rate at 2 of 10. The transcript says why:

    frame 20 @ 4005ms: metric=247  irq=1        finger landing, below threshold
    frame 21 @ 4399ms: metric=366  FINGER rej   detected; this frame costs ~400 ms
    frame 22 @ 4562ms: metric=132               already gone

A frame that detects a finger takes 400-580 ms to process, of which the
trustlet's own REPORT_EVENT is about 300, and a quick tap lasts 400-600 ms.
There is no room for a second frame however early the loop wakes. Speed was
never the constraint.

But the landing frames are being thrown away. 209 and 247 against a 133 floor,
with the interrupt already asserted, discarded by a 2x threshold -- and they are
the only frames a quick tap has to spare. 1.5x puts the threshold at 200: above
the highest idle drift observed (147), below the lowest landing frame seen
(209). The tests pin it between those two measurements rather than to a
constant, and assert that 2x would have discarded both.

Single variable against the labelled 2-of-10 baseline.
This commit is contained in:
Jorijn van der Graaf 2026-09-03 00:15:37 +02:00
commit fe6ad11951
2 changed files with 29 additions and 10 deletions

View file

@ -42,9 +42,21 @@ export namespace fingerprintd::engine {
class Baseline {
public:
static constexpr std::size_t DefaultSamples = 5;
// A finger reads roughly 2.6x the floor; 2x separates them with margin
// on both measured runs.
static constexpr std::int32_t Multiplier = 2;
// A finger at full contact reads ~2.7x the floor (133 idle, 355-375
// pressed), so 2x separated them with margin. But 2x also discards the
// LANDING frames -- measured at 209 and 247 against a 133 floor, with
// the interrupt already asserted -- and those are the only frames a
// quick tap has to spare: a frame that detects a finger takes 400-580 ms
// to process (the trustlet's REPORT_EVENT is ~300 ms of it) while a tap
// lasts 400-600 ms, so detection one frame earlier is the difference
// between one image and two.
//
// 3/2 puts the threshold at 200 for a 133 floor: above the highest idle
// drift observed (147) and below the lowest landing frame seen (209).
// Expressed as a ratio because the floor is calibrated per session.
static constexpr std::int32_t MultiplierNum = 3;
static constexpr std::int32_t MultiplierDen = 2;
explicit Baseline(std::size_t samples = DefaultSamples) : want_(samples) {}
@ -59,7 +71,7 @@ export namespace fingerprintd::engine {
bool Ready() const { return seen_ >= want_ && floor_ > 0; }
std::int32_t Floor() const { return floor_; }
std::int32_t Threshold() const { return floor_ * Multiplier; }
std::int32_t Threshold() const { return floor_ * MultiplierNum / MultiplierDen; }
// Nothing is a finger until the floor is known. An uncalibrated
// Baseline reports false for everything rather than inventing a