Metadata-Version: 2.4
Name: qhyccd
Version: 0.1.2
Requires-Dist: numpy>=1.20
Summary: QHYCCD Rust & Python FFI Binding Layer for Windows, Linux (amd64, arm64)
Requires-Python: >=3.8
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM

# qhyccd - Python Bindings for QHYCCD Astronomy Cameras

High-performance, idiomatic Python bindings for **QHYCCD Astronomy Cameras**, powered by Rust (PyO3) with native zero-copy **NumPy `ndarray`** integration.

Designed for high frame rate planetary imaging, deep sky astrophotography, scientific analysis, and automation.

---

## 🌟 Key Features

- **🚀 High Performance**: Built with Rust FFI and PyO3 with GIL releasing during blocking frame capture and exposure.
- **📊 Native NumPy Support**: Direct conversion of 8-bit & 16-bit raw sensor data into 2D `numpy.ndarray` (`uint8` / `uint16`).
- **📦 Zero Configuration (Out-of-the-Box)**: Automatic bundling and registration of official QHYCCD C-SDK dynamic libraries on Windows and Linux.
- **⚡ Dual Mode Support**: Supports both **Live Video Mode (High FPS Streaming)** and **Single Frame Mode (Long Exposure)**.
- **🌍 Cross-Platform**: Pre-compiled wheels for **Windows (x86_64)** and **Linux (amd64 / arm64)**.

---

## 📦 Installation

```bash
pip install qhyccd
```

*Prerequisites: Python >= 3.8 and NumPy >= 1.20.*

---

## ⚡ Quick Start

### 1. Single Frame Capture (Snapshots & Long Exposure)

```python
import time
import numpy as np
from qhyccd import Camera, init_sdk, scan_cameras

# Initialize QHYCCD SDK
init_sdk()

# Scan for connected cameras
cameras = scan_cameras()
print(f"Found {len(cameras)} camera(s): {cameras}")

if cameras:
    # Connect to the first camera
    cam = Camera(cameras[0])
    print(f"Firmware Version: {cam.get_firmware_version()}")

    # Setup Stream Mode (0 = SingleFrameMode) & Resolution
    cam.set_stream_mode(0)
    cam.init()
    cam.set_bin_mode(1, 1)  # 1x1 Binning
    cam.set_resolution(0, 0, 3864, 2192)  # ROI (start_x, start_y, width, height)

    # Set Exposure (in microseconds), Gain, Offset
    cam.set_exposure(100000)  # 100,000 us = 100 ms
    cam.set_gain(50)
    cam.set_offset(10)

    # Trigger Single Frame Capture
    print("Triggering single frame exposure...")
    start_time = time.time()

    # capture_single_frame returns a 2D numpy.ndarray (height, width)
    frame = cam.capture_single_frame()
    elapsed = time.time() - start_time

    if frame is not None:
        print(f"Single frame captured in {elapsed:.2f}s!")
        print(f"Shape: {frame.shape}, dtype: {frame.dtype}, min: {frame.min()}, max: {frame.max()}")

    # Clean Close
    cam.close()
    print("Camera closed cleanly.")
```

---

## 📚 API Reference

### Global Functions

- `init_sdk()`: Initialize QHYCCD SDK runtime resources.
- `scan_cameras() -> List[str]`: Scan USB bus and return a list of connected camera IDs.

### `Camera` Class

| Method | Parameters | Description |
| :--- | :--- | :--- |
| `Camera(camera_id)` | `camera_id: str` | Constructor. Open camera instance by ID string. |
| `init()` | None | Initialize camera hardware and registers. |
| `set_stream_mode(mode)` | `mode: int` (0: SingleFrame, 1: LiveVideo) | Set camera operational stream mode. |
| `set_exposure(us)` | `us: int` (microseconds) | Set exposure time in microseconds. |
| `set_gain(gain)` | `gain: float` | Set sensor gain value. |
| `set_offset(offset)` | `offset: float` | Set sensor offset value. |
| `set_bin_mode(bin_x, bin_y)` | `bin_x: int, bin_y: int` | Set hardware binning mode (e.g. 1x1, 2x2). |
| `set_resolution(x, y, w, h)` | `x, y, width, height: int` | Set ROI resolution and readout offsets. |
| `begin_live()` | None | Start live streaming mode. |
| `get_live_frame()` | None | Fetch latest frame as 2D `numpy.ndarray`. Releases GIL. |
| `stop_live()` | None | Stop live video streaming mode. |
| `exp_single_frame()` | None | Trigger single frame exposure. |
| `get_single_frame()` | None | Fetch single frame exposure data as `numpy.ndarray`. |
| `capture_single_frame()` | None | Convenience method: trigger & fetch single frame as `numpy.ndarray`. |
| `get_firmware_version()` | None | Get hardware firmware version string. |
| `close()` | None | Safely release camera handle and USB resources. |

---

## 🛠️ Build from Source

Requirements: **Rust toolchain** (`cargo`) and **Python** with `maturin`.

```bash
git clone https://github.com/xulingming/qhyccd.git
cd qhyccd

# Install maturin
pip install maturin

# Build and install locally into current python environment
maturin develop

# Build wheel package
maturin build --release
```

---

## 📄 License

Dual-licensed under **MIT** or **Apache-2.0**.

