Metadata-Version: 2.4
Name: amp-benchkit
Version: 0.3.1
Summary: Unified GUI + LabJack tooling and installer helpers
Author: Brian Wedderburn
License-Expression: MIT
Project-URL: Homepage, https://github.com/bwedderburn/amp-benchkit
Project-URL: Repository, https://github.com/bwedderburn/amp-benchkit
Project-URL: Issues, https://github.com/bwedderburn/amp-benchkit/issues
Project-URL: Changelog, https://github.com/bwedderburn/amp-benchkit/blob/main/CHANGELOG.md
Keywords: labjack,instrumentation,gui,automation
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: POSIX :: Linux
Classifier: Environment :: Console
Classifier: Environment :: X11 Applications :: Qt
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Classifier: Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator
Classifier: Topic :: Scientific/Engineering :: Visualization
Classifier: Topic :: System :: Hardware :: Universal Serial Bus (USB)
Classifier: Topic :: Utilities
Classifier: Framework :: Pytest
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy==2.3.3
Requires-Dist: matplotlib==3.10.6
Requires-Dist: pyvisa==1.15.0
Requires-Dist: pyserial==3.5
Requires-Dist: LabJackPython==2.1.0
Provides-Extra: gui
Requires-Dist: PySide6==6.9.2; extra == "gui"
Requires-Dist: PyQt5==5.15.11; extra == "gui"
Provides-Extra: dev
Requires-Dist: black; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: mypy; extra == "dev"
Requires-Dist: pre-commit; extra == "dev"
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: pytest-cov; extra == "test"
Provides-Extra: publish
Requires-Dist: build; extra == "publish"
Requires-Dist: twine; extra == "publish"
Requires-Dist: wheel; extra == "publish"
Requires-Dist: setuptools; extra == "publish"
Dynamic: license-file

# amp-benchkit

Unified GUI + LabJack instrumentation helper environment.

![Release](https://img.shields.io/github/v/release/bwedderburn/amp-benchkit)
[![CI](https://github.com/bwedderburn/amp-benchkit/actions/workflows/ci.yml/badge.svg)](https://github.com/bwedderburn/amp-benchkit/actions/workflows/ci.yml)
![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)
![Python Versions](https://img.shields.io/pypi/pyversions/amp-benchkit.svg)
![PyPI](https://img.shields.io/pypi/v/amp-benchkit.svg)


## Contents

- `unified_gui_layout.py` – Main multi‑tab GUI / CLI tool (PySide6/PyQt5, VISA, Serial, LabJack U3).
- `amp_benchkit/deps.py` – Dependency detection (Qt binding, pyvisa, pyserial, LabJack) + shared helpers.
- `amp_benchkit/fy.py` – FY32xx function generator helpers (command build, apply, sweep).
- `amp_benchkit/tek.py` – Tektronix scope SCPI helpers (setup, waveform capture, IEEE block parsing).
- `amp_benchkit/dsp.py` – DSP helpers (RMS, Vpp, THD FFT, bandwidth knees).
- `amp_benchkit/gui/` – Incremental extraction of GUI tabs (generator, scope, DAQ extracted).
- `amp_benchkit/u3util.py` – LabJack U3 safe‑open and feature detection utilities.

- `scripts/install_exodriver_alpine.sh` – Idempotent installer for Exodriver (liblabjackusb) on Alpine (musl) or glibc.
- `patches/exodriver-install-alpine.patch` – Patch capturing local enhancement to upstream `exodriver` install script (for reproducibility / PR prep).

## Quick Start

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt

# Build & install Exodriver (USB support)
./scripts/install_exodriver_alpine.sh

# Run CLI selftest (headless)
python unified_gui_layout.py selftest

# Launch GUI (if X / Wayland or VS Code desktop available)
python unified_gui_layout.py gui --gui
```

### Installed (Package) Usage

After `pip install .[gui]` you can use console scripts:

```bash
amp-benchkit selftest          # run selftests (headless)
amp-benchkit diag              # dependency diagnostics summary
amp-benchkit gui --gui         # launch GUI (or use: amp-benchkit-gui --gui)
amp-benchkit config-dump       # show persisted JSON config
amp-benchkit config-reset      # reset config to defaults

# Generate frequency sweep (headless list output)
amp-benchkit sweep --start 20 --stop 20000 --points 10 --mode log

```

### Sweep CLI (Headless Frequency Lists)

The `sweep` subcommand prints a list of frequency points to stdout (one per line) for use in shell pipelines or other tooling.

Arguments:

* `--start` (Hz, float, required)
* `--stop` (Hz, float, required)
* `--points` (int >= 2, required)
* `--mode` (`log` | `linear`, default: `log`)

Spacing rules:
* `linear`: even arithmetic spacing including both endpoints.
* `log`: geometric progression including endpoints (requires start > 0, stop > 0).

Example (log spaced 6 points 10 Hz → 10 kHz):

```bash
amp-benchkit sweep --start 10 --stop 10000 --points 6 --mode log
```
Outputs (rounded to 6 decimals, trailing zeros trimmed):
```
10
46.415889
215.443469
1000
4641.588834
10000
```

Linear example:

```bash
amp-benchkit sweep --start 100 --stop 1000 --points 10 --mode linear
```

Exit codes:
* 0 on success
* 1 on invalid arguments (e.g. points < 2, stop < start)

Typical usage piping into another tool:

```bash
for f in $(amp-benchkit sweep --start 20 --stop 20000 --points 25 --mode log); do \
	echo "Would run measurement at $f Hz"; \
done
```

Future enhancements (planned): optional JSON/CSV output (`--format json|csv`) and direct KPI sweep command.

> Note: Direct invocation via `python unified_gui_layout.py ...` still works but is considered a legacy path. Prefer the installed console scripts (`amp-benchkit`, `amp-benchkit-gui`) for forward compatibility; future releases may relocate the legacy file.
```

Add `--verbose` to any of the above to elevate logging to DEBUG.

### Logging

A central logger (`amp_benchkit.logging`) is initialized by the CLI. It emits to stderr and a rotating file (`benchkit.log`) under `XDG_STATE_HOME` / `XDG_CACHE_HOME` (fallback `~/.cache/amp-benchkit`). Levels:

* INFO: High‑level actions (applied FY settings, captured scope block, etc.)
* DEBUG: Detailed SCPI / serial commands (enable with `--verbose`)
* WARNING/ERROR: Dependency or runtime issues

You can integrate programmatically:
```python
from amp_benchkit.logging import setup_logging, get_logger
setup_logging(verbose=True)
log = get_logger()
log.info("Starting scripted acquisition")
```

### Config Persistence

User preferences (FY port/protocol, last scope resource) persist to:
`~/.config/amp-benchkit/config.json` (XDG‑style). Functions:

```python
from amp_benchkit.config import load_config, save_config, update_config
cfg = load_config()
update_config(fy_port="/dev/ttyUSB0")
```
CLI helpers: `config-dump`, `config-reset`.

Corrupt config files are ignored gracefully and overwritten on next save.

### Programmatic Use (Headless Helpers)

```python
from amp_benchkit.fy import fy_apply
from amp_benchkit.tek import tek_capture_block, TEK_RSRC_DEFAULT
from amp_benchkit.u3util import open_u3_safely

# Apply a 1 kHz 2 Vpp sine on FY CH1 (auto port detection)
fy_apply(freq_hz=1000, amp_vpp=2.0, wave="Sine", ch=1)

# Capture a waveform from a Tek scope (returns t, volts, raw)
t, v, raw = tek_capture_block(TEK_RSRC_DEFAULT, ch=1)

# Open LabJack U3 safely
try:
	d = open_u3_safely()
	ain0 = d.getAIN(0)
	print("AIN0=", ain0)
finally:
	try: d.close()
	except Exception: pass
```

If LabJack USB still not detected on Alpine (musl) add:
```bash
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
```

## Exodriver (LabJack USB Driver)

We vendor a minimal, source‑only snapshot of Exodriver under `exodriver/` (no `.git`, no compiled objects). See `EXODRIVER.md` for rationale, update procedure, and upstream attribution. For a full clone (e.g. to inspect history or contribute upstream), run:

```bash
git clone https://github.com/labjack/exodriver.git
```

Then either build directly or point our helper script at it:

```bash
EXO_DIR=exodriver ./scripts/install_exodriver_alpine.sh
```

### Health Check (after install)
```bash
python -c "import u3; print('u3 import OK')" || echo "LabJack Python import failed"
```
To probe a device (will fail gracefully if none):
```bash
python scripts/check_labjack_usb.py
```

## Make Targets (after Makefile added)
Planned:
- `make install-exodriver` – run wrapper.
- `make check-usb` – run health script.
- `make gui` – launch GUI.
- `make selftest` – run headless integrations.

## Development Hygiene

Install and enable pre-commit hooks to catch style and oversized file issues early:
```bash
pip install pre-commit
pre-commit install
pre-commit run --all-files   # optional first full pass
```
Hooks block committing virtualenvs / `site-packages` and large (>5MB) binaries, and run ruff, formatting, and mypy.
- `make selftest` – headless tests.
 - See `ROADMAP.md` for planned milestones and how to propose new items (edit a single table row per PR).

## Development (Lint / Format / Type)

We use `ruff` (lint), `mypy` (types). Formatting is kept Black-compatible (you can optionally run `ruff format` in newer versions).

Manual invocations:

```bash
ruff check .          # lint
ruff check . --fix    # autofix simple issues
mypy .                 # type check
pytest -q             # tests
```

If you add a Makefile target locally, suggested grouping:

```bash
make lint   # ruff check
make type   # mypy
make test   # pytest
make ci-local  # run all of the above
```

Pre-commit is configured; enable with:

```bash
pre-commit install
```

Then commits will auto-run ruff / mypy (if configured) on changed files.

## Troubleshooting

| Symptom | Cause | Fix |
|---------|-------|-----|
| `Could not load the Exodriver driver` | `liblabjackusb` not on search path | Run installer; set `LD_LIBRARY_PATH` |
| `No module named numpy` | Dependencies not installed | `pip install -r requirements.txt` (future) |
| GUI won’t launch | Missing Qt binding / no display | Install PySide6; ensure DISPLAY set or use VS Code desktop |
| `Permission denied` running script | Not executable | `chmod +x script.sh` |

## Future Improvements

Implemented so far:
* Phase 1 & 2 modularization: dependencies, instruments (FY / Tek / U3) extracted
* Core pytest suite (FFT THD, IEEE block decode, config roundtrip)
* Logging subsystem and JSON config persistence
* GitHub Actions CI (Python 3.11 / 3.12)
* DSP module extraction (`amp_benchkit.dsp`)
* GUI tab modularization (generator + scope + DAQ + automation + diagnostics extracted into `amp_benchkit.gui`)
* Version 0.2.0 released – full GUI tab extraction, shared Qt helper, removal of deprecated DSP wrappers.

Planned / open:
* Continue GUI tab extraction: diagnostics
* Additional error handling (timeouts around VISA / serial)
* Test extras & packaging polish
* Optional hardware-in-loop stage (conditional)
* Type hint expansion & static checks
* Potential REST / WebSocket automation bridge

---

**Support references**: LabJack Exodriver repo <https://github.com/labjack/exodriver>

## Changelog

See `CHANGELOG.md` for a full list of versions and changes. Highlight 0.2.0: all GUI tabs modular, deprecated DSP wrappers removed, shared lazy Qt import helper.

## Release & Publishing

Steps to cut a release (example for 0.2.0 already performed):

1. Update version in `pyproject.toml` & confirm tests pass.
2. Update `CHANGELOG.md` (add new heading, date, notes).
3. Commit: `git commit -am "release: vX.Y.Z"`.
4. Tag locally: `git tag vX.Y.Z` (lightweight or annotated `-a`).
5. Build artifacts locally (optional pre-check): `python -m build --sdist --wheel`.
6. Push branch and tag: `git push && git push origin vX.Y.Z`.
7. GitHub Actions CI will build & test; if a publish workflow exists with `PYPI_API_TOKEN` (an API token from PyPI stored as an Actions secret), it will upload.
8. Draft GitHub Release, attach release notes (can pull from changelog), verify assets.
9. Post-release: bump version to next dev (e.g., 0.2.1.dev0) if ongoing changes expected.

Dry-run publish (validate metadata):
```bash
python -m build
twine check dist/*
```

Install from a specific tag directly:
```bash
pip install git+https://github.com/bwedderburn/amp-benchkit@v0.2.0
```

## Next Steps / Roadmap

- Add `requirements.txt` or `pyproject.toml` for pinned dependencies.
- Continuous Integration: GitHub Actions workflow to run `make selftest` and build Docker image.
- Optional hardware-in-loop stage (tagged, skipped by default) for real LabJack tests.
- Improve `unified_gui_layout.py` modularity (further split GUI/automation logic – Phase 1 & 2 complete: deps + instruments extracted).
	(Logging + config persistence now implemented.)

	## Public API (Stability: Beta)

	The following functions / symbols are considered part of the provisional public API and will aim to avoid breaking changes without a minor version bump:

	Instrument helpers:
	* `amp_benchkit.fy.build_fy_cmds(freq_hz, amp_vpp, off_v, wave, duty=None, ch=1)`
	* `amp_benchkit.fy.fy_apply(...)`
	* `amp_benchkit.fy.fy_sweep(port, ch, proto, start=None, end=None, t_s=None, mode=None, run=None)`
	* `amp_benchkit.tek.tek_capture_block(resource, ch=1)`
	* `amp_benchkit.tek.scope_capture_calibrated(resource, timeout_ms=15000, ch=1)`
	* `amp_benchkit.tek.scope_screenshot(filename, resource=..., timeout_ms=15000, ch=1)`
	* `amp_benchkit.u3util.open_u3_safely()`

	Diagnostics / utilities:
	* `amp_benchkit.deps.find_fy_port()`
	* `amp_benchkit.logging.setup_logging(verbose=False, file_logging=True)`
	* `amp_benchkit.config.load_config()` / `save_config(cfg)` / `update_config(**kv)`

	DSP / analysis (`amp_benchkit.dsp`):
	* `vrms(v)`
	* `vpp(v)`
	* `thd_fft(t, v, f0=None, nharm=10, window='hann')`
	* `find_knees(freqs, amps, ref_mode='max', ref_hz=1000.0, drop_db=3.0)`

	Exceptions:
	* `amp_benchkit.fy.FYError`, `FYTimeoutError`
	* `amp_benchkit.tek.TekError`, `TekTimeoutError`

	Items not listed above should be treated as internal and are subject to change.
- Add type hints & mypy pass for critical modules.
- Provide wheel / PyPI packaging for headless CLI mode.
- Add a simple REST or WebSocket bridge for remote automation control.
- Extend Makefile with `format` / `lint` targets (black, ruff, mypy).
- Optional plugin architecture for new instrument tabs.
