diff --git a/README.md b/README.md index e69de29..3b52ec4 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,278 @@ +# seismo-relay + +Tools for capturing and reverse-engineering the RS-232 serial protocol between +**Blastware** software and **Instantel MiniMate Plus** seismographs. + +Built for Windows, stdlib-only (plus `pyserial` for the bridge). + +--- + +## What's in here + +``` +seismo-relay/ +├── bridges/ +│ ├── s3-bridge/ +│ │ └── s3_bridge.py ← The serial bridge (core capture tool) +│ ├── gui_bridge.py ← Tkinter GUI wrapper for s3_bridge +│ └── raw_capture.py ← Simpler raw-only capture tool +└── parsers/ + ├── s3_parser.py ← Low-level DLE frame extractor + ├── s3_analyzer.py ← Protocol analyzer (sessions, diffs, exports) + ├── gui_analyzer.py ← Tkinter GUI for the analyzer + └── frame_db.py ← SQLite frame database +``` + +--- + +## How it all fits together + +The workflow has two phases: **capture**, then **analyze**. + +``` + Blastware PC + │ + Virtual COM (e.g. COM4) + │ + s3_bridge.py ←─── sits in the middle, forwards all bytes both ways + │ writes raw_bw.bin and raw_s3.bin + Physical COM (e.g. COM5) + │ + MiniMate Plus seismograph +``` + +After capturing, you point the analyzer at the two `.bin` files to inspect +what happened. + +--- + +## Part 1 — The Bridge + +### `s3_bridge.py` — Serial bridge + +Transparently forwards bytes between Blastware and the seismograph while +logging everything to disk. Blastware operates normally and has no idea the +bridge is there. + +**Run it:** +``` +python bridges/s3-bridge/s3_bridge.py --bw COM4 --s3 COM5 --logdir captures/ +``` + +**Key flags:** +| Flag | Default | Description | +|------|---------|-------------| +| `--bw` | required | COM port connected to Blastware | +| `--s3` | required | COM port connected to the seismograph | +| `--baud` | 38400 | Baud rate (match your device) | +| `--logdir` | `.` | Where to write log/bin files | +| `--raw-bw` | off | Also write a flat raw file for BW→S3 traffic | +| `--raw-s3` | off | Also write a flat raw file for S3→BW traffic | + +**Output files (in `--logdir`):** +- `s3_session_.bin` — structured binary log with timestamps + and direction tags (record format: `[type:1][ts_us:8][len:4][payload]`) +- `s3_session_.log` — human-readable hex dump (text) +- `raw_bw.bin` — flat BW→S3 byte stream (if `--raw-bw` used) +- `raw_s3.bin` — flat S3→BW byte stream (if `--raw-s3` used) + +> The analyzer needs `raw_bw.bin` + `raw_s3.bin`. Always use `--raw-bw` and +> `--raw-s3` when capturing. + +**Interactive commands** (type while bridge is running): +- `m` + Enter → prompts for a label and inserts a MARK record into the log +- `q` + Enter → quit + +--- + +### `gui_bridge.py` — Bridge GUI + +A simple point-and-click wrapper around `s3_bridge.py`. Easier than the +command line if you don't want to type flags every time. + +``` +python bridges/gui_bridge.py +``` + +Set your COM ports, log directory, and tick the raw tap checkboxes before +hitting **Start**. The **Add Mark** button lets you annotate the capture +at any point (e.g. "changed record time to 13s"). + +--- + +## Part 2 — The Analyzer + +After capturing, you have `raw_bw.bin` (bytes Blastware sent) and `raw_s3.bin` +(bytes the seismograph replied with). The analyzer parses these into protocol +frames, groups them into sessions, and helps you figure out what each byte means. + +### What's a "session"? + +Each time you open the settings dialog in Blastware and click Apply/OK, that's +one session — a complete read/modify/write cycle. The bridge detects session +boundaries by watching for the final write-confirm packet (SUB `0x74`). + +Each session contains a sequence of request/response frame pairs: +- Blastware sends a **request** (BW→S3): "give me your config block" +- The seismograph sends a **response** (S3→BW): here it is +- At the end, Blastware sends the modified settings back in a series of write packets + +The analyzer lines these up and diffs consecutive sessions to show you exactly +which bytes changed. + +--- + +### `gui_analyzer.py` — Analyzer GUI + +``` +python parsers/gui_analyzer.py +``` + +This is the main tool. It has five tabs: + +#### Toolbar +- **S3 raw / BW raw** — browse to your `raw_s3.bin` and `raw_bw.bin` files +- **Analyze** — parse and load the captures +- **Live: OFF/ON** — watch the files grow in real time while the bridge is running +- **Export for Claude** — generate a self-contained `.md` report for AI-assisted analysis + +#### Inventory tab +Shows all frames in the selected session — direction, SUB command, page, +length, and checksum status. Click any frame in the left tree to drill in. + +#### Hex Dump tab +Full hex dump of the selected frame's payload. If the frame had changed bytes +vs the previous session, those are listed below the dump with before/after values +and field names where known. + +#### Diff tab +Side-by-side byte-level diff between the current session and the previous one. +Only SUBs (command types) that actually changed are shown. + +#### Full Report tab +Raw text version of the session report — useful for copying into notes. + +#### Query DB tab +Search across all your captured sessions using the built-in database. + +--- + +### `s3_analyzer.py` — Analyzer (command line) + +If you prefer the terminal: + +``` +python parsers/s3_analyzer.py --s3 raw_s3.bin --bw raw_bw.bin +``` + +**Flags:** +| Flag | Description | +|------|-------------| +| `--s3` | Path to raw_s3.bin | +| `--bw` | Path to raw_bw.bin | +| `--live` | Tail files in real time (poll mode) | +| `--export` | Also write a `claude_export_.md` file | +| `--outdir` | Where to write `.report` files (default: same folder as input) | +| `--poll` | Live mode poll interval in seconds (default: 0.05) | + +Writes one `.report` file per session and prints a summary to the console. + +--- + +## The Frame Database + +Every time you click **Analyze**, the frames are automatically saved to a +SQLite database at: + +``` +C:\Users\\.seismo_lab\frames.db +``` + +This accumulates captures over time so you can query across sessions and dates. + +### Query DB tab + +Use the filter bar to search: +- **Capture** — narrow to a specific capture (timestamp shown) +- **Dir** — BW (requests) or S3 (responses) only +- **SUB** — filter by command type (e.g. `0xF7` = EVENT_INDEX_RESPONSE) +- **Offset** — filter to frames that have a specific byte offset +- **Value** — combined with Offset: "show frames where byte 85 = 0x0A" + +Click any result row, then use the **Byte interpretation** panel at the bottom +to see what that offset's bytes look like as uint8, int8, uint16 BE/LE, +uint32 BE/LE, and float32 BE/LE simultaneously. + +This is the main tool for mapping unknown fields — if you change one setting in +Blastware, capture before and after, then query for frames where that offset +moved, you can pin down exactly which byte controls what. + +--- + +## Export for Claude + +The **Export for Claude** button (orange, in the toolbar) generates a single +`.md` file containing: + +1. Protocol background and known field map +2. Capture summary (session count, frame counts, what changed) +3. Per-diff tables — before/after bytes for every changed offset, with field + names where known +4. Full hex dumps of all frames in the baseline session + +Paste this file into a Claude conversation to get help mapping unknown fields, +interpreting data structures, or understanding sequences. + +--- + +## Protocol quick-reference + +| Term | Value | Meaning | +|------|-------|---------| +| DLE | `0x10` | Data Link Escape | +| STX | `0x02` | Start of frame | +| ETX | `0x03` | End of frame | +| ACK | `0x41` | Frame start marker (BW side) | +| DLE stuffing | `10 10` on wire | Literal `0x10` in payload | + +**S3-side frame** (seismograph → Blastware): `DLE STX [payload] DLE ETX` +**BW-side frame** (Blastware → seismograph): `ACK STX [payload] ETX` + +**De-stuffed payload header** (first 5 bytes after de-stuffing): +``` +[0] CMD 0x10 = BW request, 0x00 = S3 response +[1] ? 0x00 (BW) or 0x10 (S3) +[2] SUB Command/response identifier ← the key field +[3] OFFSET_HI Page address high byte +[4] OFFSET_LO Page address low byte +[5+] DATA Payload content +``` + +**Response SUB rule:** `response_SUB = 0xFF - request_SUB` +Example: request SUB `0x08` → response SUB `0xF7` + +--- + +## Requirements + +``` +pip install pyserial +``` + +Python 3.10+. Everything else is stdlib (Tkinter, sqlite3, struct, hashlib). + +Tkinter is included with the standard Python installer on Windows. If it's +missing, reinstall Python and make sure "tcl/tk and IDLE" is checked. + +--- + +## Virtual COM ports + +The bridge needs two COM ports on the same PC — one that Blastware connects to, +and one wired to the actual seismograph. On Windows, use a virtual COM port pair +(e.g. **com0com** or **VSPD**) to give Blastware a port to talk to while the +bridge sits in the middle. + +``` +Blastware → COM4 (virtual) ↔ s3_bridge ↔ COM5 (physical) → MiniMate +```