From bdc4828e0fe6f7c2c48a4edcedb1832006e841f2 Mon Sep 17 00:00:00 2001 From: Matthew Carroll Date: Thu, 10 Sep 2026 02:19:13 -0700 Subject: [PATCH] lf t55xx writetest: read each block three times and name how it differs One read cannot see the fault this suite is most useful for. A block that comes back silently wrong on some reads and right on others scores the same as one that is simply wrong, and the same as one that is fine. Read each block three times and mark disagreement between them as `unstable`. Where a block is wrong the same way every time, name it: `rol1`, `ror5`, `inverted`, `inv+ror2`, `shr1`. The kind matters. A rotation says the word boundary moved, an inversion says psk picked the opposite phase, and a shift says a demodulation opened a bit early and padded with zero - three different faults that want three different fixes, and all of them previously reported as "block 3,4 bad". Measured on a T5577 over the 21 PSK1 configurations, this turns 56 fields of "bad" into 56 marked unstable, and on a build that has since fixed part of it, into 24 rotations whose size tracks the bit rate - ror5 at RF/32, ror4 at RF/40, ror2 at RF/100 - which is a fixed sample offset rather than noise, and says where to look. Block 1 is 00000000, which equals all of its own rotations, so a rotated read of it cannot be told from a correct one. Noted in the header rather than changed, since the payload is the suite's own choice. Also corrects the header, which described block 2 as ffffffff where the script writes aa5500ff. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 1 + .../luascripts/tests/lf_t55xx_writetest.lua | 96 +++++++++++++++++-- 2 files changed, 87 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ceef461b4..ea9d3b1dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Changed `tests/lf_t55xx_writetest.lua` - reads each block three times and names how a bad read differs (`rol1`, `ror5`, `inverted`, `shr1`) instead of only pass/fail (@mfcarroll) - Fixed `lf t55xx detect` - PSK1 now detects at every bit rate and subcarrier. (@iceman1001) - Fixed `lf t55xx detect` - FSK was skipped entirely when the field-clock pair measured as neither legal pair, losing FSK1 at RF/32 and RF/40 and every variant at RF/16 (@iceman1001) - Fixed `lf t55xx dump/read` - blocks were extracted at a bit offset cached from the last `detect` The offset is now anchored in graph samples (@iceman1001) diff --git a/client/luascripts/tests/lf_t55xx_writetest.lua b/client/luascripts/tests/lf_t55xx_writetest.lua index 436846bc0..05251def2 100644 --- a/client/luascripts/tests/lf_t55xx_writetest.lua +++ b/client/luascripts/tests/lf_t55xx_writetest.lua @@ -16,7 +16,7 @@ A strict T55x7 test suite: lf t55xx wipe lf t55xx write -b 1 -d 00000000 - lf t55xx write -b 2 -d ffffffff + lf t55xx write -b 2 -d aa5500ff lf t55xx write -b 3 -d 80000000 lf t55xx write -b 4 -d 00000001 @@ -24,6 +24,14 @@ Each row names the configuration word, what it means, what `lf t55xx detect` made of it and how many of the four data blocks read back intact. Markers are also written into the session log ( `rem [ERR:...]` and `rem [SUMMARY:...]` ) +Every block is read three times. A block that reads back differently between +those is marked `unstable`, which is the fault a single read cannot see: the +value is silently wrong rather than missing. A block that is wrong the same way +each time is named where it can be - `rol1`, `ror5`, `inverted`, `shr1` - because +a rotation points at the word boundary and an inversion at psk phase, and the two +want different fixes. Block 1 is `00000000`, which is equal to all of its own +rotations, so a rotated read of it cannot be told from a correct one. + Note: that the card is wiped before each modulation. Needs a T5577 in the field. @@ -161,30 +169,98 @@ local function prepare() return true end +-- a block is read this many times, so that a value which is silently wrong on +-- only some reads is not mistaken for one that is right +local READS = 3 + +local function rol32(v, n) + n = n % 32 + if n == 0 then return v end + return ((v << n) | (v >> (32 - n))) & 0xFFFFFFFF +end + --- --- Read blocks 1-4 back. Returns how many matched and a short note naming the --- ones that did not. +-- Name how `got` differs from `want`, when it can be named. A rotation says the +-- word boundary moved, an inversion says psk picked the opposite phase, and a +-- shift says a demodulation opened a bit early and padded with zero - different +-- faults wanting different fixes, so worth telling apart. A word equal to all of +-- its own rotations carries no boundary information and only gets 'differs'. +local function classify(want, got) + + if got == want then return 'ok' end + + if rol32(want, 1) == want then return 'differs' end + + for n = 1, 31 do + if rol32(want, n) == got then + return (n <= 16) and ('rol' .. n) or ('ror' .. (32 - n)) + end + end + + if (want ~ 0xFFFFFFFF) == got then return 'inverted' end + + for n = 1, 31 do + if (rol32(want, n) ~ 0xFFFFFFFF) == got then + return 'inv+' .. ((n <= 16) and ('rol' .. n) or ('ror' .. (32 - n))) + end + end + + for n = 1, 8 do + if ((want >> n) & 0xFFFFFFFF) == got then return 'shr' .. n end + if ((want << n) & 0xFFFFFFFF) == got then return 'shl' .. n end + end + + return 'differs' +end + +--- +-- Read blocks 1-4 back, READS times each. Returns how many matched on every +-- read and a short note naming what went wrong with the ones that did not. local function read_back(config) local good, bad = 0, {} for block, want in ipairs(DATA_BLOCKS) do - local data = core.t55xx_readblock(block, '0', '0', '') - local got = data and ('%08X'):format(data) or '' + local wantv = tonumber(want, 16) + local seen, first, stable = {}, nil, true - if got:lower() == want:lower() then + for _ = 1, READS do + local data = core.t55xx_readblock(block, '0', '0', '') + local v = data and (data & 0xFFFFFFFF) or nil + table.insert(seen, v) + if first == nil then + first = v + elseif v ~= first then + stable = false + end + end + + if stable and first == wantv then good = good + 1 else - table.insert(bad, tostring(block)) - core.console(format('rem [ERR:READ:%s:%d] block %d: read %s instead of %s', - config, block, block, got, want), false, true) + local what + if stable == false then + what = 'unstable' + else + what = classify(wantv, first) + end + + table.insert(bad, format('b%d %s', block, what)) + + local reads = {} + for _, v in ipairs(seen) do + table.insert(reads, v and ('%08X'):format(v) or '----') + end + core.console(format('rem [ERR:READ:%s:%d] block %d: %s, wanted %s, read %s', + config, block, block, what, want, + table.concat(reads, ' ')), false, true) end end local note = '' if #bad > 0 then - note = 'block ' .. table.concat(bad, ',') .. ' bad' + note = table.concat(bad, ' ') end return good, note end