scopekit
scopekit is a Python library for creating high-performance, interactive oscilloscope-style plots for large time-series datasets using matplotlib.
It is designed to handle millions of data points smoothly by automatically decimating data for the current view. When zoomed out, it displays a performance-optimized "envelope" view, and when zoomed in, it seamlessly transitions to show detailed raw data.
Installation
To install the package from your local repository, run:
pip install .
Quickstart Example
Here is a simple example of how to create a plot with scopekit.
import numpy as np
from scopekit import OscilloscopePlot
# 1. Generate some sample data
fs = 1e7 # 10 MHz sampling rate
duration = 0.1 # 0.1 seconds of data
t = np.arange(0, duration, 1/fs)
# A 50 Hz signal with some high-frequency noise
x = np.sin(2 * np.pi * 50 * t) + np.sin(2 * np.pi * 1e3 * t) * 0.2
# 2. Create an OscilloscopePlot instance
# The plot automatically handles decimation and display modes.
plot = OscilloscopePlot(t, x, name="Sample Waveform")
# 3. Render the plot
# This creates the matplotlib figure and axes.
plot.render()
# 4. Show the plot
# The plot is now interactive: zoom with the mouse and use the toolbar.
plot.show()
API Reference
The primary interface for the library is the OscilloscopePlot class.
OscilloscopePlot
__init__
def __init__(
self,
t: Union[np.ndarray, List[np.ndarray]],
x: Union[np.ndarray, List[np.ndarray]],
name: Union[str, List[str]] = "Waveform",
trace_colors: Optional[List[str]] = None,
max_plot_points: int = 10000,
mode_switch_threshold: float = 10e-3,
min_y_range: Optional[float] = None,
y_margin_fraction: float = 0.15,
signal_line_width: float = 1.0,
signal_alpha: float = 0.75,
envelope_alpha: float = 0.75,
region_alpha: float = 0.4,
region_zorder: int = -5,
envelope_window_samples: Optional[int] = None,
):
Methods
def add_line(
self,
t: Union[np.ndarray, List[np.ndarray]],
data: Union[np.ndarray, List[np.ndarray]],
label: str = "Line",
color: Optional[str] = None,
alpha: float = 0.75,
linestyle: str = "-",
linewidth: float = 1.0,
display_mode: int = MODE_BOTH,
trace_idx: int = 0,
zorder: int = 5,
) -> None:
def add_ribbon(
self,
t: Union[np.ndarray, List[np.ndarray]],
center_data: Union[np.ndarray, List[np.ndarray]],
width: Union[float, np.ndarray],
label: str = "Ribbon",
color: str = "gray",
alpha: float = 0.6,
display_mode: int = MODE_DETAIL,
trace_idx: int = 0,
zorder: int = 2,
) -> None:
def add_envelope(
self,
min_data: Union[np.ndarray, List[np.ndarray]],
max_data: Union[np.ndarray, List[np.ndarray]],
label: str = "Envelope",
color: Optional[str] = None,
alpha: float = 0.4,
display_mode: int = MODE_ENVELOPE,
trace_idx: int = 0,
zorder: int = 1,
) -> None:
def add_regions(
self,
regions: np.ndarray,
label: str = "Regions",
color: str = "crimson",
alpha: float = 0.4,
display_mode: int = MODE_BOTH,
trace_idx: int = 0,
zorder: int = -5,
) -> None:
def render(self) -> None:
def show(self) -> None:
def save(self, filepath: str) -> None:
def home(self) -> None:
def refresh(self) -> None:
