Follow-ups from the capture-reliability review:
- All server-side capture log lines are now prefixed with a local
timestamp (YYYY-MM-DD HH:MM:SS) for easier correlation.
- New --log-interval SECONDS CLI flag (default 60) tunes the
"Capture alive" heartbeat interval; passed through to CaptureManager.
Stays on 1.22.0 (backend-only). 369 tests pass.
The GSMTAP capture thread died silently whenever a single non-SIM or
truncated GSMTAP datagram arrived (read_packet() raises ValueError,
uncaught in the loop, killing the daemon thread while UDP kept
flowing) — matching the "stops listening after a sniffer restart"
symptom.
Fixes:
- GsmtapListener.iter_messages() now catches ValueError/struct.error
from read_packet(), drops the offending datagram (counted via a new
`dropped` counter) and keeps listening.
- CaptureManager._capture_loop() guards the DB insert so a transient
error can't kill the thread; a top-level guard logs
"ERROR: capture thread stopped on error …" + traceback and clears
the session so /api/status no longer reports a stuck "active".
- Server now logs capture liveness to stderr: "Capture started", a
periodic "Capture alive: session=N messages=M bytes=B dropped=D"
heartbeat (default every 60 s, configurable via log_interval), and
"Capture stopped" (or "empty session discarded").
Tests: +5 (listener survives non-SIM/short packet, capture loop
survives insert error, logs stop-on-error, heartbeat+stop log).
Stays on 1.22.0 (backend-only).
sigrok-iso7816-stream sets GSMTAP_FLAG_BAD_FCS (0x01) in the GSMTAP
header `res` byte on desynced TPDUs (mis-framed exchanges kept for
forensics). Propagate that flag through the whole pipeline so such
packets are explicitly surfaced instead of decoding as garbage:
- gsmtap.py: GSMTAP_FLAG_BAD_FCS const; GsmtapReceiver.read_packet()
now returns (sub_type, data, flags) from the res byte.
- server/capture.py: GsmtapListener forwards the flags to the DB.
- pcap.py: _parse_gsmtap_hdr/_scan_gsmtap/extract_gsmtap and
parse_pcap/parse_pcapng now yield a (ts, type, payload, flags)
tuple, so the flag survives a PCAP/PCAPNG round-trip.
- server/server.py: PCAP import stores the flags.
- server/decode.py: gsmtap_flag_names() maps bit 0x01 to
'bad FCS (desynced)'; merged into decoded errors for tpdu/atr/pps
and rst/vcc, so the frontend ⚠ badge + Errors row flag the entry.
User-visible: desynced TPDUs show a ⚠ "bad FCS (desynced)" marker
in the trace and detail views. Tests: +4 (decode BAD_FCS markers,
PCAP flag round-trip); existing tests updated for the new tuple
arities. Version 1.22.0, SW cache simtrace-analyser-v32.
Support the sigrok-iso7816-stream decoder (FX2 logic-analyzer passive
SIM sniffer, github.com/anttro/sigrok_iso7816_stream) which streams
GSMTAP-SIM on UDP 4729 and adds two custom sub-types for line events.
- gsmtap.py: GSMTAP_SIM_RST_EVENT (0x10) / GSMTAP_SIM_VCC_EVENT (0x11).
- capture.py: gsmtap_msg_type() maps 0x10→'rst', 0x11→'vcc' (shared by
the listener and tests); pcap.py _SUB_TYPE_NAMES gains the same
entries so PCAP re-imports of these captures preserve the events.
- decode.py: decode_line_event() decodes [direction, level, reserved]
into reset asserted/de-asserted or power applied/removed + level;
routed through decode_sniff_msg.
- database.py: reset-assert and power-removal invalidate the per-channel
file selection (card-level transition); de-assert / power-on are
markers only.
- frontend: RST/VCC labels (RESET ASSERTED / VCC OFF, …), a detail-view
branch (Event + Level + raw), and list styling (.apdu-type.rst/.vcc).
README notes the custom sub-type compatibility. Tests: +8 (line-event
vectors, sniff routing, subtype + PCAP round-trip mappings) — 359
total. Version 1.21.0, SW cache simtrace-analyser-v31.
Frontend:
- trace empty-state now shows connection status + capture mode (boxed,
colored) in the center, updated live via checkConnection
- capture-mode uses amber warning style (gsmtap/disabled), green for direct
- stopCapture clears currentSessionId
Server:
- gsmtap read_packet catches OSError on socket close (no more EBADF
traceback on stop)
- stop_session joins the loop and discards a session with zero messages;
/api/capture/stop reports discarded
- startup prints server version
Tests: 122 (gsmtap close, empty/non-empty capture lifecycle).
Add simtrace2-pysniff-server CLI — HTTP API for simtrace-analyser PWA.
Two capture modes: GSMTAP UDP listener (compatible with simtrace2-sniff)
or direct SIMtrace2 hardware capture. SQLite storage with sessions +
messages tables. REST API with CORS.
server/ sub-package: database.py, capture.py, server.py, __main__.py
README updated with server usage section.
pyproject.toml: simtrace2-pysniff-server script entry added.