Skip to content

Commit

Permalink
Introduce the nScope python package (#12)
Browse files Browse the repository at this point in the history
Introduce a python package for native access to nLab devices
  • Loading branch information
davidjmeyer authored Oct 22, 2024
1 parent b814302 commit a68aeba
Show file tree
Hide file tree
Showing 20 changed files with 756 additions and 2 deletions.
186 changes: 186 additions & 0 deletions .github/workflows/build_python.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
# This file is autogenerated by maturin v1.7.4
# To update, run
#
# maturin generate-ci github
#
name: nlabapi Python CI

on:
push:
branches:
- main
- master
tags:
- '*'
pull_request:
workflow_dispatch:

permissions:
contents: read

jobs:
linux:
runs-on: ${{ matrix.platform.runner }}
strategy:
matrix:
platform:
- runner: ubuntu-latest
target: x86_64
- runner: ubuntu-latest
target: x86
# - runner: ubuntu-latest
# target: aarch64
# - runner: ubuntu-latest
# target: armv7
# - runner: ubuntu-latest
# target: s390x
# - runner: ubuntu-latest
# target: ppc64le
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: 3.x
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: --release --out dist --find-interpreter
sccache: 'true'
manylinux: auto
before-script-linux: |
yum install -y libusbx-devel libudev-devel
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels-linux-${{ matrix.platform.target }}
path: dist

# musllinux:
# runs-on: ${{ matrix.platform.runner }}
# strategy:
# matrix:
# platform:
# - runner: ubuntu-latest
# target: x86_64
# - runner: ubuntu-latest
# target: x86
# - runner: ubuntu-latest
# target: aarch64
# - runner: ubuntu-latest
# target: armv7
# steps:
# - uses: actions/checkout@v4
# - uses: actions/setup-python@v5
# with:
# python-version: 3.x
# - name: Build wheels
# uses: PyO3/maturin-action@v1
# with:
# target: ${{ matrix.platform.target }}
# args: --release --out dist --find-interpreter
# sccache: 'true'
# manylinux: musllinux_1_2
# before-script-linux: |
# sudo apt-get update
# sudo apt-get install -y libusb-1.0-0-dev libudev-dev pkg-config
# - name: Upload wheels
# uses: actions/upload-artifact@v4
# with:
# name: wheels-musllinux-${{ matrix.platform.target }}
# path: dist

windows:
runs-on: ${{ matrix.platform.runner }}
strategy:
matrix:
platform:
- runner: windows-latest
target: x64
- runner: windows-latest
target: x86
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: 3.x
architecture: ${{ matrix.platform.target }}
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: --release --out dist --find-interpreter
sccache: 'true'
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels-windows-${{ matrix.platform.target }}
path: dist

macos:
runs-on: ${{ matrix.platform.runner }}
strategy:
matrix:
platform:
- runner: macos-13
target: x86_64
- runner: macos-14
target: aarch64
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: 3.x
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: --release --out dist --find-interpreter
sccache: 'true'
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels-macos-${{ matrix.platform.target }}
path: dist

sdist:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build sdist
uses: PyO3/maturin-action@v1
with:
command: sdist
args: --out dist
- name: Upload sdist
uses: actions/upload-artifact@v4
with:
name: wheels-sdist
path: dist

release:
name: Release
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' }}
needs: [linux, windows, macos, sdist]
permissions:
# Use to sign the release artifacts
id-token: write
# Used to upload release artifacts
contents: write
# Used to generate artifact attestation
attestations: write
steps:
- uses: actions/download-artifact@v4
- name: Generate artifact attestation
uses: actions/attest-build-provenance@v1
with:
subject-path: 'wheels-*/*'
- name: Publish to PyPI
if: "startsWith(github.ref, 'refs/tags/')"
uses: PyO3/maturin-action@v1
env:
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
with:
command: upload
args: --non-interactive --skip-existing wheels-*/*
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
.DS_Store
Cargo.lock
.idea
venv
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,18 @@ documentation = "https://github.com/nLabs-nScope/nlabapi"
readme = "README.md"
edition = "2018"

[lib]
name = "nlabapi"
crate-type = ["rlib", "cdylib"]

[dependencies]
git-version = "0.3.4"
hidapi = "2.3.3"
log = "~0.4"
regex = "~1"
rusb = { version = "0.9.3", features = ["vendored"] }
dfu-libusb = "0.5.1"
pyo3 = { version = "~0.22", features = ["multiple-pymethods"] }

[dev-dependencies]
env_logger = "0.10.0"
Expand Down
97 changes: 96 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,99 @@

![Cargo Test](https://github.com/nLabs-nScope/nlabapi/actions/workflows/tests.yml/badge.svg)

Low-level software interface for nLab devices
Low-level software interface for nLab devices. Libraries for Python and Rust are available. The recommended easy way to access data straight from the nLab is the python interface, provided by the `nlabapi` python package.

## Python Usage

1. Install the `nlabapi` python package

On an existing python installation, use `pip` to install the nlabapi package
```shell
$ pip install nlabapi
```

2. Test Import and Usage of `nlabapi`
Entering the above one-liner python program using `python -c` should print a list of all connected nLabs.

```shell
$ python -c "import nlabapi; nlabapi.LabBench.list_all_nlabs()"
Link to nLab v2 [ available: true ]
```

3. Write your own scripts, or use the examples
```shell
$ python examples/list_all_nlabs.py
List of all detected nScopes:
Link to nLab v2 [ available: true ]
```

## Building from Source

The nLab API can be built and run from source to enable users and developers to quickly iterate on nLab source code. To establish a development environment, follow the steps below.


### Prerequisites

1. Rust Toolchain (https://rustup.rs)

After installing the development dependencies, check to make sure you have a working environment by running version commands for each of the required tools.

```shell
$ rustup --version
```
The above commands should print a version successfully.

> **Note** - macOS specifics
>
> On macOS the project is configured to build a universal binary, including both x86 and Apple Silicon binaries in one. To enable that, we must add both rust target toolchains as follows:
> ```shell
> rustup target add x86_64-apple-darwin
> rustup target add aarch64-apple-darwin
> ```
> **Note** - Linux specifics
>
> On linux distributions, we need the system library headers for `libusb` and `libudev`. To install these on an Ubuntu distribtion, the following command should work.
> ```shell
> sudo apt-get install libusb-1.0-0-dev libudev-dev
> ```
> On other distributions, developers should look to their package managers for these development headers.
### Clone and Install Development Dependencies
```shell
$ git clone https://github.com/nLabs-nScope/nlabapi.git
$ cd nlabapi
```
### Build and Run
```shell
$ cargo build
$ cargo run --example list_all_nscopes
```

## Python Development

This project also supports a python interface to the nLab. To set up an environment for python development, follow the steps below.

### Prerequisites

1. All steps above
2. A Python 3.9 or newer installation

### Create a virtual environment and activate it
```shell
$ python3 -m venv venv
$ source venv/bin/activate
```

### Install python dependencies
```shell
$ pip install maturin
```

### Build and install the nlabapi package for developement
```shell
$ maturin develop
$ python examples/list_all_nlabs.py
```
12 changes: 12 additions & 0 deletions examples/analog_outputs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from nlabapi import LabBench
import time

nlab = LabBench.open_first_available()

nlab.ax_turn_on(1)
time.sleep(10)
nlab.ax_turn_off(1)

nlab.ax_turn_on(2)
time.sleep(10)
nlab.ax_turn_off(2)
22 changes: 22 additions & 0 deletions examples/data_request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import matplotlib.pyplot as plt
import numpy as np
from nlabapi import LabBench, AnalogSignalPolarity

nlab = LabBench.open_first_available()

nlab.ax_turn_on(1)
nlab.ax_set_amplitude(1, 3.5)
nlab.ax_set_polarity(1, AnalogSignalPolarity.Bipolar)
number_of_samples = 19200
sample_rate = 8000.0

data = nlab.read_all_channels(sample_rate, number_of_samples)
nlab.ax_turn_off(1)
plt.plot(np.arange(number_of_samples)/sample_rate, data[0], label="Ch1")
plt.plot(np.arange(number_of_samples)/sample_rate, data[1], label="Ch2")
plt.plot(np.arange(number_of_samples)/sample_rate, data[2], label="Ch3")
plt.plot(np.arange(number_of_samples)/sample_rate, data[3], label="Ch4")
plt.xlabel("Time (s)")
plt.ylabel("Voltage (V)")
plt.legend()
plt.show()
4 changes: 4 additions & 0 deletions examples/list_all_nlabs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from nlabapi import LabBench

print("List of all detected nScopes:")
LabBench.list_all_nlabs()
File renamed without changes.
11 changes: 11 additions & 0 deletions examples/monitor_power.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from nlabapi import LabBench
import time

nlab = LabBench.open_first_available()

while True:
time.sleep(0.5)
power_status = nlab.power_status()

print(f"\n{power_status}")
print(f"State: {power_status.state}, Usage: {power_status.usage:1.3f} Watts")
2 changes: 1 addition & 1 deletion examples/monitor_power.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ fn main() -> Result<(), Box<dyn Error>> {
let mut nlabs: Vec<Nlab> = bench.open_all_available();

loop {
thread::sleep(time::Duration::from_millis(10));
thread::sleep(time::Duration::from_millis(500));

nlabs.retain(|n| n.is_connected());

Expand Down
12 changes: 12 additions & 0 deletions examples/pulse_outputs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from nlabapi import LabBench
import time

nlab = LabBench.open_first_available()

nlab.px_turn_on(1)
time.sleep(10)
nlab.px_turn_off(1)

nlab.px_turn_on(2)
time.sleep(10)
nlab.px_turn_off(2)
Loading

0 comments on commit a68aeba

Please sign in to comment.