7676 Commits
Author SHA1 Message Date
iceman1001andClaude Opus 5.5 c7923105e5 PM5: fuel gauge 0 % at the 3300 mV shutdown floor
The firmware powers off at 3300 mV (BWM_SHUTDOWN_MV) but the gauge
Terminate Voltage was its 3200 mV default, so the PM5 switched off while
`hw status` still showed a few percent. The gauge setup (setcap and the
boot check) now writes Terminate Voltage 3300 mV with the capacity and
chem ID, and `hw dbg -3` shows it on the Gauge line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-15 04:54:48 +07:00
Iceman 45e5ffac32 Merge branch 'master' into fix-hf14a-openpgp-aid
Signed-off-by: Iceman <iceman@iuse.se>
2026-11-14 20:17:53 +08:00
Gabriele GristinaandClaude Opus 5.5 601bb5e94f hf 15 raw: read the device reply with -r, refuse oversized data
With -r the device reply was left unread, so each later command
printed the answer of the one before. The reply is now read and
dropped.

Data that does not fit one packet with its 3-byte header is refused
at once, instead of SendCommandNG refusing it and the client waiting
2 s for a reply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 13:01:25 +01:00
Gabriele GristinaandClaude Opus 5.5 427bbc1036 hf search: probe the OpenPGP AID with its 6 bytes
The OpenPGP AID hint had length 8 for 6 bytes: the SELECT read past
the string and sent a name no OpenPGP card matches, so hf search
never found one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 11:58:26 +01:00
iceman1001andClaude Opus 5.5 35d28f39e1 PM5: charge to 4.2 V and put the fuel gauge on its 4.2 V profile
The fitted cell is 3.7 V nominal (500 mAh, 1.85 Wh), full at 4.2 V. The
charger stopped at 4.095 V and the BQ27427 stayed on its ROM default
4.35 V profile (chem ID 3230), so the gauge never saw a full charge:
Qmax kept its default 16384 and never updated, "charge done" read 83 %,
and a discharge reached 0 % with over 100 mAh still in the cell.

- default charge target 4200 mV (boot and plain `hw bwm vchg`)
- `hw bwm setcap` also selects chem ID 1202 (4.2 V) in the same config
  update and verifies both afterwards; still a no-op when correct
- boot re-applies the gauge setup after a gauge reset (Flags ITPOR, RAM
  back on ROM defaults) or when the profile is wrong, keeping a capacity
  set with setcap
- `hw dbg -3` shows the gauge chem ID on the Gauge line

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 16:07:15 +07:00
Niel Nielsen 7cbd66932b FPGA usercode added in banner 2026-11-14 09:12:24 +01:00
iceman1001andClaude Opus 5 a98c65d8ac hf 15 dump: reject a block answer that is longer than the block too
The length check only caught answers shorter than the assumed block size.
A tag with blocks larger than bytesPerPage - one that sends no memory size
field and is read with a too small --bs - answered longer, passed the
check and had each block truncated into the saved dump with no warning.

Compare for equality and name the size the answer implies, so a wrong --bs
is diagnosable in both directions instead of only one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-11-14 11:06:50 +07:00
iceman1001andClaude Opus 5 5d3f07e526 hf 15 info/dump: bound the systeminfo answer by the flags it sets
Both commands read the UID at d+2 and the optional fields at offsets
derived from the info flags, behind a resp.length >= 2 guard only. A tag
that announces fields it does not send, or truncates the answer, had the
UID and the memory layout taken from bytes past the payload. The reads
stay inside the fixed PacketResponseNG buffer, so they return zeros
rather than overflowing, but the values printed and dumped were not the
tag's.

iso15_sysinfo_min_len() gives the shortest answer that can carry what the
flags claim. info refuses a shorter one; dump reports it and falls back to
the --bs defaults it already has for a tag that gives no layout.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-11-14 11:06:09 +07:00
iceman1001andClaude Opus 5 e72c703002 hf 15: one place owns the card error message
The "iso15693 card returned error" line existed three times: in the
ISO15_ERROR_HANDLING_CARD_RESPONSE macro, in
iso15_error_handling_card_response() and in iso15_info_error_handling().

The macro now delegates to the function it was a copy of, and both
reporting paths call iso15_print_card_error(). The info path still prints
the 0F and 10 errors that the shared helper keeps silent, since
hf 15 restore reads them as the end of the tag memory.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-11-14 11:05:07 +07:00
iceman1001andClaude Opus 5 3b621df4f7 hf 15 info: check the length before printing the EAS sequence
NxpTestEAS() printed 32 bytes from the answer behind a resp.length >= 2
guard only. A tag that answers EAS ALARM with anything shorter had the
rest of the response buffer printed as its EAS sequence.

The read stays inside the fixed PacketResponseNG payload, so there is no
overflow, but the sequence shown was not the tag's.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-11-14 11:04:15 +07:00
iceman1001andClaude Opus 5 d1d4364393 hf 15: one helper for the tag layout check
eload, dump, restore and view each carried a verbatim copy of the
pagesCount/bytesPerPage bounds check and its message. Fold them into
iso15_tag_size_ok(); it prints the offending values and each caller keeps
its own cleanup, which differs between the four.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-11-14 11:03:55 +07:00
iceman1001andClaude Opus 5 f5ac4e9ae3 hf 15 dump: drop the field when the systeminfo stage fails
The GET SYSTEM INFORMATION request is sent with ISO15_NO_DISCONNECT, but
the timeout, short answer and CRC returns freed both buffers and left the
field on. The size check added later in the same function already calls
DropField(), so the function disagreed with itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-11-14 11:03:09 +07:00
iceman1001andClaude Opus 5 bcafc214b2 hf 15: honour -2 for the inventory that precedes an addressed request
getUID() hardcoded ISO15_HIGH_SPEED, so every scan step ran at 1 out of 4
even with -2. A tag that only answers at 1 out of 256 could not be found,
and the modulation changed halfway through the exchange.

Pass fast down from the callers that have -2. readHF15Uid (hf search),
hf 15 csetuid and hf 15 implant have no such option and keep high speed;
note that csetuid's -2 selects the gen2 magic command, not the data rate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-11-14 11:02:49 +07:00
Iceman 1f654ecab5 Merge branch 'master' into fix-hf15-info-parse
Signed-off-by: Iceman <iceman@iuse.se>
2026-11-14 11:47:28 +08:00
Iceman 3fff3de619 Merge branch 'master' into fix-hf15-info-eas
Signed-off-by: Iceman <iceman@iuse.se>
2026-11-14 11:46:23 +08:00
Iceman 328ac0a7a7 Merge branch 'master' into fix-hf15-dump
Signed-off-by: Iceman <iceman@iuse.se>
2026-11-14 11:45:06 +08:00
Gabriele GristinaandClaude Opus 5.5 0042fb18a5 hf 15 info: ST signature hex check, no-signature messages
- StCheckSig() ignored the result of hexstr_to_byte_array(). Signature
  blocks with a character that is not hex gave a 32-byte signature set
  only up to that character, the rest printed from an uninitialised
  buffer. The buffer is now zeroed, and such blocks print "Signature
  blocks are not a hex string" and return PM3_ESOFT.
- The two returns without a signature now say why: "No signature,
  block 63 is not K04S", or the block and the error it answered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 04:27:38 +01:00
Gabriele GristinaandClaude Opus 5.5 142260a4d0 hf 15 dump: mask the block size, fit the --bs fallback in the buffer
- The block size of the system info is masked with 0x1F, as in
  hf 15 info: its top 3 bits are RFU.
- Without a memory size in the system info, the dump reads up to 128
  blocks of --bs bytes, fewer when they do not fit in the 2048-byte
  buffer (64 with --bs 32), instead of refusing --bs above 16.
- --bs above 32, the largest ISO15693 block, is set to 32 with a
  warning. It was stored in a uint8_t, so --bs 256 became 0 and
  --bs 260 became 4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 04:21:43 +01:00
Gabriele GristinaandClaude Opus 5.5 eaf95d43bc hf 15 info: return the tearoff status of EAS ALARM, fix error macro
- NxpTestEAS() reported "EAS ... is not active" also when hw tearoff
  had stopped EAS ALARM. It now returns PM3_ETEAROFF. No answer is still
  "not active", for PM3_ETIMEOUT and PM3_EPARTIAL alike: the SLIX
  tested here sends nothing to EAS ALARM with its EAS mode disabled,
  and the firmware returned PM3_EPARTIAL in 7 of 12 runs, with no tag
  answer in the trace.
- ISO15_ERROR_HANDLING_CARD_RESPONSE tested d[0] instead of its own
  parameter. It now uses (data)[0] and (data)[1].

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 04:21:31 +01:00
iceman1001andClaude Opus 5.5 80ca56a5b1 hw bwm: align the vchg, setcap and name results, no double pairing line
ble pairing already prints the BLE status, which shows the new
pairing state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 10:12:20 +07:00
iceman1001andClaude Opus 5.5 3f06cb6908 hf mf hardnested: fix macOS build, constant state table path size
strlen() of the directory literal made the path buffer a VLA that
Apple clang folds to a constant and rejects under -Werror
(-Wgnu-folding-constant). Size it with sizeof and build it with one
snprintf.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 09:48:17 +07:00
iceman1001andClaude Opus 5.5 973193c53c hw bwm: cliparser options and clearer output
Parsing:
- all subcommands parse through the argtable: --on/--off, -m <mode>,
  -u <on|off> via CLIGetOptionList, wifi --status/--stop; no more
  sscanf positional words, so every option shows in the help
- wifi status and stop split into helpers; help examples aligned, the
  SSID example shows quoting

Output:
- 20 column labels as in hw status, state colour coded
- autooff: 4 lines; the idle timer shows when a client pauses it,
  USB / client / BLE inputs only in client debug mode
- wifi --status, wifipower and the bring-up share one WiFi state line
- ble status: the factory passkey is shown red; the pairing hint shows
  only while pairing is off, the passkey hint only for the default key

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 09:40:35 +07:00
iceman1001andClaude Opus 5 6608b52042 hf mf hardnested: cheaper partial sums and a parallel bitflip update
Two of the phases left after the state file and table build fixes.

PartialSumProperty() evaluated each of the 16 four bit suffixes as its own
chain of filter() calls, but the suffixes share prefixes, so walking them as
a binary tree needs 31 calls instead of about 64. Output verified identical
for all 2^21 state and half combinations.

update_allbitflips_array() accumulated an AND over 256 state arrays for the
even and the odd half one after the other, although the halves share no data.
Run them side by side.

init_part_sum_bitarrays  1.095 s -> 0.425 s
update_allbitflips_array 0.847 s -> 0.520 s

Key still recovered on a hard prng 4K, live and from a nonce file, and by
hf mf hardnested -t.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-11-14 09:08:32 +07:00
iceman1001andClaude Opus 5 89e8b491e1 client: let --ncpu accept the machine's core count
The check rejected ncpus >= detected_cpus, so on a 12 core host --ncpu 12
failed with "invalid number of CPU cores: --ncpu 12 (available: 12)" and the
highest value accepted was one less than what the client uses by default.
Reject only more than detected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-11-14 09:08:32 +07:00
iceman1001andClaude Opus 5 8f16b6feba hf mf hardnested: build the partial sum tables in one pass
init_part_sum_bitarrays() walked all 2^20 states twice per half of the
state, once for Sum(a0) and once for Sum(a8), so PartialSumProperty() ran
four million times although two million distinct results exist. Each state
then set its 16 bits one at a time, 67 million set_bit24() calls in total.

Walk the states once and use what the two bit patterns actually are:
  - a0 sets state << 4 | 0..f, which is one aligned half word
  - a8 sets state | 0..f << 20, the same pattern repeated every 2^20 bits,
    so fill the lowest block and copy it up

Same tables, 3.5 s -> 1.5 s. With the state file search fix the whole attack
on a 4K target goes 54 s -> 22 s, key recovered unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-11-14 09:08:32 +07:00
iceman1001andClaude Opus 5 3010ae07e4 hf mf hardnested: resolve the state table directory once
init_bitflip_bitarrays() called searchFile() for each of the 2 * 1023 bitflip
state tables, and for a table that is not present it called it three times,
once per supported extension. searchFile() walks the configured default paths,
so each lookup stats file.default.savepath, dumppath and tracepath.

Those preferences often point somewhere slow. With them on a Windows drive
under WSL, where a stat costs ~6.6 ms, the ~6000 stats cost more than half of
the whole attack. Loading the small LZ4 tables was slower than the 700 MB of
raw ones, because a missing raw table meant a second search.

All the tables live in one directory, so search for the first one only and
build the remaining paths from the directory it was found in. A broken install
with no tables behaves as before.

Table load on a FM11RF08S-era 4K target, with default preferences:
  raw tables present:  33.3 s -> 0.48 s
  LZ4 tables only:     36.5 s -> 1.09 s
  whole attack:        54 s   -> 24 s, same key recovered

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-11-14 09:08:32 +07:00
Gabriele GristinaandClaude Opus 5.5 c7ea870718 hf 15 dump: fix field offsets, buffer overflow and short block answers
- DSFID, AFI, memory size and IC reference were read at fixed
  positions, so on a tag without DSFID or AFI the block size and
  count came from the wrong bytes. They are now read after the
  fields the tag really sent.
- A memory larger than the 2048-byte dump buffer overflowed it. It
  is now refused.
- A block answer shorter than the block size was copied as a full
  block. It is now reported and not copied.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 01:30:17 +01:00
Gabriele GristinaandClaude Opus 5.5 01f3ff2be7 hf 15 info: fix field offsets, NXP lock bits and ST signature
- AFI, memory size and IC reference were read at fixed positions,
  so a tag without DSFID or AFI showed wrong values. They are now
  read after the fields the tag really sent. The block count no
  longer wraps to 0 on a tag with 256 blocks.
- The NXP DSFID and password protection pointer lock bits were read
  from the wrong bits. They are now bits 0x04 and 0x08, as in the
  SLIX2 datasheet (table 73).
- The ST25TV signature, 32 bytes, overflowed a 16-byte buffer. On a
  tag error the signature read now frees its request and drops the
  field. When the tag has no signature, it drops the field and
  returns success, so hf 15 info no longer ends with an error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 00:40:01 +01:00
Gabriele GristinaandClaude Opus 5.5 10084a7b5e hf 15 info: fix EAS status, tag errors, -2 and -u
- The SLIX tested here sends no answer to EAS ALARM when its EAS
  mode is disabled, and hf 15 info ended with an error. It now
  reports the EAS mode as not active.
- The two error helpers of the hf 15 commands printed the flags
  byte instead of the error code. They now print the error code.
  hf 15 info also prints the errors 0F and 10, which the helpers
  keep silent.
- The option -2 (1 out of 256) now also applies to the NXP and ST
  requests sent after the system info.
- A UID given with -u was sent in the wrong byte order, so no tag
  answered. It is now sent LSB first, as hf 15 rdbl does. A -u that
  is not 8 bytes long is refused instead of scanning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 23:33:12 +01:00
iceman1001andClaude Opus 5 9ef45637cf hf mf sen: index candidates when prioritising default keys
fm11_prioritize_defaults() walked the whole candidate list once per
dictionary key, and it is called for each sector with tens of thousands of
candidates and ~85k dictionary keys, so it did on the order of 10^10
comparisons per run.

Index the candidate keys first and skip a dictionary key that is not in the
list, which is nearly all of them. A key that is present still takes the
original scan, so the resulting order is unchanged. Falls back to the plain
scan when the index cannot be allocated.

Same key table recovered on a FM11RF08S, hf mf sen 6.1 s -> 5.2 s offline
and 7.4 s -> 6.6 s for a full run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-11-13 12:26:19 +07:00
iceman1001andClaude Opus 5 69ebbc8dca hf mf sen: faster default-key dictionary check
fm11_key_matches_nonce() runs once per dictionary key per unknown key slot,
up to 85442 * 34 times. For every one of them it malloc'd a cipher state,
clocked two full 32 bit keystream words and only then compared the first,
although nearly every key fails on the first byte.

Keep the state on the stack, compare a byte at a time and stop at the first
mismatch, and clock the second word only for a key that survives. Same keys
accepted, about 6x less time in that phase (2.9 s -> 0.5 s for a full 34 slot
scan of the 85k dictionary).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-11-13 12:23:43 +07:00
iceman1001andClaude Opus 5 c154f730c3 crapto1: much faster MIFARE Classic key recovery
Nested and static nested recovery ran lfsr_recovery32 once per nonce and
intersected two ~2^16 candidate lists. The cipher state is linear while the
nonce is fed in, so the states for one nonce are those for the other xored
with a known difference. Checking both keystreams inside a single recovery
leaves about one candidate instead of two large lists.

lfsr_recovery32_dual() does that: it skips the early bit times where the
difference prunes little (start chosen per call from an exact per bit match
probability, falling back to 0 on overflow), scans the first pass 64 states
at a time through a bit packed filter table, extends the tables branch free
in cache sized chunks, and replaces the 512 scattered sort buckets with one
contiguous counting sort.

lfsr_recovery32() is rebuilt on the same machinery, so hf mf sen, mfkey32,
mfkey64 and the nested tools all gain. It stays single threaded because
nested_util.c already calls it from four threads. recover() takes the second
keystream as optional extra state; the plain path is unchanged.

Both return the same key sets as before, verified against the old code under
ASan/UBSan over random and degenerate nonce pairs.

nested per key:            ~390 ms -> ~4 ms
lfsr_recovery32 per call:  ~270 ms -> ~165 ms
peak RSS, single nonce:    47.6 MB -> 27.6 MB

tools/mfc/protocol links pthread now, as the other two mfc tool dirs do.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-11-13 12:20:04 +07:00
Iceman 18728cc970 Merge branch 'master' into fix-mf-ndefread-4k
Signed-off-by: Iceman <iceman@iuse.se>
2026-11-13 11:11:13 +08:00
Iceman 20f59381be Merge branch 'master' into fix-mf-ndefread-4k
Signed-off-by: Iceman <iceman@iuse.se>
2026-11-13 11:04:55 +08:00
Iceman 2471ee133a Merge branch 'master' into fix-mf-ndefread-options
Signed-off-by: Iceman <iceman@iuse.se>
2026-11-13 11:04:08 +08:00
Iceman 4aa5dcb8ce Merge branch 'master' into fix-mf-mad-options
Signed-off-by: Iceman <iceman@iuse.se>
2026-11-13 11:03:08 +08:00
Gabriele GristinaandClaude Opus 5.5 e6e5c56b36 hf mf ndefread: describe the MAD key fallback in the command help
Review of #3807: the -k description is back to its short text, and
the command description says that the MAD is read with -k when the
default MAD key fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 03:47:38 +01:00
Gabriele GristinaandClaude Opus 5.5 0c7f0b0429 hf mf ndefread: read the whole of 4K sectors 32-39
The command put the 256 bytes of a 4K sector 32-39 into a 64-byte
buffer, a stack buffer overflow, and kept 3 of its 15 data blocks.

Now the buffer holds 16 blocks, and the command keeps every data block
of the sector.

Split from #3807.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 03:41:35 +01:00
Gabriele GristinaandClaude Opus 5.5 f458b1ccb4 hf mf ndefread: try -k on the MAD sectors, check -k and --aid
When the MAD key failed on sector 0, the command told the user to retry
with -k, but it used -k only for the NDEF sectors. A -k or --aid of the
wrong length was ignored without a word.

Now the command tries -k on the MAD sectors when the MAD key fails,
and a -k that is not 12 hex digits or an --aid that is not 4 is
refused.

Split from #3807.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 03:41:12 +01:00
Gabriele GristinaandClaude Opus 5.5 cc30d200ae hf mf mad: accept --override, check -k and --aid, fix -f --aid
--override was refused: the code read it at index 9, which had no
argtable entry. A -k or --aid of the wrong length was ignored without
a word. With -f and --aid the command printed no block of the AID.

Now --override has its argtable entry, a -k that is not 12 hex digits
or an --aid that is not 4 is refused, and -f with --aid prints the
AID's blocks.

Split from #3806.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 03:18:34 +01:00
Gabriele GristinaandClaude Opus 5.5 b80b13f73c hf mf mad: read the whole of 4K sectors 32-39
With --aid or --dch, the card read put the 256 bytes of a 4K sector
32-39 into a 64-byte buffer, a stack buffer overflow. The command
printed only the first blocks of the sector, numbered wrong from
sector 33, and gave --dch 3 of its 15 data blocks.

Now both buffers hold 16 blocks, and the command prints and decodes
every block of the sector.

Split from #3806.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 03:18:06 +01:00
Iceman 952e4effec Merge branch 'master' into fix/t55xx-st-stride
Signed-off-by: Iceman <iceman@iuse.se>
2026-11-13 09:51:32 +08:00
Iceman 3bfec259dc Merge branch 'master' into fix-cli-str-terminator
Signed-off-by: Iceman <iceman@iuse.se>
2026-11-13 09:45:24 +08:00
Iceman be3ab8099b Merge branch 'master' into fix-legic-options
Signed-off-by: Iceman <iceman@iuse.se>
2026-11-13 09:43:46 +08:00
Iceman a6d49da2bf Merge branch 'master' into fix-save-dump-return
Signed-off-by: Iceman <iceman@iuse.se>
2026-11-13 09:40:05 +08:00
Iceman e51d140ce5 Merge branch 'master' into fix-gallagher-test
Signed-off-by: Iceman <iceman@iuse.se>
2026-11-13 09:36:22 +08:00
Iceman 373d561bce Merge pull request #3804 from matrix/fix-mf-sen
hf mf sen: field off and a message on Enter, fix options and saves
2026-11-13 09:22:03 +08:00
iceman1001 9041810cdf style and textual changes 2026-11-13 07:57:33 +07:00
Matthew CarrollandClaude Opus 5.5 d83606a089 lf t55xx detect: report the sequence terminator on fsk hits
DetectST only runs on ask, and each fsk hit set its ST flag to false. An
fsk hit now reports ST if its block 0 word has the ST bit set and a copy
of the word sits 33 to 36 bits before or after it.

On a buffer of 92 bits or more, test() starts its scan at bit 28 and
often picks the second copy of block 0. On a short capture the copy
after that word may not fit, so the check also looks behind it. If
neither side has room for a copy, the ST bit decides. A Q5 word still
reports no ST.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 17:42:16 -07:00
Matthew CarrollandClaude Opus 5.5 65802994b2 lf t55xx detect: report the sequence terminator only if DetectST found one
ASKDemod_ext sets its ST flag if DetectST cut terminators out, and never
clears it. The four ask attempts that call it preset the flag to true,
so every hit from them reported ST. Each of those hits now starts from
false and reports ST only if DetectST cut terminators out of that
demodulation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 17:41:50 -07:00