pos() was eval'ing over the script's positional parameters from inside a function, where $1 is the function's own argument. So `pos 3` printed "3" and `pos 10` printed "100": every run this script has ever driven printed sample numbers where positions should have been. That rewrites an earlier conclusion rather than just fixing a bug. No enrolment on this device had ever been guided, including the one that produced the best template measured so far -- its spread came entirely from the config's overlap band refusing near-duplicates, not from anything this script said. The paired-test attribution drops a contributor, and the 30-sample run becomes directly comparable to it because both were equally unguided. Prompts are therefore opt-in now, behind FPENROL_GUIDE=1. The first run where they actually fired steered the finger to edges, tips and hard rolls, needed 74 presses for 30 accepts, and produced a template that matched 0 of 10 taps. Prompting a user toward positions they never use in a real tap builds a template of positions they never use in a real tap. The default is the instruction that worked: press as you would to unlock, and let the refusals move you. The stage count comes from the daemon's num-enroll-stages property rather than a second hardcoded 20.
116 lines
4.9 KiB
Shell
Executable file
116 lines
4.9 KiB
Shell
Executable file
#!/bin/sh
|
|
# fpenrol.sh -- enrol a finger with COVERAGE guidance.
|
|
#
|
|
# fpenrol.sh [finger_name] default right-middle-finger
|
|
#
|
|
# The first template on this device was enrolled from ten quick taps in
|
|
# roughly one position, and it matches roughly one frame in seven -- against
|
|
# 47-75% for the template the research harness enrolled. The algorithm was
|
|
# given ten near-duplicate images of one spot, so it recognises that spot and
|
|
# little else.
|
|
#
|
|
# This walks a different contact position per accepted sample. It reads
|
|
# fprintd-enroll's per-stage output and prompts the next position as each
|
|
# stage passes, so the guidance tracks what the trustlet ACCEPTED rather than
|
|
# how many times you pressed.
|
|
#
|
|
# TWO BUGS FIXED 2026-09-03, both of which had silently disabled a feature
|
|
# this script claims to provide:
|
|
#
|
|
# 1. `pos()` was `eval "printf '%s' \"\$$1\""` over the script's positional
|
|
# parameters. Inside a FUNCTION, `$1` is the function's own argument, so
|
|
# `pos 3` printed "3" and `pos 10` printed `${1}0` = "100". Every run this
|
|
# script ever drove -- including the one that produced the 7/10 template --
|
|
# printed sample numbers where positions should have been, so NO enrolment
|
|
# has ever actually received position guidance. The spread in those
|
|
# templates came from the config's overlap band refusing near-duplicates,
|
|
# not from anything this script said. Positions now live in a newline
|
|
# list indexed with sed, which has no interaction with $1 at all.
|
|
#
|
|
# 2. The sample count was hardcoded to 20 while the daemon was configured for
|
|
# 30, so the counter froze at 20/20 and the run looked like it had hung or
|
|
# been clamped. It had not: all 30 samples were taken. The count now comes
|
|
# from the daemon's own num-enroll-stages property, which it reads from the
|
|
# same config the trustlet is given.
|
|
F=${1:-right-middle-finger}
|
|
|
|
# Position prompts are OPT-IN: FPENROL_GUIDE=1. The one template that verified
|
|
# well (right-middle, 20 samples, 7/10 quick taps) was enrolled with NO prompts
|
|
# -- the pos() bug meant none were ever shown -- and the algorithm's own
|
|
# overlap band did the spreading. The first run where prompts actually fired
|
|
# (right-middle, 30 samples, edges/tips/rolled-hard) needed 74 presses for 30
|
|
# accepts and then matched 0/10. Prompting a user toward positions they never
|
|
# use in a real tap builds a template of positions they never use in a real
|
|
# tap. Without prompts, the instruction is the one that worked: press the way
|
|
# you would to unlock, and let the REFUSED messages move you.
|
|
GUIDE=${FPENROL_GUIDE:-0}
|
|
|
|
# The daemon is the authority on how many stages there are.
|
|
STAGES=$(busctl get-property net.reactivated.Fprint \
|
|
/net/reactivated/Fprint/Device/0 \
|
|
net.reactivated.Fprint.Device num-enroll-stages 2>/dev/null \
|
|
| awk '{print $2}')
|
|
case "$STAGES" in
|
|
''|*[!0-9]*) STAGES=20
|
|
echo "warning: could not read num-enroll-stages; assuming $STAGES" ;;
|
|
esac
|
|
|
|
POSITIONS='centre of the pad
|
|
slightly LEFT of centre
|
|
slightly RIGHT of centre
|
|
higher up, toward the TIP
|
|
lower down, toward the JOINT
|
|
centre, rolled LEFT
|
|
centre, rolled RIGHT
|
|
tip, rolled slightly left
|
|
joint, rolled slightly right
|
|
centre, a little firmer
|
|
far LEFT edge of the pad
|
|
far RIGHT edge of the pad
|
|
very tip of the finger
|
|
well down toward the joint
|
|
upper left of the pad
|
|
upper right of the pad
|
|
lower left of the pad
|
|
lower right of the pad
|
|
centre, rolled hard left
|
|
centre, rolled hard right'
|
|
NPOS=$(printf '%s\n' "$POSITIONS" | wc -l)
|
|
|
|
# More samples than positions is expected at 30. Cycle, and say so, rather
|
|
# than printing nothing -- printing nothing is what hid bug 1 for two sessions.
|
|
pos() {
|
|
n=$(( ($1 - 1) % NPOS + 1 ))
|
|
printf '%s' "$(printf '%s\n' "$POSITIONS" | sed -n "${n}p")"
|
|
[ "$1" -gt "$NPOS" ] && printf ' (again, press slightly differently)'
|
|
}
|
|
|
|
i=1
|
|
if [ "$GUIDE" = 1 ]; then
|
|
echo "enrolling $F with position guidance -- $STAGES samples (from the daemon)"
|
|
echo "press FLAT and firm, hold about a second, then LIFT fully."
|
|
prompt() { echo ">>> sample $1/$STAGES: $(pos $1)"; }
|
|
else
|
|
echo "enrolling $F -- $STAGES samples (from the daemon), NO position prompts"
|
|
echo "press the way you would to UNLOCK the phone. When a sample is REFUSED,"
|
|
echo "shift a little from where you were; otherwise do not think about it."
|
|
prompt() { echo ">>> sample $1/$STAGES"; }
|
|
fi
|
|
echo
|
|
prompt 1
|
|
fprintd-enroll -f "$F" user 2>&1 | while IFS= read -r line; do
|
|
case "$line" in
|
|
*enroll-stage-passed*)
|
|
i=$((i+1))
|
|
if [ "$i" -le "$STAGES" ]; then
|
|
echo " accepted."
|
|
prompt "$i"
|
|
else
|
|
echo " accepted."
|
|
fi ;;
|
|
*enroll-retry-scan*) echo " REFUSED -- move further from the last position, or press flatter" ;;
|
|
*enroll-completed*) echo; echo "COMPLETED." ;;
|
|
*enroll-failed*) echo; echo "FAILED." ;;
|
|
*) printf '%s\n' "$line" ;;
|
|
esac
|
|
done
|