Skip to content
This repository was archived by the owner on Jul 27, 2026. It is now read-only.
 
 

Latest commit

 

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

logo ScopeBinReader

A universal reader, analyzer and viewer for oscilloscope .bin files — the SPBXDS container format used by OWON, Hanmatek, AKIP and other scopes built on the same OEM platform.

Build Status Latest release Nuget version

This project is a fork of RobThree/OwonBinfileReader, the original C# library for OWON scope binfiles (still used here as-is, and still published under its original name on NuGet). It grew into ScopeBinReader after discovering that the SPBXDS container is shared across vendors, but the metadata schema inside it is not. The .NET library remains the parsing core (Python package name: scopebin); a Python layer adds multi-vendor support, analysis tools and a GUI on top of it. Much of the format knowledge here is reverse engineered — don't use it to build a nuclear power plant or launch people into space!


Download

Just want to open a .bin file? Grab the latest Windows build — no Python, no .NET, no setup:

➡️ Download the latest release — download scopebin-view.exe, double-click it, File → Open .bin…. That's it.

(Building from source, the CLI, and the C# library are documented below for developers.)


Highlights

  • 🧩 One container, many vendors. A single schema-agnostic parser reads the SPBXDS container; small pluggable profiles interpret each vendor's JSON metadata and calibrate raw samples into real volts and seconds.
  • 🔎 Auto-detection. The right profile is chosen automatically from the file's IDN/MODEL and the shape of its JSON.
  • 🖥️ Oscilloscope-style GUI. Dark theme, per-channel colors, draggable time & voltage cursors with live ΔT / 1÷ΔT / ΔV readouts, zoom & pan.
  • 🛠️ CLI for inspect, csv, plot, overlay and fft.
  • 📦 Single-file Windows .exe — bundles the .NET bridge, so it runs with nothing installed.
  • 🧪 Cross-validated: 27 C# tests + 6 Python tests, covering both vendor schemas.

Repository layout

.
├── dotnet/                    # Everything C#
│   ├── OwonBinfileReader/     #   Core library (NuGet) — container + OWON schema
│   │   └── Raw/               #     Schema-agnostic RawContainerReader (the parsing core)
│   ├── OwonBinfileReader.Tests/
│   ├── BinfileDump/           #   Tiny CLI: dumps a .bin into raw parts (the Py↔C# bridge)
│   └── TestApp/               #   Upstream console demo (CSV export)
├── python/                    # Everything Python
│   ├── scopebin/              #   The scopebin package
│   │   ├── bridge.py          #     Invokes BinfileDump, loads segments into numpy
│   │   ├── profiles/          #     akip.py · owon.py · generic.py (+ auto-detect)
│   │   ├── api.py · model.py  #     read() → normalized Waveform
│   │   ├── cli.py · gui.py    #     command line + Tk/matplotlib viewer
│   │   └── units.py
│   ├── app.py                 #   PyInstaller entry point (the viewer)
│   └── tests/
├── samples/                   # Example capture (150-1.bin, an AKIP-4122/7 pulse)
├── scripts/                   # build_windows_exe.ps1
└── README.md

Quick start

The viewer (Windows .exe)

Download the latest release (or build it yourself — see Building the Windows exe) and just run it — no Python, no .NET required. File → Open .bin…, or drop a path on the command line:

scopebin-view.exe samples\150-1.bin
  • Drag the vertical (time) and horizontal (voltage) cursors to measure; the side panel shows the values, ΔT, frequency 1÷ΔT, ΔV and each channel's value at both cursors.
  • Scroll to zoom the time axis, Shift+Scroll to zoom voltage, or use the matplotlib toolbar (box-zoom / pan / home).
  • Toggle channels with the checkboxes.

From source (Python)

conda create -n scopebin python=3.11 numpy pandas matplotlib pytest
conda activate scopebin
pip install -e python

scopebin view samples/150-1.bin          # GUI
scopebin inspect samples/150-1.bin       # metadata + channel summary
scopebin csv     samples/150-1.bin -o out.csv
scopebin plot    samples/150-1.bin -o out.png
scopebin overlay a.bin b.bin c.bin       # several captures on one time axis
scopebin fft     samples/150-1.bin       # magnitude spectrum of a channel

In development mode the Python side shells out to the BinfileDump .NET tool (building it on first use), so a .NET 8 SDK is needed then. The packaged .exe bundles it instead.

import scopebin

wf = scopebin.read("samples/150-1.bin")
print(wf.vendor, wf.model, wf.idn)       # -> akip 320202103 AKIP,AKIP-4122/7,...
ch = wf.channels[0]
ch.t, ch.v                               # numpy arrays: time (s), value (V)

The C# library (NuGet)

The .NET core is still usable standalone for OWON/Hanmatek files:

using OwonBinfileReader;

var bf = await new BinfileReader().ReadAsync(@"/path/to/file.bin");
// bf.MetaData          -> scope settings
// bf.Measurements      -> ReadOnlyDictionary<int, double[]> per displayed channel

For files whose JSON schema BinfileReader doesn't understand, use the vendor-agnostic container reader and interpret the metadata yourself:

using OwonBinfileReader.Raw;

var raw = await new RawContainerReader().ReadAsync(@"/path/to/file.bin");
// raw.RawJson          -> the metadata JSON, untouched
// raw.Segments         -> one raw Int16[] per data segment
// raw.Tail             -> optional trailing INFO block

How it works

          ┌──────────────────────────────────────────────────────────────┐
          │  C# core  (single source of truth for the binary container)   │
          │  RawContainerReader → BinfileDump CLI:                        │
          │    magic · JSON header · N int16 segments · INFO tail          │
          └───────────────────────────────┬──────────────────────────────┘
                         raw JSON + raw int16 segments  (subprocess bridge)
          ┌───────────────────────────────▼──────────────────────────────┐
          │  Python  (scopebin)                                           │
          │  profile auto-detect → calibrate → normalized Waveform        │
          │    akip · owon · generic          (t in seconds, v in volts)  │
          │  → CLI · GUI · CSV · FFT                                       │
          └──────────────────────────────────────────────────────────────┘

The container is the same everywhere; the calibration differs per vendor, which is why the raw-parse and the interpretation are split:

Vendor JSON channel key Sample → value formula
OWON CHANNEL (DISPLAY, Current_Ratio, Current_Rate) value = raw₁₆ × (Current_Ratio ÷ Current_Rate)
AKIP channel (Display_Switch, Reference_Zero, Voltage_Rate) value = (raw₁₆ ÷ 16 − Reference_Zero) × Voltage_Rate
unknown (heuristic) reuses whichever known fields it finds, else raw uncalibrated codes

Adding a vendor profile

Drop a module in python/scopebin/profiles/ exposing two functions and register it:

NAME = "myvendor"

def matches(meta: dict) -> bool:
    ...   # True if this profile recognizes the JSON schema

def build_channels(meta: dict, segments: list[np.ndarray]) -> list[ChannelWaveform]:
    ...   # calibrate raw int16 segments into t (s) / v (V)

Add it to _VENDOR_PROFILES in profiles/__init__.py. generic.py is always the last-resort fallback, so an unknown file still opens (uncalibrated if need be) rather than failing.


File format

The .bin file is a binary container with this structure:

Offset Bytes Value Meaning
0 6 SPBXDS Magic header
6 4 JSON length Int32 length of the following JSON metadata
10 Json Length JSON data Metadata about the capture (schema varies per vendor)
10 + Json Length 4 Data Length Int32 length of the data segment (bytes)
14 + Json Length Data length Data Int16 little-endian samples, Data Length ÷ 2 of them

The Data Length + Data block repeats for each captured channel. An optional segment may follow the data (End Of Data):

Offset Bytes Value Meaning
EOD 4 INFO Additional information marker
EOD + 4 8 Unknown Two Int32 values
EOD + 12 19 Date ISO8601 date (YYYY-MM-DD HH:MM:SS)
EOD + 31 11 Unknown Usually zeroes

RawContainerReader captures this whole trailing block verbatim as Tail.

JSON metadata — the vendor variation

OWON / Hanmatek uses uppercase keys and per-channel Current_Ratio/Current_Rate:

{
  "TIMEBASE": { "SCALE": "200ms", "HOFFSET": 120 },
  "SAMPLE":   { "DATALEN": 1520, "SAMPLERATE": "(2.5MS/s)", "DEPMEM": "10M" },
  "CHANNEL": [
    { "NAME": "CH1", "DISPLAY": "ON", "COUPLING": "DC", "PROBE": "10X",
      "SCALE": "50.0mV", "OFFSET": -201, "Current_Rate": 10000, "Current_Ratio": 0.78125 }
  ],
  "IDN": "OWON,XDS3104AE,2308149,V4.0.0", "MODEL": "310401101"
}

AKIP uses a lowercase channel array with Reference_Zero + Voltage_Rate, and may keep data for channels whose Display_Switch is OFF:

{
  "MODEL": "320202103", "IDN": "AKIP,AKIP-4122/7,24230571,V7.6.0",
  "channel": [
    { "Index": "CH1", "Display_Switch": "OFF", "Sample_Rate": "(2.5MS/s)",
      "Hscale": "200us", "Vscale": "500mV", "Reference_Zero": "-29",
      "Adc_Data_Time": "0.400000us", "Voltage_Rate": "1.250000mv", "Data_Length": "10000" }
  ]
}

AKIP samples are an 8-bit ADC code shifted left by 4 bits (i.e. stored int16 values are multiples of 16), which is why the AKIP formula divides by 16 before applying the zero reference. Note also that only the active channel tends to be written to the file, even if several were on screen — worth knowing if you expected two traces and got one.

Normalization

The Python layer always returns SI base units: seconds for time and volts for value. The C# BinfileReader similarly normalizes to base units (mVV, µss, …) and parses what it can into enums / bools / timespans, represented as double.


Building the Windows exe

From an environment that has scopebin and pyinstaller installed, and with a .NET 8 SDK available:

conda activate scopebin
pip install pyinstaller
scripts\build_windows_exe.ps1

This does two things:

  1. Publishes the bridge (BinfileDump) as a self-contained, single-file win-x64 executable — the .NET runtime is embedded, so no .NET install is needed to run.
  2. Runs PyInstaller to bundle the Python viewer + that bridge into one file: dist/scopebin-view.exe.

Useful switches:

Switch Effect
(default) one file, with console (startup errors visible)
-OneDir one folder in dist/scopebin-view/ — faster startup, easier to inspect
-Windowed no console window (nicer for double-click, hides stdout)
-Python <path> pick a specific interpreter (e.g. the env's python.exe)

The one-file build is ~215 MB because it embeds the .NET runtime, numpy, matplotlib and Tk. -OneDir produces the same total size split across a folder and starts faster.

Sanity-check a fresh build without touching your PATH:

dist\scopebin-view.exe --self-test samples\150-1.bin out.png

Development

# C# — 27 tests
dotnet test dotnet/OwonBinfileReader.sln

# Python — 6 tests (rebuilds the bridge on first run; needs the .NET 8 SDK)
python -m pytest python/tests

Tested oscilloscopes

The OWON schema has been verified with a Hanmatek DSO1102 (a rebranded OWON SDS1102), an OWON XDS3104AE, and various .bin files found online. The AKIP profile was built and validated against an AKIP-4122/7 (firmware V7.6.0). Other scopes on the same platform are likely to work — if not, a new profile is only a few lines.

Attribution

Original C# library by RobThree. The logo is composed of:

License

MIT — see LICENSE.

About

Universal reader, analyzer & GUI viewer for oscilloscope .bin files (SPBXDS container: OWON, Hanmatek, AKIP, and others) — C# core + Python CLI/GUI, single-file Windows exe

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages