Metadata-Version: 2.4
Name: harp
Version: 0.5.0
Summary: Library for data acquisition and control of devices implementing the Harp protocol.
Author-email: harp-tech <contact@harp-tech.org>
License-Expression: MIT
Project-URL: Homepage, https://harp-tech.org/
Project-URL: Repository, https://github.com/harp-tech/python/
Project-URL: Documentation, https://harp-tech.org/python/
Project-URL: Bug Tracker, https://github.com/harp-tech/python/issues
Project-URL: Changelog, https://github.com/harp-tech/python/releases
Keywords: harp,harp-protocol,data-acquisition,serial,hardware,device-control
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: Intended Audience :: Developers
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS
Classifier: Programming Language :: Python :: 3
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 :: Hardware Drivers
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: harp-protocol
Requires-Dist: harp-device
Requires-Dist: harp-serial
Requires-Dist: harp-data
Dynamic: license-file

<p align="center">
  <img src="https://raw.githubusercontent.com/harp-tech/python/c0c8c23cc1a965c41834a106fe686be76c2f0520/docs/assets/logo.svg" alt="Harp logo" width="400">
</p>

# harp

Python interface to [Harp](https://harp-tech.org/articles/what-is-harp.html) devices and their recorded data, implementing the [Harp binary protocol](https://harp-tech.org/protocol/BinaryProtocol-8bit.html).

Harp is a standard for asynchronous real-time data acquisition and experimental control in neuroscience. Every command and event is hardware timestamped on the device. Devices sharing a clock line continuously self-synchronize, so events across a rig sit on one clock and need no post-hoc alignment.

This project includes four main packages:

 - **harp-protocol**: Implements the Harp binary protocol in Python, with registers, messages, and payload parsing. See [Protocol API Documentation](https://harp-tech.org/python/api/protocol) for details.

 - **harp-device**: Implements the transport-agnostic `Device` interface and the core register set. See [Device API Documentation](https://harp-tech.org/python/api/device) for details.

 - **harp-serial**: Connects to a `Device` over a serial COM or tty port. See [Serial API Documentation](https://harp-tech.org/python/api/serial) for details.

 - **harp-data**: Reads logged register files into pandas DataFrames. See [Data API Documentation](https://harp-tech.org/python/api/data) for details.

## Installation

All packages are published to PyPI. The `harp` package is a metapackage with no code of its own. It depends on the four packages above, so it is the easiest way to get everything:

```sh
pip install harp
```

`uv add` substitutes for `pip install` throughout.

To install only part of the stack, for example when reading recorded data with no need for serial I/O, install the individual packages. Each one only pulls in what it actually depends on:

| Package | Depends on |
| --- | --- |
| `harp-protocol` | none |
| `harp-device` | `harp-protocol` |
| `harp-serial` | `harp-protocol`, `harp-device` |
| `harp-data` | `harp-protocol` |

```sh
pip install harp-protocol
pip install harp-device
pip install harp-serial
pip install harp-data
```

`harp-benchmarks`, under `src/packages/`, is internal-only and is never published to PyPI.

## Quickstart

There are two typical ways to use `harp`: talking to a **live device** over a serial connection, or reading **data recorded to disk**.

**Talk to a live device.** Open a connection and read/write registers by class:

```python
from harp import serial
from harp.device import behavior, core

# Use "COMx" on Windows, "/dev/ttyUSBx" on Linux.
with serial.open_device(behavior, port="COM3") as device:
    print(device.read(core.WhoAmI).payload)         # a core register
    print(device.read(behavior.AnalogData).payload) # a device register
    device.write(
        core.OperationControl,
        core.OperationControlPayload(operation_mode=core.OperationMode.ACTIVE),
    )
```

**Read a recorded session.** Point a `DatasetReader` at a dataset folder and read registers into pandas DataFrames, with no hardware required:

```python
from harp import data

# Finds device.yml in the folder, builds the device, returns a ready-to-use reader
reader = data.open_dataset("session.harp")
df = reader.read("AnalogData")  # by name
df = reader.read(44)            # or by address

# `contents` names every register the folder holds
frames = {name: reader.read(name) for name in reader.contents}
```

Given a device package already in hand, pass it as the second argument and read by register class. This is the form that type-checks, and it also checks the device identity against the `device.yml` in the folder:

```python
from harp import data
from harp.device import behavior

reader = data.open_dataset("session.harp", behavior)
df = reader.read(behavior.AnalogData)
```

Both paths are based on a device schema. Given only a `device.yml` and no pre-generated package, `create_device_module` compiles it into a module of register classes at runtime, with no code-generation step. This is exactly what `open_dataset` does internally:

```python
from pathlib import Path

from harp.device import schema

behavior = schema.create_device_module(Path("device.yml").read_bytes())
AnalogData = behavior.AnalogData                 # registers are accessed by name
assert behavior.REGISTER_MAP[44] is AnalogData   # or by address
```

See the examples in the documentation for the full walkthroughs, including subscribing to device events and working with custom interface-type converters.

## Contributing

harp is a [uv workspace](https://docs.astral.sh/uv/concepts/workspaces/): every package under `src/packages/` is its own distribution, plus the root `harp` metapackage. Bug reports and contributions are welcome, so please open an issue or pull request.

Clone the repository and install everything with the `dev` dependency group: all workspace packages, editable, plus test and lint tooling.

```sh
uv sync --group dev
```

Before opening a pull request, run the same checks CI runs:

```sh
uv run ruff format --check   # formatting
uv run ruff check            # lint
uv run pyright               # type checking
uv run codespell             # spelling
uv run pytest --cov harp     # tests
```

To add a new package, place it under `src/packages/<name>/` with its own `pyproject.toml` and add it to `[tool.uv.sources]` in the root `pyproject.toml`. If it should ship as part of `harp`, add it to the dependencies of the root package as well.

## Build the documentation

Install the docs dependency group and run mkdocs through uv:

```sh
uv sync --group docs --group dev
uv run mkdocs serve   # live-reloading local preview
uv run mkdocs build   # static site in ./site
```
