A unified CLI tool for controlling oscilloscopes from the terminal.
Abstracts manufacturer-specific differences that VISA/SCPI alone does not cover, providing a unified command set across supported instruments. All commands produce structured, machine-readable output — suitable for scripting and AI-assisted workflows.
| Manufacturer | Series |
|---|---|
| Rigol | DHO800, DHO900 |
Adding support for a new manufacturer requires implementing one Python class. See Contributing.
- Python 3.10 or newer
- Oscilloscope connected via USB or LAN
USB instruments require the kernel usbtmc driver (included in all mainstream distributions) and read/write access to /dev/usbtmc*. If you get a permission error, create a udev rule:
sudo tee /etc/udev/rules.d/60-usbtmc.rules << 'EOF' SUBSYSTEM=="usb", ATTRS{idVendor}=="1ab1", MODE="0664", GROUP="plugdev" KERNEL=="usbtmc*", ATTRS{idVendor}=="1ab1", MODE="0664", GROUP="plugdev" EOF sudo udevadm control --reload-rules && sudo udevadm trigger
Replace 1ab1 with your instrument's USB vendor ID if not Rigol. Your user must be in the plugdev group (groups to check).
LAN connections work out of the box. For USB, Windows has no built-in USBTMC driver — install one of the following:
| Option | Notes |
|---|---|
| NI-VISA | Recommended. Free, installs USB drivers automatically. |
| libusb + Zadig | No extra software, but requires manually replacing the USB driver for each instrument. |
NI-VISA is automatically preferred over pyvisa-py when installed — no configuration needed.
sh install.sh
powershell -ExecutionPolicy Bypass -File install.ps1
The install script will:
- Check that Python 3.10+ is available and print instructions if not
- Create a local virtual environment (
venv/) - Install the package and its dependencies
- Linux/macOS: create an
omniscopesymlink in~/.local/bin/
If Python is not installed:
- macOS:
brew install python— Homebrew - Linux:
sudo apt install python3/sudo dnf install python3 - Windows: python.org/downloads — check Add Python to PATH during setup, or run
winget install Python.Python.3.12
rm ~/.local/bin/omniscope
rm -rf venv/Remove-Item -Force "$env:USERPROFILE\.local\bin\omniscope.bat" Remove-Item -Recurse -Force venv
| Platform | Command |
|---|---|
| Linux / macOS | omniscope <subcommand> [options] |
| Windows | omniscope <subcommand> [options] |
The examples below use omniscope for brevity.
# Get labels for all channels omniscope get-label # Get label for channel 1 only omniscope get-label --ch1 # Set labels omniscope set-label --ch1 SDA --ch2 SCL
# Get visibility of all channels omniscope get-visible # Show channel 1, hide channel 2 omniscope set-visible --ch1 on --ch2 off
# Get current timebase omniscope get-x-axis # Set scale to 1 ms/div, offset to 0 omniscope set-x-axis --scale 0.001 --offset 0
# Get vertical settings for channels 1 and 2 omniscope get-y-axis --ch1 --ch2 # Set channel 1 to 1 V/div omniscope set-y-axis --ch1 --scale 1.0 # Set channels 1 and 2 together omniscope set-y-axis --ch1 --ch2 --scale 0.5 --offset 0
# Set edge trigger on CH1 omniscope set-trigger --source CH1 --level 1.0 --slope rising --sweep normal # Read current trigger settings omniscope get-trigger
Use canonical source names in the CLI (CH1..CH4, EXT). Vendor-specific forms are handled internally.
# Save a screenshot to the current directory omniscope save-image # Save screenshot to a specific directory omniscope save-image -o ./captures # Save screenshot to a specific filename omniscope save-image -o ./captures/shot.png # Save waveform CSV for channels 1 and 2 omniscope save-csv --ch1 --ch2 # Save screenshot + CSV for all currently visible channels omniscope save # Save screenshot + CSV for specific channels omniscope save --ch1 --ch2 -o ./captures
Output files are timestamped: 20240315_123456.png, 20240315_123456_ch1.csv.
All get-* commands support --json for structured output:
omniscope get-label --json
{
"ch1": "SDA",
"ch2": "SCL",
"ch3": "CH3",
"ch4": "CH4"
}omniscope get-x-axis --json
{
"scale": 0.001,
"offset": 0.0
}- Create
omniscope/drivers/<manufacturer>.pyand implementOscilloscopeBase(defined inomniscope/base.py). - Register the driver in
omniscope/drivers/registry.pyby adding an entry to_DRIVER_MAP:
(r"KEYSIGHT", KeysightOscilloscope),
The auto-detect logic queries *IDN? and matches the response against these patterns.
omniscope/
base.py # abstract interface — defines all supported operations
cli.py # subcommand definitions and argument parsing
drivers/
registry.py # auto-detection via *IDN?
rigol.py # Rigol implementation
MIT