The first-run step explained that the PWA and the API share an origin and no
CORS/Private-Network-Access setup is involved - noise for the default setup,
which always serves both from the same server. The line is gone (both
languages), the Usage server intro no longer repeats it either, and the one
place where it matters - the hosted PWA, which talks to a local server from
another origin - now says so explicitly before the browser requirements.
The prerequisites read as if PyUSB, libusb and SIMtrace2 hardware were needed
generally. They are only for `--capture direct` (and the bundled
`simtrace2-pysniff` utility): the analyser starts and captures GSMTAP with
just Python 3.9+ - verified in a fresh venv without PyUSB, where the server
served the PWA and a GSMTAP ATR+APDU landed in the session (2 messages).
Both READMEs split the list into "Required" (Python 3.9+) and "Only for
capturing from Osmocom SIMtrace2 hardware", and state that GSMTAP capture
(the source may even run on another machine), --capture disabled and PCAP
imports need none of it. No code change; 557 Python + 80 Node tests.
The startup instructions were buried after Components/Features and mixed
release/download variants with runtime detail; a reader had to scroll through
the usage guide to find how to launch it, and the start scripts were only
mentioned in passing.
Both READMEs now lead with Prerequisites then a Quick start with three clear
paths - wheel (pip), runtime zip (start.bat / ./start.sh) and a git clone
(launchers, optional pip install -e .) - followed by First run (URL, capture
modes, Ctrl+C). The detailed runtime guide moves under `Usage`: the server
and PWA, COMPRION import, the Files view, the hosted PWA, the capture utility,
the utility CLI options, output formats, the daemon, library usage and
recovery. Components and Features follow the usage guide; USB device access
keeps its anchor.
Only the Analysis server intro gained the launcher note; every moved section
is byte-identical (checked old vs new). 557 Python + 80 Node tests.
A container's 127.0.0.1 is its own loopback, so a GSMTAP feed sent to the
container's bridge address never reached the listener: it was hard-bound to
127.0.0.1.
- `simtrace-analyser-server --gsmtap-bind ADDR` (default 127.0.0.1) is passed
through GsmtapListener to GsmtapReceiver (whose bind_ip already existed),
including the receiver re-created by a start() after close. The startup
banner prints the effective bind address next to the port.
- READMEs (EN/RU) document the option, naming the container case.
- tests: the listener forwards the address on construction and on start(),
binds 0.0.0.0, and keeps the loopback default.
Verified end-to-end: with --gsmtap-bind 0.0.0.0 the socket listens on
0.0.0.0:14732 and an ATR + APDU sent to 127.0.0.1:14732 landed in the capture
session (2 messages); without the flag it listens on 127.0.0.1.
557 Python + 80 Node tests.
The project's only runtime dependency is PyUSB (BSD-3-Clause) and it bundles
no third-party code (the PWA's DES/3DES is written from the FIPS 46-3 tables,
AES uses WebCrypto, the decoders come from the ISO 7816/ETSI/3GPP specs), so
nothing forced GPL; the GPL-2.0-or-later declaration is replaced with MIT.
- LICENSE: the MIT text, Copyright (c) 2026 Anton Troshin.
- pyproject: license = "MIT" + license-files = ["LICENSE"] (PEP 639), and the
build requirement moves to setuptools>=77 for the SPDX form. The wheel now
carries dist-info/licenses/LICENSE and License-Expression: MIT, the sdist
and the runtime zip carry LICENSE at the top.
- READMEs (EN/RU): a License section (original decoders/DES implementation,
PyUSB as the only runtime dependency).
- tests: the packaging test pins the MIT declaration and the LICENSE text;
the release-zip test requires LICENSE in the archive.
- version 2.7.13; SW cache v95.
Copies published before this tag (v2.7.10-v2.7.12) remain under the license
they were released with.
The releases carried the wheel and the sdist; the only zip was GitHub's source
archive of the whole repository (tests, CI, tools). A Windows user just wants
to extract a folder and double-click start.bat.
- scripts/build_release_zip.py builds simtrace-analyser-<version>.zip with a
single top-level directory: the two Python packages, the PWA runtime files
(frontend/tests and package.json stay out), the four launchers, the udev
rule, the READMEs and the screenshot. The .bat entries are written with
CRLF line endings (cmd.exe mishandles bare-LF labels/goto), the .sh files
keep their executable bit; LICENSE is included automatically once the repo
has one.
- tests/test_release_zip.py pins the archive shape, the exclusions, the sw.js
PRECACHE drift (every precached asset must travel), the CRLF/LF line endings
and start.sh's mode.
- the release workflow builds the zip, checks it carries start.bat and the
PWA, uploads it with the run and attaches it to the release.
- READMEs (EN/RU) and the release-notes template offer the zip as the
Windows-friendly option; version 2.7.12, PWA cache v94.
Verified: the built zip (34 entries, 658 KB compressed) extracted and started
on Linux serves the same index.html; 553 Python + 80 Node tests.
The release notes and the README's local-wheel examples still led with
`pipx install`; only the direct-URL form had been switched to pip earlier.
Both READMEs now show a plain `pip install <wheel>` (with the PEP 668 note to
use a virtual environment when the system Python is externally managed), and
the `gh release download` example installs with pip too. The v2.7.10 and
v2.7.11 release bodies are edited to match.
Ships the launcher/doc commit on top of v2.7.10 as a release:
- start.bat passes --db/DB to the server (it only stored the value before) and
sends --gsmtap-port only for gsmtap capture, like start.sh;
- both .bat launchers quote their values and keep the quoting of pass-through
options, so paths with spaces work (--db, --web-dir, --pcap, --output);
- the READMEs describe both install paths (release artifacts or a git clone)
and document the launchers and the Windows python-on-PATH requirement.
Version strings in version.py, pyproject.toml, PWA_VERSION and the version
test move together; SW cache simtrace-analyser-v93.
- the Install section now leads with the release artifacts (the wheel via
pipx/pip, the direct release URL, `gh release download` for the
version-agnostic case, and the source archive) and keeps the git clone as
the development path. It finally names the launchers (./start.sh,
start.bat): a clone needs no install, but the README never said how to start
it while the Running section only showed the console script that an install
provides.
- Windows: the USB section now says the .bat launchers mirror the shell ones
and call the `python` command (Python 3 must be on PATH); the Install section
carries the same note.
- start.bat: --db/DB was parsed and then never passed to the server; it is
passed now, and --gsmtap-port is sent only for gsmtap capture (as start.sh
already did). Both .bat files passed values unquoted and stripped the
quotes of pass-through options, so paths with spaces broke
(--db "C:\My Data\...", --web-dir, --pcap, --output); values are quoted now
and pass-through keeps the original quoting (argument assembly through
delayed expansion).
- the launcher usage comments include the `disabled` capture mode.
- verified the argument assembly with Wine cmd.exe and an echo harness: the
quoted paths survive, --db arrives, --gsmtap-port appears only for gsmtap;
547 Python + 80 Node tests.
- pyproject declared build-backend = "setuptools.backends._legacy:_Backend",
which does not exist: `python -m build`, `pip install .` and even
`pip install -e .` died with "Backend ... is not available". It is
setuptools.build_meta now.
- the wheel and sdist carried only the Python packages, and _default_web_dir()
resolved <site-packages>/../frontend, so an installed server started fine but
answered every static request with 404. setup.py's build_py now copies the
checkout's frontend/ into simtrace_analyser/web/ inside the build (the PWA's
tests excluded), and _default_web_dir() prefers that packaged copy, falling
back to <repo>/frontend for source and editable runs. The source directory
is resolved from setup.py (so an sdist build works) and a missing frontend/
fails the build loudly instead of shipping a package without its UI.
- MANIFEST.in grafts frontend/ (with the READMEs, the udev rule and the four
launch scripts) into the sdist, so a wheel built from the sdist has the PWA.
- README (EN/RU): an install-from-a-release section (pipx/pip with the wheel,
the direct release URL for pip) and the --web-dir default documented as the
bundled PWA, or <repo>/frontend from source.
- tests/test_packaging.py pins the packaged-first resolution (including the
empty-directory fallback), the source checkout default, the real build
backend, the manifest graft and setup.py's bundling.
- verified: a fresh venv install from the wheel and from the sdist serve
GET / (200, byte-identical index.html, PWA v2.7.10) out of
site-packages/simtrace_analyser/web; an editable install still resolves
<repo>/frontend. 547 Python + 80 Node tests.
- EF_PBR (TS 31.102 §4.4.2.1) is the card's declaration of its phonebook:
the constructed tags A8/A9/AA announce the type-1/2/3 groups and each nested
primitive tag C0…CC names the file kind plus its FID — and, in a three-byte
object, its SFI (length 2 = FID only). pySim has no parser for it (its
EF_PBR class still carries a "# TODO: a80ac0034f3a…" stub), so the decoder
follows the spec: new simtrace_analyser/pbr.py
- the tracker decodes every EF_PBR record it observes (a READ RECORD's data
or an UPDATE RECORD's body) and uses it for the DF it belongs to: the
phonebook files get their symbolic names (4FA1 = EF_PBC, 4F3A = EF_ADN,
4F01 = EF_IAP, 4F4A = EF_EXT1, 4F3D = EF_CCP1, 4F61 = EF_EMAIL, …), an
earlier placeholder name is upgraded when the PBR arrives later, and the
declared SFIs feed the DF's SFI→FID map (so SFI-addressed reads resolve
without waiting for an FCP)
- verified on the live sessions 47/193:
DF_TELECOM/DF_PHONEBOOK/EF_PBC (4FA1) and the SFI map
{2→4F3A, 4→4FA1, 6→4F4A, 7→4F4F}
- also fixed: the "SFI = FID's five low bits" fallback (TS 102 221
§11.1.1.4.8) no longer applies to a DF/ADF's FCP, which has no SFI
- tests: tests/test_pbr.py (the two real records, SFI-less objects, padding,
truncation, merging, the shape check) and tracker cases (names, path,
SFI map, the later-PBR upgrade); 537 Python + 76 Node tests
- README/README_RUS document the PBR naming; PWA_VERSION/pyproject/
version.py 2.7.7, SW cache simtrace-analyser-v87
- the tracker kept only the immediate parent DF, so a phonebook file read as
"DF_PHONEBOOK/4FA1" — the trace's own selection path (00a4080406
7F105F3A4FA1) spells out the whole chain; the channel state now carries it
(selected_df_state() returns (df, path), e.g. ('5f3a', ['7f10', '5f3a'])),
the tracker stores it per file and _file_path() renders it:
"DF_TELECOM/DF_PHONEBOOK/4FA1"
- chain rules: a child DF selected by FID appends to the current chain, MF and
the ADF restart it, an AID is its own root, a path selection uses the DFs
in the path (an explicit '7fff' collapsing to the ADF), and an EF selects
keeps it. A leading MF is implicit in a longer path; MF-only files keep
"MF/…", ADF files "ADF/…" (the previous labels unchanged)
- the chain is learned from the FCP sighting too and extends a shorter path
recorded earlier, so entries created from a GET RESPONSE still get the full
path
- verified on the live traces (sessions 47/193): 5f3a/4fa1 ->
DF_TELECOM/DF_PHONEBOOK/4FA1, 5f3a/4f30 -> DF_TELECOM/DF_PHONEBOOK/EF_PBR
(4F30), while 3f00/2fe2 -> MF/EF_ICCID (2FE2) and 7fff/6f07 ->
ADF/EF_IMSI (6F07) stay as before
- tests: selected_df_state (path/FID/MF/ADF/AID/EF cases) and a tracker flow
with a path selection asserting the full path; 529 Python + 76 Node tests
- README/README_RUS show the chain; PWA_VERSION/pyproject/version.py 2.7.6,
SW cache simtrace-analyser-v86
- the hex views padded every line to 16 bytes with '--', so a 2-byte file
looked like it was missing data; the row now ends at the known file/record
length (from the FCP — hexImageRows gained the size parameter), while '--'
still marks bytes *inside* the file that were never observed (an unknown
length keeps the old row padding, nothing better is available)
- the offset column is shown only when a view spans more than one row — a
four-hex-digit 0000 above two bytes was noise
- the legend lists only the marks actually rendered: 'never observed' is gone
from a short/fully-observed view, and 'newly observed' when nothing was
first observed (the summary line still counts both)
- tests: pure hexImageRows cases (the 2-byte file, a 9-byte record with an
unobserved tail, the unknown-size fallback) plus two renderFileDiff cases
pinning the missing filler/offset and the trimmed legend; 527 Python +
75 Node tests
- README/README_RUS describe the new hex-view behaviour;
PWA_VERSION/pyproject/version.py 2.7.3, SW cache simtrace-analyser-v83
- the 'Proactive + events' preset is gone: the proactive one (FETCH,
TERMINAL PROFILE and the SW 91xx initiators) plus the individual ENVELOPE
entry cover it, and its Russian key is removed with it
- with several entries checked the filter button now reads "Selected: N"
(«Выбрано: N») instead of "N commands"; a single entry keeps showing its
name and an empty selection stays "All commands"
- tests: the removed preset's case is replaced by an absence pin in the menu
test, and the summary case follows the new wording; 527 Python + 72 Node
tests
- README/README_RUS list two presets; PWA_VERSION/pyproject/version.py 2.7.2,
SW cache simtrace-analyser-v82
- the single-command <select> becomes a checkbox dropdown: the filter is a
set of entries (an empty set shows everything), so any number of commands
can be shown at once instead of one
- three fixed presets select whole conversations:
* Proactive sessions — FETCH, TERMINAL PROFILE and every command the card
answered with SW 91xx (the proactive-session initiators)
* Proactive + events — the same plus ENVELOPE
* File operations — SELECT, READ BINARY, READ RECORD, UPDATE BINARY,
UPDATE RECORD (their GET RESPONSE, which carries the data/FCP, comes
along through the existing response_for linkage)
presets and single commands combine as a union, and the menu lists the
session's commands with their counts
- the button summarises the selection ("All commands" / one name /
"{n} commands"); Escape or a click outside closes the menu; focusing an
APDU from the Files panel/map still clears the filter first
- tests: a new filter test (the matching rules per entry kind, the GR
linkage, the presets, the union, the summary, the menu rows, the RU keys
and the wiring pins), and the focusMessage tests follow the new state;
527 Python + 72 Node tests
- README/README_RUS describe the filter; PWA_VERSION/pyproject/version.py
2.7.0, SW cache simtrace-analyser-v80
- an update row whose image changed (or whose bytes were first observed)
offered a generic "Details" link; it now says "Show diff" — Russian
«Изменения» — while a card-side change keeps its "Changed on card!" link
(both open the same change modal)
- the now-unused 'Details' RU key is gone; tests update the wiring pin and
the Russian-translation list; 527 Python + 61 Node tests
- README/README_RUS mention the new label; PWA_VERSION/pyproject/version.py
2.6.5, SW cache simtrace-analyser-v79
- a card-side change is discovered by a read operation, so the timeline row
now reads "Read EF_IMSI" — the row keeps its warn colour and layout — with
"Changed on card!" as the trailing underlined link that opens the change
modal; an update row keeps the generic "Details" link
- the 'Changed on card' verb string is gone; the new 'Changed on card!' key
carries the Russian «Изменено на карте!»
- tests: the verb/label cases updated (card_change -> Read, "Read EF_LOCI")
and the Files-view wiring test pins the kind-dependent link label;
527 Python + 61 Node tests
- README/README_RUS describe the new row wording; PWA_VERSION/pyproject/
version.py 2.6.4, SW cache simtrace-analyser-v78
- the header title drops its "UICC" prefix — "GSMTAP trace analyser" is enough
- the trace topbar's search input is half its previous width (flex basis
33.333% -> 16.667%); the session name now ellipsizes instead of wrapping
the tighter row
- the side-panel view selector moved out of the scrollable side panel into
the trace topbar: right-aligned over the panel at the panel's width
(--side-panel-w: 260px, the negative right margin cancelling the topbar's
16px padding so the edges line up), so it stays visible at all times while
the panel scrolls
- tests: the topbar/selector position pins (the Files-view wiring test and
style.test.js), the shared width variable, the halved search basis and the
title; 527 Python + 61 Node tests
- README/README_RUS: the selector's new place; PWA_VERSION/pyproject/
version.py 2.6.3, SW cache simtrace-analyser-v77
- focusMessage()/scrollToType() highlighted the target and removed the class
after 1500 ms, so a Files-row (or map/category) jump looked selected for a
moment and then lost it; on top of that any re-render (the 500 ms live
poll, a filter/search change) rebuilt the list and dropped the highlight —
even for a clicked entry
- the selection is now persistent app state (STATE.selectedIdx): the render
marks the entry (gap rows included), focusMessage records the index before
rendering, showDetail records it for plain clicks, scrollToType persists it
too (clearing the previous highlight), and a new session/capture resets it
so a stale index cannot mark an unrelated APDU
- tests: a stub-driven focusMessage case (state + detail updated, no render
and no timer), the filter-clearing case, and source pins for
renderMessages, focusMessage, scrollToType, showDetail, loadSession and
startCapture; 527 Python + 58 Node tests
- README/README_RUS: the jump leaves the record selected; PWA_VERSION/
pyproject/version.py 2.6.2, SW cache simtrace-analyser-v76
- jumpToMessage() rendered the APDU list through the unqualified
renderMessages(), whose tail pins the view to the bottom (the live-capture
stick), and only then let focusMessage() smooth-scroll back up to the
clicked record — the visible jump-to-end-then-back. It now renders with
skipScroll, so the only scroll is the focus itself
- the content-follow became conditional: shouldStickToBottom() pins the view
to the newest messages only while the reader is already at the bottom
(40 px slack), so the 500 ms live poll no longer pulls away a view the
reader scrolled to — a Files/map jump is no longer yanked down by the next
batch. A freshly opened session or a new capture still lands at the
bottom (renderMessages(false, true))
- tests (node): the jump's paging/render(true)/focus sequence with stubs, the
no-session no-op, the stick decision (at bottom / within slack / scrolled
up / explicit jump / forced / first render) and source pins for
renderMessages, jumpToMessage, loadSession and startCapture; 527 Python +
53 Node tests
- README/README_RUS note the follow behaviour; PWA_VERSION/pyproject/
version.py 2.6.1, SW cache simtrace-analyser-v73
- CLA decode (TS 102 221 §10.1.1/§10.3): standard channels 0-3 from b2-b1
and the extended channels 4-19 from b4-b1. The old further-format rule
read only b2-b1 (channel 8 read as 6, 19 as 7) and the ETSI '11x0 xxxx'
coding (0xC0-0xEF) was not handled at all; the class ranges now follow the
specs (0x80-0x8F UICC, 0xA0-0xAF SIM, 0xC0-0xEF ETSI extended, 0x20-0x3F
reserved, 0xFF invalid) and SM/chain are decoded per family — the SM
extraction and the chaining bit (b5, not b6) were off by one bit
- per-channel state: database._decode_rows/_replay_context use the shared
cla_channel() helper — before, CLA & 0x03 with no +4, so a channel-5
SELECT clobbered channel 1's selection and a channel-8 one channel 0's
(the local traces already open channels: 29 MANAGE CHANNEL opens, 1-2)
- COMPRION T=0 import: the synthesized GET RESPONSE keeps the channel —
a plain CLA for 0-3 (as real terminals do) and 01x0 for 4-19
- ATR historical bytes: the UICC's COMPACT-TLV objects (TS 102 221 §6.3.1)
with card service data, the card capabilities (ISO 7816-4 §8.1.1.2.7
Table 88: assignment methods and max logical channels — the workspace
cards announce 4 and "8 or more") and the status indicator ('00' trailing
bytes / the '8Y' TLV); a truncated ATR no longer raises
- MANAGE CHANNEL success replies decode the assigned channel byte
- capture/import: a 'tpdu' packet that is not APDU-shaped (shorter than the
header, CLA FF, ISO-reserved class) is skipped and counted, so a
character-level or mis-sampled stream no longer pollutes the decode
(the local DB's outlier sessions are exactly that)
- tests: CLA table vectors, per-channel state isolation on channels 8/12,
COMPRION GR channels, filetrack extended channels, ATR compact-TLV and
card-capability cases, the capture skip counter, the PWA rows; 527 Python
+ 47 Node tests
- README/README_RUS document the extended channels, card capabilities and
the skip counter; PWA_VERSION/pyproject/version.py 2.6.0, SW v72
- the file/DF/SFI dictionaries move to simtrace_analyser/ef_tables.py and are
extracted from the pinned specs: TS 102 221 (MF, Annex H.1), TS 31.102
(ADF USIM 4.2 and the child DFs 4.3/4.4/4.6, the H.1-H.12 SFI annexes, the
Annex L.6 Relay Node/certificate files; Annex A/E as the ADF/DF file
completeness cross-check), TS 31.103 and TS 51.011 - 20 DF tables (~370
entries) and 14 SFI tables, up from ~180 flat names and 3 SFI tables
- names are DF-scoped: a FID reused across DFs resolves to the file its DF
context names - 4F09 is EF_SUPI_NAI under DF_5GS but EF_PROSE_GC under
DF_ProSe, 6F07 is EF_IMSI in the USIM/SIM but EF_IST in the ISIM, 6F4E is
EF_EXT4 in DF_TELECOM but EF_EXT5 in the USIM ADF; the flat table keeps
only names that are identical everywhere and is the fallback when no DF is
known (the two names the old table had wrong there are fixed: 6F0A is
EF_AC_GBAUAPI, 6F53 EF_LOCIGPRS)
- the SELECT-path decoder resolves the 7FFF children against the USIM ADF
table, and the Files view names files DF-first - including files first seen
only via their FCP (a select-only file used to keep the bare FID, e.g.
ADF/6F3C is now ADF/EF_SMS (6F3C))
- tests/data/spec_files.json pins the extracted tables; tests/test_decode.py
asserts the DF/ADF tables cover them, the SFI tables match them exactly and
every flat name is a spec name for that FID; tests/extract_spec_tables.py
regenerates the fixture from the PDFs and --check re-verifies the tables
- README/README_RUS document the dictionary and its sources; PWA_VERSION and
pyproject/version.py 2.5.0, SW cache simtrace-analyser-v71
- the modal header showed only the flat name (EF_ICCID, 4F09); it now shows the
path built from data already available: the tracker's DF identity named via
KNOWN_FIDS (MF, DF_GSM_ACCESS, DF_TELECOM, …), 'ADF' for the current
application (7FFF), DF_CHILDREN for DF-qualified EF names (4F52 →
EF_KcGPRS) and the EF.DIR labels for AID-selected applications —
e.g. `MF/EF_ICCID (2FE2)`, `ADF/EF_IMSI (6F07)`,
`DF_GSM_ACCESS/EF_KcGPRS (4F52)`, `AID A00000015141434C00/4F09`
- exposed as `path` in the change-details payload and in the files entries
(the timeline rows keep the short name — they have no room)
- the colour legend (changed / newly observed / never observed) moved below
the two hex views
- tests: `_file_path` cases (MF, ADF, DF-qualified EF, AID with and without an
EF.DIR label, FID-as-name) plus the payload field, and the Node modal test
asserts the path header and that the legend follows the views
- v2.4.2; SW cache simtrace-analyser-v68
- the modal's hex views carried a 16-byte ASCII column per row; file data is
rarely text and the column halved the room for the two before/after views,
so it is gone: `hexImageRows` no longer builds an `ascii` field,
`hexImageHtml` no longer emits it and the `.hex-ascii` CSS rule is removed
- tests: the hex-row test asserts the field is gone (so it cannot come back
silently); README/README_RUS wording updated
- SW cache simtrace-analyser-v67 (cosmetic, no version bump)
- the timeline's Δ glyph became a **Details** button, and it now appears on
Update rows too (not just card-side changes); EN "Changed (card)" ->
"Changed on card", RU "Изменено (картой)" -> "Изменено картой"
- the details modal shows the affected unit — the whole file, or the single
record that changed — **before and after** the change as hex views (16 bytes
per row, ASCII column, never-observed bytes as `--`), with the bytes that
changed and the bytes the operation *first observed* highlighted in two
different colours, plus the changed/new byte counts and the APDU reference;
the old before/after byte table is gone
- `GET /api/sessions/<id>/files/image?key=…&change=…` returns those two views
(segments with offsets) plus the change's metadata; the images are
reconstructed from the current image by undoing the later changes
(`FileTracker.change_images`), so nothing is duplicated per event — every
value change is an event, hence the values are exact (a byte only first
observed later can appear as known in both views)
- tracker: change events carry `observed_ranges` (bytes that were unknown
before the operation) next to `ranges`; an update that only observes new
bytes now records an event (so it can be inspected); record-file ranges are
in-record byte offsets (the record number stays in its own field), which the
previous "record number as position" could not express for a hex highlight
- tests: change-images reconstruction (before/after per event, later changes
undone, records isolated, observation-only events, unknown gaps), the image
endpoint (200/404/400), and Node tests for the hex-image helper (highlight
classes, unknown gaps, ASCII, row limit) and the modal rendering
- v2.4.0; SW cache simtrace-analyser-v65
- new `simtrace_analyser/filetrack.py`: a single-pass tracker over a
session's decoded messages, keyed by (DF, FID) and trace-wide (resets only
wipe the per-channel selection): counters (`select`/`read`/`update`,
successful operations only), a sparse image of the observed bytes
(transparent files by offset, record files per record number) and the
change log
- changes: **device** = an UPDATE seen in the trace (applied to the image,
`no_change` flags a provable no-op); **card** = a READ whose bytes differ
from the image on bytes already known (logged at the surfacing read with
the changed ranges and the before/after hex; never-seen bytes just fill
the image, they are an observation, not a change)
- SFI-addressed accesses join the same entry as path/FID ones: the SFI ↔ FID
relations come from the normative tables and from every surfaced FCP `88`
DO (including the spec's "SFI = 5 LSBs of the FID" rule when the DO is
absent); an SFI used before its relation is learned stays provisional and
merges into the resolved entry
- the DF identity follows TS 102 221 §8.4/Table 8.2: `selected_df_fid()` now
resolves an EF path selection to its parent DF and returns `aid:<hex>` for
applications selected by an unknown AID (named from EF.DIR when the trace
surfaced it); `Database._decode_rows` gained an `on_message` hook and
`track_files()`
- `GET /api/sessions/<id>/files`: the operation timeline, the per-file
counters/coverage/change log, the SFI relations and the AID labels
- PWA: the side panel's Map/Stats tabs became a view selector (Map / Stats /
Files); the Files view is a timeline of Read / Update / Changed (card) rows
(colour-coded, the same file names as the Map view), a row click jumps to
the APDU (loading the pages it needs) and a card-change row's Δ opens the
changed ranges with the previous and the new bytes; EN/RU
- bounds: the image freezes past 64 KB of observed bytes per file, the change
log keeps the last 1000 events per file (counters keep counting)
- tests: tests/test_filetrack.py (counters, resets, channels, image/diff
semantics, record files, SFI resolution and the provisional merge, EF.DIR
labels, caps, the endpoint) and Node tests for the Files-view helpers and
wiring
- v2.3.0; MIN_SERVER_VERSION 2.3.0; SW cache simtrace-analyser-v63
- new `simtrace_analyser/comprion.py`: parses the tab-separated ISO Protocol
Layer export (metadata block, ATR/PPS exchanges, APDUs with continuation
rows, delays in etu) and re-encodes every exchange into the wire-shaped
TPDUs the analyser stores — a case-4 exchange becomes the command, a
fabricated `61 <len>` status word and a GET RESPONSE (command's logical
channel preserved), so command/response pairing, FCP/file decoding and the
GET RESPONSE filters work unchanged; Cold reset / Power off become RST/VCC
line events, deliberately without the sigrok CLK extension (the export
carries no clock measurement)
- optional matching **ISO Application Layer** export as the exact time base:
its one `Time:` row per exchange (ms precision) replaces the delay estimate
(0-based elapsed, `started = Session Name + first Time`), guarded by a
session-identity check (`Session Name`, falling back to `Working
directory`; the two layers' export timestamps differ and are not compared)
and an exchange-count check — mismatched files are refused, never mixed
- protocol-only imports keep the Delay-based timeline: card/line activity
time (host pauses absent), the unit assuming 3.8 MHz (`CLK_HZ`); the
estimate is not fed into the CLK/RST line-event annotation
- `POST /api/sessions/import?format=comprion` (raw text body, or a JSON
`{text, times_text}` bundle) with stable error codes (app_layer /
not_comprion / unsupported_layout / no_data / times_unparsable /
session_mismatch / time_base_unverified / time_base_mismatch); a COMPRION
file dropped on the PCAP path answers with a pointer (comprion_export);
session mode `comprion`, window from the Session Name
- PWA: import dialog with two separate pickers (Protocol Layer required,
Application Layer optional) whose hint switches between "activity time"
and "exact timestamps"; errors stay in the dialog with both selections
until fixed or cancelled (Cancel/Esc, also disabled while importing);
EN/RU
- tests: tests/test_comprion.py (parser, wire encoding, time base + guards,
import endpoint) and frontend/tests/comprion.test.js (file checks, slot
validation, dialog state machine with a DOM stub, RU coverage)
- v2.2.0; MIN_SERVER_VERSION 2.2.0; SW cache simtrace-analyser-v61
- terminal power supply (tag 80): voltage class per Table 6.1, maximum
current in mA, clock in 0.1 MHz steps ('FF' = not indicated)
- extended logical channels (tag 81) — zero length per §11.1.19.2.2, a
nonzero length is preserved as raw
- additional interfaces (tag 82 b1 = UICC-CLF)
- eUICC device capabilities: tag 83 per GSMA SGP.22 v2.6 §3.4.2 Table 7
(LUId/LPDd/LDSd/LUIe-SCWS); tag 84 preserved raw (no pinned SGP.32 spec)
- unknown/private TLVs preserved via raw_tlv
- concise APDU-list summary; decoded rows in the PWA detail view
- v2.1.0; SW cache simtrace-analyser-v55
- tests: 6 real samples from the local trace sessions (49/55/58/61/206/208)
- move the analysis server out of simtrace2_pysniff into simtrace_analyser;
simtrace2_pysniff stays as the simtrace2-sniff replacement capture utility
- new server CLI: simtrace-analyser-server; python -m simtrace_analyser
(simtrace2-pysniff-server and python -m simtrace2_pysniff.server dropped)
- simtrace2_pysniff lazy-imports hardware symbols, so the server no longer
pulls PyUSB at startup; the GSMTAP/PCAP codecs work without libusb
- version 2.0.0 everywhere (was 1.22.21); PWA SW cache simtrace-analyser-v54
- frontend: server name, clone URL and i18n strings updated; vendored-parser
reference otaman -> SIMple
- README/README_RUS reworked product-first (analyser first, utility second)
- DB dir ~/.simtrace-analyser, /api status payload and hosted PWA unchanged
- More precise than 'ATR STARTS' (the ATR starts after the de-assert
delay, not at the edge) while staying human-friendly; reads as a pair
with 'RESET ASSERTED'
- Update tests and both READMEs (backend-only, no version bump)
- capture.py: map GSMTAP PPS request/response (0x02/0x03) to 'pps',
matching the pcap-import mapping (was decoded as TPDU live)
- decode.py: parse the optional RST CLK extension (sigrok v1.9.0+,
payload flag bit 0); ATR gains clk_hz/data_rate/etu_us derived from
TA1 Fi/Di (data_rate = clk_hz × D / F)
- database.py: CLK state is consumed by the next ATR (no stale rate
for an ATR without preceding RST); a late-arriving RST measurement
is backfilled onto the ATR it belongs to; replay context carries
the CLK across pagination/live polling
- PWA: ATR detail shows CLK/Data rate/ETU; RST event detail shows CLK
- tests: PPS mapping, CLK parse with/without flag, rate math,
DB consume + backfill round-trips
- README/RUS updated
- Bump 1.22.18, SW cache v49->v50