Metadata-Version: 2.4
Name: pyrpl-voltage-driver
Version: 0.1.0
Summary: Standalone Red Pitaya analog-output voltage driver, built on pyrpl
Author-email: Marcello Laurel <mlaurel@mit.edu>
Keywords: dac,driver,instrument,lab,pyrpl,red-pitaya,redpitaya,voltage
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: System :: Hardware
Requires-Python: >=3.10
Provides-Extra: test
Requires-Dist: pytest>=8; extra == 'test'
Description-Content-Type: text/markdown

# pyrpl-voltage-driver

A small standalone driver that turns a Red Pitaya (STEMlab 125-10 / 125-14)
analog output into a held DC voltage source via
[pyrpl](https://github.com/pyrpl-fpga/pyrpl). Configures pyrpl's arbitrary
signal generator (ASG) as a `dc` waveform with `amplitude=0`, so `offset` is
the only knob that drives the SMA — `set_voltage()` is then a single FPGA
register write.

Designed to drop into any project that needs a positive, unipolar voltage
actuator on a networked Red Pitaya — independent of any larger control
framework.

## Install

PyPI's `pyrpl` is stuck at 0.9.3.6 (2017). The maintained fork lives on
GitHub, so install it from there **first**, then install this package:

```sh
pip install git+https://github.com/pyrpl-fpga/pyrpl
pip install pyrpl-voltage-driver
```

If you use `uv`:

```sh
uv add git+https://github.com/pyrpl-fpga/pyrpl
uv add pyrpl-voltage-driver
```

## Usage

```python
from pyrpl_voltage_driver import RedPitayaPyrplDAC

dac = RedPitayaPyrplDAC(
    hostname="rp-f0aabb.local",  # or static IP
    output="out1",
    vmax=0.5,                    # clamp output to 0..0.5 V
)

dac.connect()                    # 10-30 s on first call: SSH + bitstream upload
try:
    dac.set_voltage(0.2)
    # ... do stuff ...
    dac.set_voltage(0.0)
finally:
    dac.disconnect()             # parks the output at 0 V and turns it off
```

`set_voltage(v)` clamps `v` to `[0, vmax]`. Calling `set_voltage()` before
`connect()` raises `NotConnectedError`.

Wrapping this driver to fit an existing host application's interface
(`VoltageDriver` Protocol, pydantic YAML config, async, custom error
hierarchy, ...)? See [docs/integration.md](docs/integration.md).

## Constructor parameters

| Parameter             | Default                   | Notes                                                                                       |
| --------------------- | ------------------------- | ------------------------------------------------------------------------------------------- |
| `hostname`            | (required)                | Red Pitaya hostname or IP, e.g. `rp-f0aabb.local`.                                          |
| `output`              | `"out1"`                  | `"out1"` or `"out2"` — which SMA the ASG is wired to.                                       |
| `asg`                 | `"asg0"`                  | `"asg0"` or `"asg1"` — which on-FPGA ASG channel to use.                                    |
| `vmax`                | `1.0`                     | Upper bound on the output in volts. Must be in `(0, 1.0]`. The SMA itself is bipolar ±1 V. |
| `config_name`         | `"pyrpl_voltage_driver"`  | Name of pyrpl's per-instrument YAML file under `~/.pyrpl/`. Use distinct names per device. |
| `user`                | `None`                    | SSH user. Defaults to pyrpl's choice (typically `root`).                                    |
| `password`            | `None`                    | SSH password. Defaults to pyrpl's choice (typically `root`).                                |
| `park_on_disconnect`  | `True`                    | On `disconnect()`, zero the output and turn `output_direct` off.                            |
| `force_qt_offscreen`  | `True`                    | Set `QT_QPA_PLATFORM=offscreen` before importing pyrpl. Disable if you run with a display. |

## Headless / Qt note

pyrpl imports PyQt at top level, which tries to open an X display. By default
this driver sets `QT_QPA_PLATFORM=offscreen` before importing pyrpl, so it
works on headless servers. If you're already exporting your own
`QT_QPA_PLATFORM` (e.g. running with a real display), pass
`force_qt_offscreen=False`.

## First-connect cost

`connect()` opens an SSH session to the Red Pitaya, pushes pyrpl's bitstream,
and starts the FPGA monitor server. Expect **10-30 seconds**. Subsequent
`connect()` calls in the same Python process are no-ops.

## Development

```sh
uv sync --extra test
uv run pytest tests/unit -v
```

The unit tests are fully mocked — no Red Pitaya required.

Opt-in hardware integration test (skipped unless `REDPITAYA_HOST` is set):

```sh
REDPITAYA_HOST=rp-f0aabb.local uv run pytest tests/integration -v
```

## Releasing (maintainers)

Cutting and publishing PyPI releases is documented in
[docs/releasing.md](docs/releasing.md) — covers one-time PyPI setup,
the first-version flow, subsequent versions, and recovering from a
broken upload.
