- Decode the first post-header byte as SW1 before the ACK/data decision:
a case-2 rejection (6A82/6CXX) carries no ACK and used to be read as
command data, silently consuming the exchange and the next one.
- Short responses (Lr < Le): after the P3-byte read, split the buffer at
the first plausible SW1 followed by a plausible CLA, replay the tail and
flag BAD_FCS; previously the SW scan discarded the real SW, merged the
following exchanges and lost the exchange at EOF.
- P3=0 is Le=256 for an outgoing command: keep the response data (read
until SW) instead of discarding it, including data bytes equal to INS.
- ~INS ACK: one data byte per ACK via the new helpers; case-4 inline data
is appended (permissive; ISO 7816-3 §12.2 maps case 4 to GET RESPONSE).
- tests: four TestDecodeStep vectors (immediate SW, short read + split +
replay, Le=256 data incl. an INS-valued byte); 99/99.
- AGENTS v1.9.5 + T=0 section, README/README_RUS bullets; baseline
regenerated (xiaomi_mi_a1_coldboot 473 -> 475 APDUs, all else identical);
compare_baseline: IDENTICAL.
- tests: TestDecodeStep drives decode_step() with a scripted byte queue
(read/peek/consume stubbed over _replay + the queue; the re-align/PPS/
ATR/annotation sinks stubbed): 'CX'/'EX' exchanges frame and emit clean,
0x5F frames as a command (it was always eaten by the idle-noise gate
before v1.9.3), the 0x61 text-byte boundary is a one-byte suspicious-CLA
discard, the invalid-INS re-frame recovers a shifted SELECT, 0xFE still
takes the idle-noise path.
- tests: TestAtrHuntDetect drives handle_atr(): 0xC4 0xA4 / 0xC4 0x89
hand the header to DATA via _replay, CLA 0x00 stays excluded, an invalid
INS does not hand off.
- pd.py: the hunt command-detect guards peek_byte() returning None (a
capture ending right after a plausible CLA byte raised TypeError, seen
only by decode()'s crash protection);
test_eof_after_plausible_cla_is_clean pins the clean return.
- AGENTS v1.9.4 section; baseline regenerated (version + test listing;
compare_baseline: IDENTICAL); 95/95 unit tests.
- pd.py/AGENTS: the rejected '8X' range now reads 0x81-0x83/0x85-0x8F
(0x84 stays accepted); the 0x40-0x4F vs 0x50-0x5F citations are split
(chaining is ISO-only, TS 102 221 keeps b5=0); the bit-dense CLA list
gains 0xE7 (exact set verified).
- README_RUS.md: the invalid-INS note mirrored (the removed odd-LSB rule,
citation corrected to 5.1.2).
- tests: test_vs_reader_mirror (pd.py <-> tools/vs_reader.py drift guard),
test_odd_ins_set_pinned, the 0xE7 noise vector, and the 'EX' vector now
uses STATUS (e2 f2) as TS 102 221 Table 10.5 defines.
- tests/baseline.txt regenerated (test listing only); compare_baseline:
IDENTICAL.
- handle_pps(): flag the first post-PPS character; _read_byte_clk() re-measures
the CLK period there and accounts the measurement's 16 edges. The Alcor
reader upclocks 4.0 -> 4.8 MHz at PPS (FI/DI still 512/32), so the ATR
measurement was stale: 64 vs 53.33 samples/ETU, no post-PPS APDU framed.
- _wait_clk_rising(): project to the n-th edge (skip to target - half a
period, take one edge) instead of "skip n-2 cycles + 2 edges", which lost
~1 edge per call on fractional periods and slid the samples ~0.5 ETU early
by the parity bit. xiaomi 7698 -> 2, 4gmodem 7218 -> 0, S21p_sim2 394 -> 0
CHKSUM lines (previously misread as dirty-wire noise).
- CONCAT gate: EMBEDDED_CLA excludes the structural 0x4x/0x5x classes (payload
false hit on the Alcor secured packet); mirrored in tools/vs_reader.py and
covered by a new vector in tests/test_gsmtap.py.
- tools/capture_baseline.sh: add the alcor_reader_sample fixture; baseline
regenerated (APDU counts and RESULT: OK unchanged everywhere, 0 CONCAT).
- 75/75 unit tests. README/AGENTS updated.
Follow-up to ad3ff6b: the two remaining prose mentions of the old
short name become SIMtrace Analyser (AGENTS.md v1.9.0 note, test
comment). Upstream 'simtrace2-sniff'/'simtrace2' references stay --
those are the Osmocom tool/hardware, not the renamed project.
v1.9.0 -- CLK frequency in RST events:
- RST events (sub_type 0x10) append the measured CLK frequency when known:
[direction, level, flags, clk_hz_be32], flags bit 0 = rate present; the
3-byte form is kept when the rate is not yet measured. Value is
samplerate / _samples_per_clock from the existing 16-edge measurement (PPS
changes F/D, not the CLK rate). VCC events unchanged. Backward
compatible (old consumers read the first two bytes only); consumers can
compute data_rate = clk_hz x D / F from the ATR/PPS Fi/Di.
- Unit tests for the payload codec; README/README_RUS/AGENTS updated.
v1.9.1 -- T=0 NULL is not an SW1:
- The 'scan for SW1' loops accepted any 0x6X byte, including the NULL
procedure byte 0x60 (card busy), so 'NULL + real SW1' was mis-framed as
the status word: the phone traces' SELECT MF came out as ...3f00 6061 —
real SW1 0x61 eaten as SW2, real SW2 0x29 left on the wire and discarded
as a suspicious CLA (desync cascade).
- plausible_sw() now accepts 0x61-0x6F and 0x90-0x9F only, per
ISO 7816-3:2006 10.3.3 Table 11 ('6X' or '9X', EXCEPT '60'; ISO 7816-4
enforces '60' as an invalid SW1) and UICC_SPECS.md 1.8. All seven
SW1-detection sites use plausible_sw(); NULLs met while scanning are
logged as rate-limited wait extensions (_note_pre_sw) instead of
suspicious bytes.
- tools/vs_reader.py::_plausible_sw() mirrors the fix; regression vector
for the 00a40004023f006061 artifact.
Validation: 74/74 unit tests; baseline regenerated and IDENTICAL (the SW
fix is count-neutral -- only the SW bytes change); all 9 example pcaps
regenerated -- vs_reader OK, zero APDUs ending in 60 61, SELECT MF now
61 29 / 61 25.
- Split the PPS exchange into the two standard GSMTAP-SIM subtypes:
0x02 (PPS_REQ, raw request bytes, span ss..es_req) and 0x03 (PPS_RSP,
raw response bytes, span es_req..end), replacing the single 0x02 packet
with concatenated request+response that deviated from libosmocore's
gsmtap.h. Wireshark now dissects both halves; simtrace2-pysniff needs
no change (its importer already maps 0x03 to 'pps' and _decode_pps()
parses the 0xFF-leading response frame). PPS annotation/log unchanged.
- gsmtap_stream.py: GSMTAP_SIM_PPS_REQ/RSP constants; header field labels
aligned with libosmocore's struct (signal_dbm/snr_db).
- Docs: GSMTAP event tables (EN/RU) updated, PPS deviation note removed;
corrected the res-byte flag attribution -- upstream simtrace2 never
sets res, so the GSMTAP_FLAG_BAD_FCS carriage is this decoder's own
extension (semantics mirror simtrace2's USB data flags).
- Tests: assert the PPS subtype values; baseline regenerated (only the
version line and test listing differed -- no decode-metric changes).
68/68 unit tests.
Full translation following the otaman README_RUS.md convention (prose and
code-block comments translated; commands, flags and paths unchanged).
Links back to the English README at the top.
Links the first mention and documents it as the recommended tool for
visualising live traces and viewing saved captures, with GSMTAP pcap
import/export support.
The recommended tap is the short cable with external pull resistors whose
pins are fixed and known in advance (D5=CLK, D0=I/O, D4=RST, D2=VCC), so
the auto-detection tool is no longer needed. Drop it and all references:
- Delete tools/detect_pins.py.
- README: replace the 'Detecting the wiring' section with a fixed-wiring
note; drop the Python/numpy prerequisite and the detect_pins capture
step from the Windows guide.
- setup.bat: remove the Python/numpy check and the 'find the wiring' next
step.
- pd.py: remove _lock_etu_from_edges (long-disabled) and the no-op
while-True FIND START wrapper.
- pd.py: re-raise SystemError (libsigrokdecode's clean EOF signal) in the
six 'except Exception: pass' guards so put()/wait() at end-of-capture
exits cleanly instead of being swallowed.
- pd.py: fix _measure_clock_period docstring (averages, not robust-min);
drop stale 'line 1633' comment; document the synthetic pcap UDP header
(zero IP checksum / fixed loopback+ports are intentional).
- signal_diagnostic.py: add --max-samples cap + document whole-capture
RAM cost; fix stale '--rst/--vcc=ignore' guidance (-> --no-rst/--no-vcc).
- tests: make test_valid_cla_interindustry_classes use subTest (was a
no-op tuple expression).
- start.sh/start.bat: note the -A multi-spec form is sigrok-cli 0.7.x.
- Remove stale tests/baseline_new.txt (v1.1.3-era, ATR-included mode).
- Add top-level LICENSE (GPL-2.0-or-later).
- pd.py ts_from_samplenum: drop c_uint32 truncation + dead sampleOverflowCount
(pcap timestamps wrapped backwards past ~4.5 min at 16 MHz).
- pd.py handle_atr: guard None reads at EOF (was TypeError (None & 0x80) on a
capture truncated mid-ATR-hunt).
- start.bat: add --protocol (+ :protocol=T=0 default) to match start.sh's
mid-session decode; correct the 'flag-for-flag' header comment.
- start.sh: reject --rst/--vcc assigned to the same pin (was unchecked).
- setup.bat: set DEVICE_MSG on blocked/skipped probe so the trailing
'unknown' message is suppressed.
- vs_reader.py: RESULT now fails on CONCAT / payload mismatch / suspicious
CLA, not just GARBAGE (OK text unchanged; clean traces stay identical).
Full -A iso7816 printed all annotation rows including the per-byte 'bytes'
row (thousands of lines per capture; ~22k on xiaomi_phone, ~11k on A55).
Default is now '-A iso7816=apdus,iso7816=warnings' (decoded APDUs + decoder
warnings/errors only); --debug restores the full '-A iso7816' output plus
the existing -l 4. stderr decoder log (banner, PACKETEND hex, glitches,
discards) is untouched.
Residual CONCAT mis-frames (4gmodem/A55_sim2/S21p_sim2) traced to a silent
5-byte drop at an invalid-INS T=0 header (0x0f 00 a4 08 04). Drop only the
stray leading byte and re-frame the remaining four via _replay so the real
command (00 a4 08 04 02 2f 00 -> 61 1c) is recovered.
- _note_discard()/_consume_first(): every consumed-but-unemitted byte is
now logged; idle 0x00/0xFF discards are rate-limited, real data always
gets an ANN_WARN annotation.
- All read-until-SW loops log skipped non-SW bytes.
- All traces: CONCAT 0; BAD_FCS/GARBAGE 0; test_8/test_7 RESULT OK;
47/47 unit tests; baseline byte-identical.
History rewritten to a single root commit so no example trace is stored
in the repo (fixtures are local-only, gitignored). README keeps only
generic <trace>.sr placeholders; AGENTS.md retains the fixture names.