Metadata-Version: 2.4
Name: QPolargraph
Version: 1.3.3
Summary: Qt-based interface for a two-dimensional polargraph scanner
Author-email: "David G. Grier" <david.grier@nyu.edu>
License: GNU GENERAL PUBLIC LICENSE
        Version 3, 29 June 2007
        
        Copyright (C) 2022 David G. Grier
        
        This program is free software: you can redistribute it and/or modify
        it under the terms of the GNU General Public License as published by
        the Free Software Foundation, either version 3 of the License, or
        (at your option) any later version.
        
        This program is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.
        
        You should have received a copy of the GNU General Public License
        along with this program.  If not, see <https://www.gnu.org/licenses/>.
        
Project-URL: Homepage, https://github.com/davidgrier/QPolargraph
Project-URL: Repository, https://github.com/davidgrier/QPolargraph
Project-URL: Documentation, https://qpolargraph.readthedocs.io
Project-URL: Issue Tracker, https://github.com/davidgrier/QPolargraph/issues
Keywords: polargraph,scanner,stepper motor,Arduino,Qt,scientific instrument
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE.md
Requires-Dist: numpy>=1.20
Requires-Dist: qtpy>=2.0
Requires-Dist: pyqtgraph>=0.13
Requires-Dist: parse>=1.19
Requires-Dist: QInstrument>=3.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-qt>=4.0; extra == "dev"
Provides-Extra: docs
Requires-Dist: sphinx>=8.0; extra == "docs"
Requires-Dist: pydata-sphinx-theme>=0.16; extra == "docs"
Requires-Dist: sphinx-autodoc-typehints>=2.0; extra == "docs"
Requires-Dist: PyQt5; extra == "docs"
Dynamic: license-file

# QPolargraph

[![Tests](https://github.com/davidgrier/QPolargraph/actions/workflows/tests.yml/badge.svg)](https://github.com/davidgrier/QPolargraph/actions/workflows/tests.yml)
[![Documentation](https://readthedocs.org/projects/qpolargraph/badge/?version=latest)](https://qpolargraph.readthedocs.io/en/latest/)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.19630922.svg)](https://doi.org/10.5281/zenodo.19630922)
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)

A Qt-based instrument interface for a two-dimensional polargraph scanner.
Built on [QInstrument](https://github.com/davidgrier/QInstrument).

A [polargraph](http://www.polargraph.co.uk/) translates a payload to any
position within its scan area using two stepper motors and a timing belt.
[Originally developed](http://juerglehni.com/works/hektor) by Jürg Lehni and
Uli Franke as a drawing machine, the design scales easily to large areas,
delivers millimeter-scale positioning accuracy, and is very cost-effective to
build.

<img src="docs/QScanner.png" width="75%" alt="QScanner interface">

## Hardware

| Component | Description |
|-----------|-------------|
| 2× Nema-17 stepper motor | Mounted above and to either side of the scan area |
| GT2 timing belt | Spans both motors; payload hangs from its midpoint |
| GT2 drive gear (25-tooth) | One per motor; engages the belt |
| [Adafruit Motor Shield v2](https://www.adafruit.com/product/1438) | Drives both steppers from a single Arduino |
| Arduino (Uno or compatible) | Runs the `acam3` firmware; connects to the host via USB |

The belt forms a V-shape between the two motors. Each motor changes the length
of one side of the V, moving the payload in two dimensions.

## Firmware

The Arduino must be flashed with `hardware/arduino/acam3/acam3.ino` before first use.
The easiest way is to use the built-in installer:

```bash
qpolargraph-flash
```

This opens a dialog that detects the attached Arduino, installs any missing
Arduino libraries (`Adafruit Motor Shield V2 Library`, `AccelStepper`),
compiles, and uploads the firmware — all without opening the Arduino IDE.
It requires [`arduino-cli`](https://arduino.github.io/arduino-cli/) to be
installed and on `PATH`.

Alternatively, open `hardware/arduino/acam3/acam3.ino` in the
[Arduino IDE](https://www.arduino.cc/en/software) and upload manually.

The firmware and package versions are coupled: `Motors.identify()` checks
that the connected Arduino reports the expected firmware version and that
the Adafruit Motor Shield is detected at I2C address `0x60`, refusing to
open the port if either check fails.

## Installation

Clone the repository and install in editable mode:

```bash
git clone https://github.com/davidgrier/QPolargraph
cd QPolargraph
python -m venv .qp
source .qp/bin/activate          # Windows: .qp\Scripts\activate
pip install -e ".[dev]"
pip install PyQt6                # or PyQt5, PySide2, PySide6
```

[QInstrument](https://github.com/davidgrier/QInstrument) is installed
automatically as a dependency.

## Calibration

Five geometric parameters describe the polargraph. Set them in the
`QPolargraphWidget` control panel or pass them directly to `Polargraph()`:

| Parameter | Default | Description |
|-----------|---------|-------------|
| `ell` | 1.0 m | Horizontal distance between the two motor pulley centres |
| `y0` | 0.1 m | Vertical distance from the pulleys to the home position |
| `pitch` | 2.0 mm | GT2 belt tooth pitch |
| `circumference` | 25 | Number of belt teeth on the drive gear |
| `steps` | 200 | Motor steps per revolution |

Measure `ell` and `y0` with a ruler after mounting the motors.
`pitch`, `circumference`, and `steps` are determined by the belt and gear
specifications and normally do not need to change.

Settings are saved to `~/.QScanner/` automatically when the application
closes, so calibration only needs to be done once.

## Quick start

### Scanner application

```bash
python -m QPolargraph
# or, after installation:
qpolargraph
```

The application opens a live plot showing the belt geometry and the scan
trajectory. Use the **Scan** button to start a polar-arc sweep, or
**Home** / **Center** to move the payload to the home or centre position.

### Embedding in your own application

Subclass `QScanner` to add experiment-specific data acquisition:

```python
from qtpy.QtWidgets import QApplication
from qtpy import QtCore
from QPolargraph.QScanner import QScanner
import sys


class MyScanner(QScanner):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.scanner.dataReady.connect(self.acquire)

    @QtCore.Slot(object)
    def acquire(self, position):
        x, y = position
        # measure something at (x, y) and call self.plotData(x, y, hue)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    scanner = MyScanner()
    scanner.show()
    sys.exit(app.exec())
```

### Controlling the hardware directly

```python
from qtpy.QtCore import QCoreApplication
from QPolargraph.Polargraph import Polargraph
import sys

app = QCoreApplication(sys.argv)
pg = Polargraph(ell=0.8, y0=0.15).find()   # auto-detects USB port
print(pg.position)                           # (x, y, running) in metres
pg.moveTo(0.0, 0.3)
while pg.running():
    pass
pg.release()
```

## Architecture

```
QInstrument.QSerialInstrument
└── Motors               # acam3 serial protocol: goto, speed, position, …
    └── Polargraph       # geometry: step indexes ↔ Cartesian coordinates

QInstrument.QInstrumentWidget
└── QRasterScanWidget    # scan-pattern parameter controls

QInstrument.QInstrumentWidget
└── QPolargraphWidget    # polargraph hardware controls

QtCore.QObject
└── QScanPattern         # base: rectangular perimeter trajectory
    ├── RasterScan       # row-by-row zigzag raster
    └── PolarScan        # arc-by-arc sweep centred on the left pulley

QtWidgets.QMainWindow
└── QScanner             # full application: live plot + scan controls
```

## Development

Run the test suite:

```bash
source .qp/bin/activate
pytest tests/
```

Tests run automatically before every `git push`. To install the pre-push
hook in a fresh clone:

```bash
git config core.hooksPath .githooks
```

## Acknowledgements

Work on this project at New York University is supported by the
National Science Foundation of the United States under award number DMR-2438983.

## References

H. W. Gao, K. I. Mishra, A. Winters, S. Wolin, and D. G. Grier,
"Flexible wide-field high-resolution scanning camera for continuous-wave acoustic holography,"
*Review of Scientific Instruments* **89**, 114901 (2018).
https://doi.org/10.1063/1.5053666
