diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d682caf..1e116b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,9 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies - run: uv sync --all-extras --dev + run: | + uv sync --all-extras --dev + uv pip install -e . - name: Lint with Ruff run: | diff --git a/.gitignore b/.gitignore index b8f5c8d..842561b 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,5 @@ env/ .idea/ *.ipynb_checkpoints + +*.egg-info/ diff --git a/README.md b/README.md index 7b53905..55debce 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,51 @@ This project consists of four interconnected subsystems, each one enabling the n --- +## 🚀 Getting Started + +This project uses **uv** for dependency management. + +### 1. Clone & Enter + +```bash +git clone https://github.com/JacksonFergusonDev/systems-audio-lab.git +cd systems-audio-lab +``` + +### 2. Install Environment +We use an editable install so changes to the `sysaudio` library are immediately reflected in the notebooks. + +```bash +# Initialize virtual environment +uv venv + +# Activate environment +source .venv/bin/activate # Mac/Linux +# .venv\Scripts\activate # Windows + +# Install dependencies and the local package +uv pip install -e . +``` + +### 3. Usage Options +Option A: Interactive Analysis (Jupyter) Launch the lab to view the engineering reports and signal processing pipelines. + +```bash +jupyter lab +``` + +Option B: Headless Tools (CLI Scripts) You can run the capture and visualization tools directly from the command line. + +```bash +# Example: Launch the real-time oscilloscope visualization +python oscilloscope-rp2040/scripts/visualization/live_scope.py + +# Example: Record a single burst of data +python oscilloscope-rp2040/scripts/capture/record.py +``` + +--- + ## 📂 Repository Structure ```text @@ -83,7 +128,7 @@ This project consists of four interconnected subsystems, each one enabling the n │ │ ├── 02_instrument_analysis.ipynb # Harmonic Analysis 🟢 │ │ ├── 03_transfer_acquisition.ipynb # Sine Sweep Generation 🟢 │ │ └── 04_transfer_analysis.ipynb # Deconvolution (In Progress) 🟡 -│ ├── src/ # Analysis Library (FFT, Plotting, Signal Processing) +│ ├── sysaudio/ # Analysis Library (FFT, Plotting, Signal Processing) │ └── schematics/ # Signal Conditioning Circuit Design ├── red-llama-build/ # Guitar Overdrive Test Circuit │ └── procurement/ # Bills of Materials diff --git a/oscilloscope-rp2040/README.md b/oscilloscope-rp2040/README.md index 8a50f5e..7fa2bdd 100644 --- a/oscilloscope-rp2040/README.md +++ b/oscilloscope-rp2040/README.md @@ -32,28 +32,50 @@ The MicroPython logic running on the MCU. * **Engine:** Uses `@micropython.native` emitters for a jitter-free polling loop. * **Protocol:** Implements a binary "Handshake" protocol (`s` for Science Burst, `v` for Video) to manage USB serial backpressure. -### [`src/`](./src) +### [`sysaudio/`](./sysaudio) The Python library backing the analysis pipeline. -* **`src.daq`**: Hardware abstraction for USB UART stream processing. -* **`src.audio`**: Host-side signal generation (Sine, Square, Log Sweeps) via `sounddevice`. -* **`src.dsp`**: Signal processing (FFT, THD, Triggering). -* **`src.analysis`**: Publication-ready plotting and reporting engines. +* **Core & Hardware** + * `sysaudio.daq`: Hardware abstraction for USB UART stream processing. + * `sysaudio.calibration`: Routines for robust sample rate ($F_s$) verification. + * `sysaudio.audio`: Host-side signal generation (Sine, Square, Log Sweeps) via `sounddevice`. +* **Experimentation** + * `sysaudio.experiments`: High-level automation for Notebooks (Linearity, Bandwidth, THD captures). + * `sysaudio.io`: Standardized file handling for burst (`.npz`) and continuous signal data. +* **Analysis & Viz** + * `sysaudio.dsp`: Signal processing primitives (FFT, THD calculation, Triggering). + * `sysaudio.plots`: Publication-ready plotting wrappers (Bode, Transfer Curves, THD Fingerprints). + * `sysaudio.viz`: Real-time rendering engines for the live oscilloscope. ### [`scripts/`](./scripts) User-facing tools for interaction. * **`capture/`**: Headless recording tools. - * `record.py`: High-fidelity bursts. - * `measure_transfer.py`: Automated stimulus-response testing (Sweep/Steady). + * `record.py`: High-fidelity burst capture. + * `master_transfer.py`: Automated stimulus-response testing (Sweep/Steady). + * `stream.py`: Continuous data streaming utility. * **`signal/`**: Interactive function generators. * `play_wave.py`: Infinite waveform generator with live scope. * `play_sweep.py`: Logarithmic sine sweep generator. -* **`visualization/`**: Real-time monitoring (`live_scope.py`) and rendering (`render_scope_video.py`). +* **`visualization/`**: Monitoring and rendering. + * `live_scope.py`: Real-time oscilloscope with FFT. + * `playback_scope.py`: Offline replay of captured signal files. + * `joyplot.py`: Ridge-line plot generation for spectral waterfalls. + * `render_scope_video.py`: Exports scope data to video frames. +* **`fun/`**: Creative coding experiments. + * `neon_torus.py`: XY oscilloscope visualization. + * `render_landscape.py`: 3D terrain generation from audio data. Used to generate the title graphic for [systems_audio_tech_report.pdf](../docs/systems_audio_tech_report.pdf) ### [`notebooks/`](./notebooks) The scientific workbench. -* **`01_acquisition.ipynb`**: Hardware calibration and raw data inspection. -* **`02_analysis.ipynb`**: Empirical characterization of the Red Llama Overdrive (Harmonics & Topology). -* **`03_transfer_function.ipynb`**: System identification (Linearity & Frequency Response). +* **`01_instrument_acquisition.ipynb`**: Hardware calibration and raw data inspection. +* **`02_instrument_analysis.ipynb`**: Empirical characterization of the Red Llama Overdrive (Harmonics & Topology). +* **`03_transfer_acquisition.ipynb`**: Automated capture of transfer function datasets. + * *Set A:* Linearity (Triangle Wave). + * *Set B:* Bandwidth (Log Sine Sweep). + * *Set C:* Standard THD (1kHz Sine). +* **`04_transfer_analysis.ipynb`**: System Identification (In Progress). + * *Bode Response:* Magnitude and Phase analysis. + * *Transfer Curves:* Input vs. Output linearity mapping. + * *THD Fingerprinting:* Harmonic distortion profiling. ## Hardware Requirements **Total Cost:** ~$4.14 USD @@ -78,26 +100,28 @@ The scientific workbench. Flash the RP2040 with MicroPython (v1.27+) and copy `firmware/main.py` to the root of the device. ### 2. Host Environment -Install dependencies (requires Python 3.10+): +Dependencies are managed at the **project root**. Run the following from the top-level `systems-audio-lab` directory: ```bash -uv sync +# Install dependencies and link the sysaudio package +uv pip install -e . ``` ### 3. Execution +Run scripts from the project root to ensure proper package resolution. Real-Time Monitoring: ```bash -python scripts/visualization/live_scope.py +python oscilloscope-rp2040/scripts/visualization/live_scope.py ``` Function Generator (Signal + Scope): ```bash -python scripts/signal/play_wave.py +python oscilloscope-rp2040/scripts/signal/play_wave.py ``` Automated Transfer Function Capture: ```bash # Capture a 5-second log sweep for Bode plotting -python scripts/capture/measure_transfer.py sweep --duration 5 --amp 0.5 +python oscilloscope-rp2040/scripts/capture/master_transfer.py sweep --duration 5 --amp 0.5 ``` diff --git a/oscilloscope-rp2040/notebooks/01_instrument_acquisition.ipynb.ipynb b/oscilloscope-rp2040/notebooks/01_instrument_acquisition.ipynb.ipynb index 83e01cc..c01d139 100644 --- a/oscilloscope-rp2040/notebooks/01_instrument_acquisition.ipynb.ipynb +++ b/oscilloscope-rp2040/notebooks/01_instrument_acquisition.ipynb.ipynb @@ -39,11 +39,7 @@ ], "source": [ "# Cell 1: Setup\n", - "import sys\n", - "\n", - "sys.path.append(\"..\")\n", - "\n", - "from src import calibration, experiments\n", + "from sysaudio import calibration, experiments\n", "\n", "# Verify Sampling Rate\n", "FS = calibration.load_calibration()\n", diff --git a/oscilloscope-rp2040/notebooks/02_instrument_analysis.ipynb b/oscilloscope-rp2040/notebooks/02_instrument_analysis.ipynb index f7020e1..ccb5814 100644 --- a/oscilloscope-rp2040/notebooks/02_instrument_analysis.ipynb +++ b/oscilloscope-rp2040/notebooks/02_instrument_analysis.ipynb @@ -28,7 +28,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "4ea87ab1", "metadata": {}, "outputs": [ @@ -41,14 +41,13 @@ } ], "source": [ - "# ruff: noqa: E402\n", "import os\n", - "import sys\n", "\n", "import numpy as np\n", "import seaborn as sns\n", + "from sysaudio import config, dsp, io, plots\n", "\n", - "# 1. Define Paths\n", + "# Define Paths\n", "# Current: .../oscilloscope-rp2040/notebooks\n", "current_dir = os.getcwd()\n", "project_root = os.path.abspath(os.path.join(current_dir, \"..\"))\n", @@ -60,13 +59,7 @@ "# Ensure it exists\n", "os.makedirs(FIGURES_DIR, exist_ok=True)\n", "\n", - "# 2. Setup System Path\n", - "sys.path.append(project_root)\n", - "\n", - "# 3. Import Libraries\n", - "from src import config, dsp, io, plots\n", - "\n", - "# 4. Set Styling\n", + "# Set Styling\n", "sns.set_style(\"darkgrid\")\n", "print(f\"📂 Figures will be saved to: {os.path.relpath(FIGURES_DIR, repo_root)}\")" ] diff --git a/oscilloscope-rp2040/notebooks/03_transfer_acquisition.ipynb b/oscilloscope-rp2040/notebooks/03_transfer_acquisition.ipynb index a8d5659..a1b6d62 100644 --- a/oscilloscope-rp2040/notebooks/03_transfer_acquisition.ipynb +++ b/oscilloscope-rp2040/notebooks/03_transfer_acquisition.ipynb @@ -18,12 +18,8 @@ "source": [ "# Cell 1: Imports & Setup\n", "import os\n", - "import sys\n", "\n", - "# Add project root to path\n", - "sys.path.append(\"..\")\n", - "\n", - "from src import calibration, experiments\n", + "from sysaudio import calibration, experiments\n", "\n", "# Verify Sampling Rate\n", "FS = calibration.load_calibration()\n", diff --git a/oscilloscope-rp2040/notebooks/04_transfer_analysis.ipynb b/oscilloscope-rp2040/notebooks/04_transfer_analysis.ipynb index c44e153..386d281 100644 --- a/oscilloscope-rp2040/notebooks/04_transfer_analysis.ipynb +++ b/oscilloscope-rp2040/notebooks/04_transfer_analysis.ipynb @@ -2,25 +2,19 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "62d1e8ef", "metadata": {}, "outputs": [], "source": [ - "# ruff: noqa: E402\n", "import os\n", - "import sys\n", "from pathlib import Path\n", "\n", - "# 1. Define Paths\n", + "from sysaudio import io, plots\n", + "\n", + "# Define Paths\n", "current_dir = os.getcwd()\n", "project_root = os.path.abspath(os.path.join(current_dir, \"..\"))\n", - "sys.path.append(project_root)\n", - "\n", - "# 2. Import Libraries\n", - "from src import io, plots\n", - "\n", - "# 3. Define File Paths\n", "DATA = Path(\"..\") / \"data\"\n", "\n", "FILE_SWEEP_SRC = DATA / \"continuous\" / \"src_sweep_20260125_124443.npz\"\n", diff --git a/oscilloscope-rp2040/scripts/analysis/spectrum.py b/oscilloscope-rp2040/scripts/analysis/spectrum.py index 449851c..1daf76e 100644 --- a/oscilloscope-rp2040/scripts/analysis/spectrum.py +++ b/oscilloscope-rp2040/scripts/analysis/spectrum.py @@ -6,12 +6,8 @@ """ import os -import sys -# Ensure src is in path for imports -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) - -from src import config, io, viz +from sysaudio import config, io, viz def main() -> None: diff --git a/oscilloscope-rp2040/scripts/capture/master_transfer.py b/oscilloscope-rp2040/scripts/capture/master_transfer.py index 7bc531c..ca942ca 100644 --- a/oscilloscope-rp2040/scripts/capture/master_transfer.py +++ b/oscilloscope-rp2040/scripts/capture/master_transfer.py @@ -6,13 +6,7 @@ It delegates the actual acquisition logic to the `experiments` module. """ -import os -import sys - -# Ensure src is in path for imports -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) - -from src import experiments # noqa: E402 +from sysaudio import experiments # Configuration # Options: 'sweep' (Log Sine Sweep) or 'steady' (Single Frequency Burst) diff --git a/oscilloscope-rp2040/scripts/capture/record.py b/oscilloscope-rp2040/scripts/capture/record.py index 0783701..a048e13 100644 --- a/oscilloscope-rp2040/scripts/capture/record.py +++ b/oscilloscope-rp2040/scripts/capture/record.py @@ -6,13 +6,7 @@ the result to the burst data directory. """ -import os -import sys - -# Add project root to path -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) - -from src import config, daq, dsp, io # noqa: E402 +from sysaudio import config, daq, dsp, io def main() -> None: diff --git a/oscilloscope-rp2040/scripts/capture/stream.py b/oscilloscope-rp2040/scripts/capture/stream.py index 9ed0dd3..0ebd99c 100644 --- a/oscilloscope-rp2040/scripts/capture/stream.py +++ b/oscilloscope-rp2040/scripts/capture/stream.py @@ -6,13 +6,7 @@ logging or monitoring sessions. """ -import os -import sys - -# Ensure src is in path for imports -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) - -from src import experiments # noqa: E402 +from sysaudio import experiments def main() -> None: diff --git a/oscilloscope-rp2040/scripts/fun/neon_torus.py b/oscilloscope-rp2040/scripts/fun/neon_torus.py index 28116a9..f39ea6b 100644 --- a/oscilloscope-rp2040/scripts/fun/neon_torus.py +++ b/oscilloscope-rp2040/scripts/fun/neon_torus.py @@ -6,17 +6,11 @@ using time-delay embedding. """ -import os -import sys import time import numpy as np import sounddevice as sd - -# Ensure src is in path for imports -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) - -from src import audio, daq, dsp, plots # noqa: E402 +from sysaudio import audio, daq, dsp, plots # Configuration FREQ_1: float = 55.0 diff --git a/oscilloscope-rp2040/scripts/fun/record_drone.py b/oscilloscope-rp2040/scripts/fun/record_drone.py index 20ca2e2..fc2f37d 100644 --- a/oscilloscope-rp2040/scripts/fun/record_drone.py +++ b/oscilloscope-rp2040/scripts/fun/record_drone.py @@ -7,18 +7,12 @@ visualizations. """ -import os -import sys import time from typing import List import numpy as np import sounddevice as sd - -# Ensure src is in path for imports -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) - -from src import audio, config, daq, dsp, io # noqa: E402 +from sysaudio import audio, config, daq, dsp, io # Configuration FREQ: float = 55.0 diff --git a/oscilloscope-rp2040/scripts/fun/render_landscape.py b/oscilloscope-rp2040/scripts/fun/render_landscape.py index 366dd5d..2776959 100644 --- a/oscilloscope-rp2040/scripts/fun/render_landscape.py +++ b/oscilloscope-rp2040/scripts/fun/render_landscape.py @@ -6,13 +6,7 @@ `plots.plot_spectral_landscape` function. """ -import os -import sys - -# Ensure src is in path for imports -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) - -from src import config, dsp, io, plots # noqa: E402 +from sysaudio import config, dsp, io, plots def main() -> None: diff --git a/oscilloscope-rp2040/scripts/signal/play_sweep.py b/oscilloscope-rp2040/scripts/signal/play_sweep.py index 4e5039f..1da5f47 100644 --- a/oscilloscope-rp2040/scripts/signal/play_sweep.py +++ b/oscilloscope-rp2040/scripts/signal/play_sweep.py @@ -7,15 +7,8 @@ setup before committing to a recording. """ -import os -import sys - import sounddevice as sd - -# Add project root to path -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) - -from src import audio, daq, viz # noqa: E402 +from sysaudio import audio, daq, viz # Configuration F_START: float = 20.0 # Start Frequency (Hz) diff --git a/oscilloscope-rp2040/scripts/signal/play_wave.py b/oscilloscope-rp2040/scripts/signal/play_wave.py index aea8329..9f94ed0 100644 --- a/oscilloscope-rp2040/scripts/signal/play_wave.py +++ b/oscilloscope-rp2040/scripts/signal/play_wave.py @@ -6,13 +6,7 @@ the DAQ to a live oscilloscope window. """ -import os -import sys - -# Add project root to path -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) - -from src import audio, daq, viz # noqa: E402 +from sysaudio import audio, daq, viz # Configuration SHAPE: str = "sine" # Options: "sine", "triangle", "square", "saw" diff --git a/oscilloscope-rp2040/scripts/visualization/joyplot.py b/oscilloscope-rp2040/scripts/visualization/joyplot.py index d9aa7fe..a9ecdb4 100644 --- a/oscilloscope-rp2040/scripts/visualization/joyplot.py +++ b/oscilloscope-rp2040/scripts/visualization/joyplot.py @@ -6,13 +6,8 @@ """ import argparse -import os -import sys -# Add project root -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) - -from src import io, plots # noqa: E402 +from sysaudio import io, plots def main() -> None: diff --git a/oscilloscope-rp2040/scripts/visualization/live_scope.py b/oscilloscope-rp2040/scripts/visualization/live_scope.py index 173220d..fcc5226 100644 --- a/oscilloscope-rp2040/scripts/visualization/live_scope.py +++ b/oscilloscope-rp2040/scripts/visualization/live_scope.py @@ -6,17 +6,11 @@ It serves as the foundational example for real-time plotting in this project. """ -import os -import sys import time from typing import Any, cast import matplotlib.pyplot as plt - -# Add project root to path -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) - -from src import daq, dsp, viz # noqa: E402 +from sysaudio import daq, dsp, viz def main() -> None: diff --git a/oscilloscope-rp2040/scripts/visualization/playback_scope.py b/oscilloscope-rp2040/scripts/visualization/playback_scope.py index 05d639c..29c410a 100644 --- a/oscilloscope-rp2040/scripts/visualization/playback_scope.py +++ b/oscilloscope-rp2040/scripts/visualization/playback_scope.py @@ -7,14 +7,9 @@ """ import os -import sys import numpy as np - -# Add project root to path -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) - -from src import config, dsp, io, viz # noqa: E402 +from sysaudio import config, dsp, io, viz def main() -> None: diff --git a/oscilloscope-rp2040/scripts/visualization/render_scope_video.py b/oscilloscope-rp2040/scripts/visualization/render_scope_video.py index 48aac9e..6622d88 100644 --- a/oscilloscope-rp2040/scripts/visualization/render_scope_video.py +++ b/oscilloscope-rp2040/scripts/visualization/render_scope_video.py @@ -7,13 +7,9 @@ """ import os -import sys from typing import Any, Dict -# Add project root to path -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) - -from src import config, io, render # noqa: E402 +from sysaudio import config, io, render # Configuration for video output VIDEO_SETTINGS: Dict[str, Any] = { diff --git a/oscilloscope-rp2040/src/README.md b/oscilloscope-rp2040/sysaudio/README.md similarity index 99% rename from oscilloscope-rp2040/src/README.md rename to oscilloscope-rp2040/sysaudio/README.md index 89f01ad..4eb2fe4 100644 --- a/oscilloscope-rp2040/src/README.md +++ b/oscilloscope-rp2040/sysaudio/README.md @@ -65,4 +65,4 @@ from src import daq, dsp, config with daq.DAQInterface() as device: data = device.capture_burst() - volts = dsp.raw_to_volts(data) \ No newline at end of file + volts = dsp.raw_to_volts(data) diff --git a/oscilloscope-rp2040/src/__init__.py b/oscilloscope-rp2040/sysaudio/__init__.py similarity index 100% rename from oscilloscope-rp2040/src/__init__.py rename to oscilloscope-rp2040/sysaudio/__init__.py diff --git a/oscilloscope-rp2040/src/audio.py b/oscilloscope-rp2040/sysaudio/audio.py similarity index 100% rename from oscilloscope-rp2040/src/audio.py rename to oscilloscope-rp2040/sysaudio/audio.py diff --git a/oscilloscope-rp2040/src/calibration.py b/oscilloscope-rp2040/sysaudio/calibration.py similarity index 100% rename from oscilloscope-rp2040/src/calibration.py rename to oscilloscope-rp2040/sysaudio/calibration.py diff --git a/oscilloscope-rp2040/src/config.py b/oscilloscope-rp2040/sysaudio/config.py similarity index 100% rename from oscilloscope-rp2040/src/config.py rename to oscilloscope-rp2040/sysaudio/config.py diff --git a/oscilloscope-rp2040/src/daq.py b/oscilloscope-rp2040/sysaudio/daq.py similarity index 100% rename from oscilloscope-rp2040/src/daq.py rename to oscilloscope-rp2040/sysaudio/daq.py diff --git a/oscilloscope-rp2040/src/diagnostics.py b/oscilloscope-rp2040/sysaudio/diagnostics.py similarity index 100% rename from oscilloscope-rp2040/src/diagnostics.py rename to oscilloscope-rp2040/sysaudio/diagnostics.py diff --git a/oscilloscope-rp2040/src/dsp.py b/oscilloscope-rp2040/sysaudio/dsp.py similarity index 100% rename from oscilloscope-rp2040/src/dsp.py rename to oscilloscope-rp2040/sysaudio/dsp.py diff --git a/oscilloscope-rp2040/src/experiments.py b/oscilloscope-rp2040/sysaudio/experiments.py similarity index 100% rename from oscilloscope-rp2040/src/experiments.py rename to oscilloscope-rp2040/sysaudio/experiments.py diff --git a/oscilloscope-rp2040/src/io.py b/oscilloscope-rp2040/sysaudio/io.py similarity index 100% rename from oscilloscope-rp2040/src/io.py rename to oscilloscope-rp2040/sysaudio/io.py diff --git a/oscilloscope-rp2040/src/metrics.py b/oscilloscope-rp2040/sysaudio/metrics.py similarity index 100% rename from oscilloscope-rp2040/src/metrics.py rename to oscilloscope-rp2040/sysaudio/metrics.py diff --git a/oscilloscope-rp2040/src/plots.py b/oscilloscope-rp2040/sysaudio/plots.py similarity index 100% rename from oscilloscope-rp2040/src/plots.py rename to oscilloscope-rp2040/sysaudio/plots.py diff --git a/oscilloscope-rp2040/src/render.py b/oscilloscope-rp2040/sysaudio/render.py similarity index 100% rename from oscilloscope-rp2040/src/render.py rename to oscilloscope-rp2040/sysaudio/render.py diff --git a/oscilloscope-rp2040/src/viz.py b/oscilloscope-rp2040/sysaudio/viz.py similarity index 100% rename from oscilloscope-rp2040/src/viz.py rename to oscilloscope-rp2040/sysaudio/viz.py diff --git a/oscilloscope-rp2040/tests/test_imports.py b/oscilloscope-rp2040/tests/test_imports.py index 37ca31d..1cb0a22 100644 --- a/oscilloscope-rp2040/tests/test_imports.py +++ b/oscilloscope-rp2040/tests/test_imports.py @@ -1,10 +1,3 @@ -import os -import sys - -# Ensure src is in path for tests -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../"))) - - def test_can_import_package() -> None: """ Ensures the dependency graph is acyclic and src can be imported. @@ -12,7 +5,7 @@ def test_can_import_package() -> None: This test verifies that the package structure is valid and that no circular imports prevent the root package from initializing. """ - import src + import sysaudio # If we got here, __init__.py ran successfully - assert hasattr(src, "__file__") or hasattr(src, "__path__") + assert hasattr(sysaudio, "__file__") or hasattr(sysaudio, "__path__") diff --git a/pyproject.toml b/pyproject.toml index 5eb6b94..9a10c80 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,7 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + [project] name = "systems-audio-lab" version = "0.9.0" @@ -51,9 +55,11 @@ testpaths = [ "oscilloscope-rp2040/tests", ] -pythonpath = [ - "oscilloscope-rp2040/src", -] +pythonpath = ["oscilloscope-rp2040"] + +[tool.setuptools.packages.find] +where = ["oscilloscope-rp2040"] +include = ["sysaudio*"] [tool.mypy] mypy_path = "oscilloscope-rp2040"