Authenticate: one matcher run per frame, on a settled image

Two changes to the verify loop, both from measurement on the daemon.

The rising edge sent events 5 and 7 from the same capture and got two verdicts
back from one image -- rej rej, -11 -11, MATCH MATCH. Event 5 reaches the
matcher here as well as event 7, so the second REPORT_EVENT was 250 to 300 ms
of redundant work on every press, on the frame where speed matters most.
Authentication now sends only event 7. Enrolment keeps event 5, where it is the
sample trigger rather than a duplicate.

And the frame that detects the finger is the finger landing: partial contact,
and the frame that rejects most often -- across the real runs matches came at
frame 3, 5 and 8 of a press, and a quick tap is one frame. So on the rising
edge the daemon captures once more, about 50 ms later, before reporting, and
the matcher's first look is at a settled finger.

The rescan-budget experiment is reverted. At the stock budget every
non-matching frame answered -11, for the enrolled finger and the wrong one
alike, while matches landed exactly where they did at rescan=0. The budget
relabels a non-match; it does not make the trustlet try harder. Under the press
rule the two are functionally identical, and rescan=0's terminal rejection is
the cleaner label.
This commit is contained in:
Jorijn van der Graaf 2026-09-02 22:59:31 +02:00
commit 9790381e72
3 changed files with 28 additions and 11 deletions

View file

@ -1280,12 +1280,20 @@ public:
out.frames++;
std::string note;
bool rising = finger && !inPress;
if (rising) {
inPress = true; pressMatched = false; pressRejected = false;
pressFrames = 0; rescans = 0; pressFid = 0;
out.presses++;
// The frame that detected the finger is the finger LANDING --
// partial contact, and the frame that rejects most often.
// Capture once more now that it has settled, so the image the
// matcher sees on the first report is a real one. One capture,
// ~50 ms, on the frame that decides every quick tap.
auto c2 = SendCommand(app_, ta::Cmd::CaptureImage, cap);
if (c2.invoked) { c = c2; note += " recap"; }
}
for (ta::Event ev : tracker.Observe(finger, en::Mode::Authenticate)) {
if (ev == ta::Event::FingerTouched) {
inPress = true; pressMatched = false; pressRejected = false;
pressFrames = 0; rescans = 0; pressFid = 0;
out.presses++;
}
std::vector<std::byte> evbuf(ta::EventContextSize);
ta::BuildEventContext(evbuf, { .event = ev });
// A zero-initialised buffer cannot tell "the matcher never