- 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.
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.