diff --git a/packaging/fplearn.sh b/packaging/fplearn.sh index 7c3ae9a..ebc4032 100755 --- a/packaging/fplearn.sh +++ b/packaging/fplearn.sh @@ -2,12 +2,26 @@ # fplearn.sh -- the template-learning measurement, as a protocol rather than a # pile of remembered commands. # +# fplearn.sh snapshot [name] save the current template aside +# fplearn.sh restore put a saved template back and reload it # fplearn.sh wipe remove every stored template (backup first) # fplearn.sh enrol [finger] enrol at the config's sample count # fplearn.sh base [n] [w] trial with learning OFF (the baseline) # fplearn.sh trend [n] [w] three trials with learning ON # fplearn.sh sizes just print the template container sizes # +# SNAPSHOT BEFORE EVERY MEASUREMENT BLOCK. Learning rewrites the template in +# place, so without a snapshot an improvement cannot be attributed: a rate that +# climbs over an evening is equally explained by the template getting better, by +# the SENSOR being wiped clean, or by the USER learning where the sensor likes +# to be pressed (Jorijn, 2026-09-04, having spotted both while the numbers were +# still going up). All three move the same way on the same time axis. +# +# A snapshot makes the paired test possible: measure the learned template, then +# restore the older one and measure again WITHIN THE SAME SESSION, so the sensor +# is equally clean and the user equally practised for both halves. A rate that +# falls back on restore is learning. A rate that stays up is not. +# # WIPE FIRST when re-enrolling a finger that is already in the group. Measured # 2026-09-04: the duplicated-finger check refuses a finger the group already # holds -- 0 accepted of 7 presses, rc=0 on every one, while a never-enrolled @@ -76,7 +90,46 @@ restart() { # $1 = extra args printf ' TIMED OUT\n'; return 1 } +SNAPDIR=$HOME/fp6-backups/snapshots + case "${1:-}" in +snapshot) + NAME=${2:-$(date +%Y%m%d-%H%M%S)} + D=$SNAPDIR/$NAME + mkdir -p "$D" + n=0 + for f in $(sudo ls "$GROUP"); do + sz=$(sudo stat -c %s "$GROUP/$f") + if [ "$sz" -gt 200000 ]; then sudo cp -p "$GROUP/$f" "$D/$f"; n=$((n+1)); fi + done + sudo cp -p "$MAP" "$D/" 2>/dev/null || true + sudo chown -R "$(id -u):$(id -g)" "$D" + echo "snapshot '$NAME': $n container(s)" + ls -la "$D" | awk 'NR>3{printf " %9d %s\n", $5, $9}' + echo "restore with: fplearn.sh restore $NAME" ;; + +restore) + NAME=${2:-} + D=$SNAPDIR/$NAME + [ -n "$NAME" ] && [ -d "$D" ] || { echo "usage: fplearn.sh restore "; echo "available:"; ls "$SNAPDIR" 2>/dev/null | sed 's/^/ /'; exit 1; } + echo "=== restore snapshot '$NAME' ===" + echo "before:"; sizes + # The trustlet holds the template in memory once loaded, so a restore that + # does not restart the daemon changes the file and nothing else. + sudo systemctl stop "$UNIT" 2>/dev/null; sleep 2 + for f in $(ls "$D"); do + case "$f" in fingers-*.map) sudo cp -p "$D/$f" "$MAP" ;; + *) sudo cp -p "$D/$f" "$GROUP/$f" ;; esac + done + sudo sync + restart "--learn=0" || exit 1 + echo "after:"; sizes + fprintd-list user 2>&1 | tail -1 + echo + echo "Daemon is up with learning OFF, so the restored template stays put." + echo "Measure it now, in this session, against the block you just ran:" + echo " fplearn.sh base" ;; + sizes) echo "template containers now:"; sizes ;; @@ -156,5 +209,5 @@ trend) echo "does not is noise, and the daemon's own 'learn:' lines say which." ;; *) - sed -n '2,38p' "$0"; exit 1 ;; + sed -n '2,52p' "$0"; exit 1 ;; esac