Skip to content

Repository files navigation

scope_control

Control and digitize bench oscilloscopes. Targets Rigol SCPI scopes (DS1000Z / DS2000 / MSO5000 / DHO800/900 and similar) and is built to manage several scopes at once over LAN.

Two ways to use it:

  1. Multi-scope web dashboard (scope-dashboard) -- view and fully control every connected scope from a browser: live waveform plot, live screen mirror, per-channel / timebase / trigger / acquisition controls, run/stop/single, and a raw SCPI console for everything else.
  2. Python library + CLI (scope-control) -- scripted capture to CSV/NPZ, ScopeFarm for parallel reads, one-off deep-memory grabs.

Layout

scope_control/
  scope_control/        package
    webdash.py          FastAPI multi-scope dashboard server  (scope-dashboard)
    scpi_tcp.py         ScpiScope: fast raw-socket SCPI client (dashboard backend)
    web/dashboard.html  the dashboard UI (tabs + plot/screen + controls + console)
    rigol.py            RigolScope: pyvisa control + waveform readout (CLI/USB)
    manager.py          ScopeFarm: many scopes by name, parallel reads
    discover.py         list / identify VISA resources, find Rigol(s)
    cli.py              scope-control  (list/info/capture/farm)
  examples/             quick_capture.py, farm_capture.py
  scopes.example.json   copy to scopes.json, fill in your scope IPs
  pyproject.toml        pip-installable; defines the console scripts
  requirements.txt

Install

# dedicated venv (kept separate from the SLM/torch venv)
py -m venv .venv
.\.venv\Scripts\python -m pip install -e .

pip install -e . installs the deps and creates the scope-dashboard and scope-control commands. The dashboard itself needs only numpy + fastapi

  • uvicorn; pyvisa/pyvisa-py (for the CLI / USB) install with -e ".[visa]".

Web dashboard

# one scope, quick:
scope-dashboard --host-scope 169.254.242.0
# all scopes from a config:
scope-dashboard --config scopes.json

Access (default --access tailscale, no password): binds 0.0.0.0 but accepts requests ONLY from loopback + Tailscale (100.64.0.0/10 and the Tailscale IPv6 ULA); any other source IP gets 403. So it's viewable from any machine on your tailnet with no password, and unreachable from the plain LAN. Other modes: --access local (loopback only) or --access all (unrestricted — only behind a trusted network).

Then open http://127.0.0.1:8600/ locally, or http://<this-host-tailscale-ip>:8600/ from another tailnet machine. Each configured scope is a tab; the selected scope shows:

  • Plot (fast native waveform, all displayed channels) or Screen (the scope's actual display, PNG, ~1 Hz) -- toggle top-right.
  • Per-channel Vpp / mean / Vrms tiles + live trigger status.
  • Controls: Run / Stop / Single / Auto / Clear; per-channel on-off, V/div, coupling, offset; timebase; trigger (sweep / source / slope / level); acquisition (type / averages / mem depth).
  • SCPI console: send any command from the programming guide (e.g. :MEASure:ITEM? FREQuency,CHANnel1, :MATH:DISPlay ON). Queries (with ?) return a value. This covers the full long tail (math, FFT, cursors, measurements, save/recall, decode, utility).

It is read+control over Ethernet only -- no instrument driver to install. The Rigol accepts multiple simultaneous connections, so the dashboard coexists with other clients.

scopes.json (copy from scopes.example.json) maps a name to each scope's IP:

{ "trap_pd": "192.168.1.51", "awg_mon": "192.168.1.52" }

Moving to another computer (e.g. exp-control)

The package is self-contained and config-driven -- nothing is hard-coded to this machine. To run the dashboard on a different PC:

  1. Put the scopes on a routable network. The current setup uses a direct link-local cable (169.254.x.x), which is reachable only from the PC it's plugged into. For multi-PC / multi-scope use, put the scopes on a switch with static IPs (Utility -> IO -> LAN) and list those IPs in scopes.json.
  2. Copy the package (this folder) to the other machine, or pip install a built wheel (python -m build -> pip install scope_control-*.whl).
  3. python -m venv .venv; .\.venv\Scripts\python -m pip install -e .
  4. scope-dashboard --config scopes.json (add --host 0.0.0.0 to view it from yet another machine's browser).

Because addressing is by IP in scopes.json, the same dashboard runs from the rearrangement PC, the exp-control PC, or anywhere on the network / Tailscale -- just point it at the scopes.

Library / CLI use

from scope_control import RigolScope            # pyvisa-based (also USB)
with RigolScope("TCPIP0::192.168.1.51::INSTR") as scope:
    wf = scope.read_waveform(channel=1)         # NORM: fast on-screen trace
    wf.to_csv("ch1.csv")
    deep = scope.read_waveform(channel=1, mode="RAW")   # full memory (auto-stops + chunks)

from scope_control import ScpiScope             # fast raw-socket (dashboard backend)
sc = ScpiScope("192.168.1.51")
print(sc.read_state(channels=(1, 2))["trigger"])
sc.set_trigger(sweep="NORMal", source="CHANnel1", level=0.5)
scope-control --backend '@py' list
scope-control capture --resource TCPIP0::192.168.1.51::INSTR --channel 1 --out ch1.csv
scope-control farm --config scopes.json --channel 1 --outdir captures

Ethernet vs USB (with ~6 scopes: use Ethernet)

Ethernet (LAN) USB (USBTMC)
Scaling to 6 scopes One switch, talk to all concurrently 6 ports/hubs; shared host controller
Addressing Stable static IP per scope Resource string keys off serial; order can shuffle
Cable length ~100 m ~5 m
Driver on Windows None NI-VISA / USBTMC driver, hub flakiness
Remote access Yes (e.g. Tailscale) Local only

Notes / gotchas

  • Frame rate vs sample rate: the dashboard refresh (a few Hz) is how often a frame is pulled; within each frame the scope samples at its full rate (up to 1 GSa/s). Set the scope's timebase to frame the signal. It is not a gap-free recorder -- for that use a one-off RAW deep-memory capture via the library.
  • NORM vs RAW (library): NORM = ~1200 on-screen points (fast). RAW = full memory, scope must stop; read in <=250k-pt chunks automatically.
  • Scaling: volts = (code - yorigin - yreference) * yincrement, t = (i - xreference) * xincrement + xorigin.
  • Screenshot: the dashboard uses PNG (:DISPlay:DATA? ON,0,PNG, ~46 KB); BMP24 also works (~1.1 MB) if a model lacks PNG.
  • For exact SCPI on your model, see its Rigol Programming Guide PDF.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages