Real-Time Simultaneous Dual-Channel Oscilloscope & Spectrum Analyzer
A high-performance, dark-mode real-time Dual-Channel Oscilloscope and Spectrum Analyzer software stack running natively on PYNQ Linux platforms.
Features simultaneous dual-channel continuous analog acquisition on Arduino header pins A0 (Vaux1) and A1 (Vaux9), selectable hardware edge triggering (CH1 / A0 vs CH2 / A1), FPGA-accelerated 2048-point FFT & CORDIC magnitude extraction in Programmable Logic, dual AXI DMA streaming direct to DDR memory at ~30 FPS, and non-blocking concurrent dual-channel analog waveform generation with the Digilent Analog Discovery 3 (AD3) via pydwf.
🏛 System Architecture
This repository adopts the canonical PYNQ Custom Overlay pattern (OscilloscopeOverlay). It automatically pulls its compiled hardware bitstream and metadata from GitHub Releases (or loads local custom .bit builds) and encapsulates the Dual DMA receivers, AXI-Lite trigger registers, sequencer controls, and dual-wavegen into a unified Python object.
[ Analog Discovery 3 ] ──(W1: Yellow)──────> [ PYNQ-Z2 Pin A0 (Vaux1) ]
[ Wavegen ] ──(W2: Yellow/White)─> [ PYNQ-Z2 Pin A1 (Vaux9) ]
│ │
(pydwf SDK) (XADC Dual Continuous Sequencer)
│ │ (1 MSPS Interleaved Stream)
▼ ▼
[ AD3SignalGenerator ] [ axis_trigger_unit IP ]
(Concurrent W1 & W2) (Selectable Trigger Source: A0/A1)
│
[ tlast_generator (2048 pts) ]
│
[ axis_broadcaster ]
┌──────────────┴──────────────┐
▼ (Time Stream) ▼ (Signed Stream)
[ AXI DMA 0 (Time) ] [ xfft (2048-pt BFP) ]
│ │
│ [ CORDIC (Magnitude) ]
│ │
│ [ AXI DMA 1 (FFT) ]
│ │
└──────────────┬──────────────┘
▼
[ OscilloscopeOverlay ]
├── .trigger (HardwareTrigger AXI-Lite)
├── .xadc (StreamingXADC DMA Driver)
├── .fft (StreamingFFT PL DMA Driver)
├── .wavegen (AD3SignalGenerator Dual-DAC)
└── .dashboard() (Interactive 4-Tab Instrument)
🖥 Interactive 4-Tab Dashboard UI Guide
Tab 1 — 📈 Dual Oscilloscope (A0 & A1)
Displays synchronized real-time time-domain traces for Channel 1 (A0, Cyan) and Channel 2 (A1, Magenta). The Trigger Threshold Line (Orange Dashed) automatically relocates to whichever channel is selected as the active trigger source.
Trigger source set to CH2 (A1) — the trigger threshold line dynamically moves to the bottom A1 subplot:
Tab 2 — 📊 Dual Spectrum Analyzer (FFTs of A0 & A1)
Computes and renders high-speed frequency spectra for both channels simultaneously ($0 - 250,\text{kHz}$) with automated fundamental peak frequency ($f_0$) tracking.
Tab 3 & 4 — 🔀 Dedicated Channel Views
Stacked multi-domain displays showing Time Domain (top) and Frequency Domain (bottom) for each channel individually:
| Channel 1: A0 (Time + Spectrum) | Channel 2: A1 (Time + Spectrum) |
|---|---|
⚙️ Control Panel Reference
-
Action Row (Row 1):
▶ Start Live: Launches non-blocking dual DMA stream and AD3 dual wavegen.■ Stop: Cleanly halts hardware loops, disarms triggers, and releases AD3 handles.⚡ Force / Arm: Forces capture in Auto/Normal mode or arms single-shot capture.Auto-Range(Toggle): Dynamically scales horizontal timebase (5–10 signal periods) according to the selected trigger channel's frequency.Live Metric Bar: Real-time $V_{pp}$ and peak fundamental frequency ($f_0$) for both A0 and A1.
-
Trigger Controls (Row 2):
Trig Mode:Auto(continuous with 50 ms fallback),Normal(strictly edge-gated),Single(transient capture).Trig Edge:Rising/Fallingedge slope detection.Trig Source: Selects active hardware trigger channel:CH1 (A0)orCH2 (A1).Trig Level: Sets the analog threshold register ($0.0,\text{V} - 3.3,\text{V}$).
-
Dual Wavegen Controls (Rows 3 & 4):
CH1 (A0)&CH2 (A1): Independent waveform shapes (Sine,Triangle,Square), amplitudes ($0.1,\text{V} - 1.5,\text{V}$), and frequencies ($50,\text{Hz} - 100,\text{kHz}$).
-
FFT Controls (Row 5):
FFT Unit: Select logarithmic power (dBV,dBFS) or linear amplitude (Linear).Span / Zoom: Zooms frequency axis (Full 250 kHz,100 kHz,20 kHz).
🔌 Hardware Setup & Wiring
| AD3 Wire | Wire Color | PYNQ-Z2 Analog Pin | Signal Description |
|---|---|---|---|
| Wavegen 1 (W1) | Solid Yellow | Header J1 Pin A0 (Pin 6 - Bottom) |
Channel 1 Analog Input (Vaux1) |
| Wavegen 2 (W2) | Yellow / White Stripe | Header J1 Pin A1 (Pin 5 - 2nd from Bottom) |
Channel 2 Analog Input (Vaux9) |
| GND | Solid Black | PYNQ-Z2 GND | Common Analog Reference |
Note: Connect the AD3 USB cable to the large rectangular USB HOST port on the PYNQ-Z2 board. Use an external 5V auxiliary power supply for the AD3 to ensure voltage rail stability under dual-channel generation.
🚀 Quick Start & Installation
1. Install Package from PyPI
pip install --upgrade pynq-oscilloscope
2. Copy Example Notebooks to Jupyter Workspace
pynq-oscilloscope-get-notebooks
3. Install Digilent AD3 Drivers (One-Time Setup)
from pynq_oscilloscope import install_ad3_drivers
install_ad3_drivers()
💻 Python API Usage
Launch Interactive Dual Dashboard
from pynq_oscilloscope import check_usb_permissions, OscilloscopeOverlay
check_usb_permissions()
# Automatically loads v1.3.0-rc1 dual-channel overlay and programs FPGA
ol = OscilloscopeOverlay()
# Launch interactive 4-tab Plotly + IPywidgets dashboard
app = ol.dashboard()
Programmatic Dual-Channel Capture
from pynq_oscilloscope import OscilloscopeOverlay
ol = OscilloscopeOverlay()
# Configure Trigger: Trigger on Channel 2 (A1) Rising Edge @ 1.65V
ol.trigger.configure(mode="Auto", edge="Rising", source="CH2", threshold_volts=1.65)
# Synchronously capture both channels (1024 samples per channel @ 500 kSPS)
v_a0, v_a1 = ol.capture_stereo()
print(f"Captured A0 Vpp: {v_a0.max()-v_a0.min():.2f} V | A1 Vpp: {v_a1.max()-v_a1.min():.2f} V")
ol.close()
📓 Notebook Suite
| Notebook | Description | Key Modules Used |
|---|---|---|
01_ad3_getting_started.ipynb |
Verifies Digilent drivers and generates analog waveforms in background worker. | AD3SignalGenerator, check_usb_permissions |
02_xadc_getting_started.ipynb |
Single-channel hardware triggering and DMA capture on A0. | OscilloscopeOverlay, HardwareTrigger |
03_oscilloscope_dashboard.ipynb |
Main Application: Deploys the complete interactive 4-Tab Dual-Channel Dashboard. | OscilloscopeOverlay |
04_fft_spectrum_analyzer.ipynb |
Spectrum Analyzer Guide: Captures PL hardware FFT spectra, analyzes harmonics. | OscilloscopeOverlay, StreamingFFT |
⚠️ Release Notes & Disclaimer
Pre-Release Disclaimer (
v1.3.0-rc1):
- Sampling Rate: The dual-channel continuous sequencer operates at an aggregate sampling rate of $1.0,\text{MSPS}$ ($500,\text{kSPS}$ per channel).
- Hardware Phase-Lock: The hardware trigger unit guarantees that DMA packets strictly start aligned to Channel 1 (A0) even when triggering off Channel 2 (A1).
- Known Considerations: While fully validated for standard laboratory wave generation and cross-channel triggering up to $100,\text{kHz}$, fine adjustments for extreme frequency ratios (e.g. $>10:1$ differences between A0 and A1) or high-noise edge environments may be further tuned in future revisions.
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
Metadata
Release files for pynq-oscilloscope 1.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pynq_oscilloscope-1.3.0.tar.gz | 29.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pynq_oscilloscope-1.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 61.8 kB
Release files / pynq_oscilloscope-1.3.0.tar.gz
| Download URL | pynq_oscilloscope-1.3.0.tar.gz |
|---|---|
| Size | 29.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
87d286187324ed5189336c0e9a996040b4a0d719638a94c41b7ea28e1cb2b272
|
|
BLAKE2b-256 checksum How to use checksums |
81c36bae73a239df61c51602b99cf4e092ee9ca8a2a4e2ccaf22052d7aae2432
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 17, 2026.
Transparency logRelease files / pynq_oscilloscope-1.3.0-py3-none-any.whl
| Download URL | pynq_oscilloscope-1.3.0-py3-none-any.whl |
|---|---|
| Size | 32.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6cbaa1054f53030a7cf1e8ada40ad6ce8502c374c90d467adc453897a8b85523
|
|
BLAKE2b-256 checksum How to use checksums |
596bbcb655c3550c05ffe71ef3e61cab0ecdce1fb0eccecac958007f04fdf089
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 17, 2026.
Transparency log