279 lines
8.7 KiB
Markdown
279 lines
8.7 KiB
Markdown
# 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_<timestamp>.bin` — structured binary log with timestamps
|
|
and direction tags (record format: `[type:1][ts_us:8][len:4][payload]`)
|
|
- `s3_session_<timestamp>.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_<ts>.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\<you>\.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
|
|
```
|