Metadata-Version: 2.4
Name: pyhantek6022
Version: 0.0.1
Summary: Hantek 6022 Python Interface Library
Author-email: Thomas Spielauer <pypipackages01@tspi.at>
Project-URL: Homepage, https://github.com/tspspi/pyhantek6022
Platform: FreeBSD
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: POSIX :: BSD :: FreeBSD
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE.md
Requires-Dist: pylabdevs-tspspi>=0.0.16
Requires-Dist: pyusb>=1.2.1
Requires-Dist: numpy>=1.26.4
Requires-Dist: matplotlib>=3.8.0
Provides-Extra: gui
Requires-Dist: FreeSimpleGUI>=5.1.1; extra == "gui"
Dynamic: license-file

# Hantek6022 Python Interface

## Introduction

`pyhantek6022` provides an implementation of the `Oscilloscope` base class of [pylabdevs](https://github.com/tspspi/pylabdevs) to access the Hantek `DSO6022BE` digital storace oscilloscope from Python applications.

Even though this application provides a simple GUI based on [FreeSimpleGUI](https://freesimplegui.readthedocs.io/en/latest/), I would stronlgy recommend using [OpenHantek6022](https://github.com/OpenHantek/OpenHantek6022) for utilizing the oscilloscope in interactive mode. This library is designed to be used with experimental automation scripts that are built around [pylabdevs](https://github.com/tspspi/pylabdevs).

Triggered acquisitions return one visible screen width. In `AUTO`, `NORMAL` and `SINGLE` sweep mode the software trigger uses the configured trigger source, level, slope and position.

## License

The package is licensed unter a 3-clause BSD license (see [LICENSE.md](./LICENSE.md); compact: attribution is required, else do what you want with the code for any usage without any liability of the original author)

## Installation

The library can be installed from the PyPi package repository:

```sh
pip install pyhantek6022
```

It is also possible to deploy the application from the checked out repository for development:

```
git clone git@github.com:tspspi/pyhantek6022.git
cd pyhantek6022
pip install -e .
```

## Firmware

Due to licensing and lack of own firmware, the package depends on external firmware blobs. The runtime firmware loader does not use any bundled firmware files. Instead it looks into:

`~/.config/pyhantek6022/firmware/`

The directory may contain:

* `index.json` containing one object or a list of objects
* one `.json` metadata file per firmware image
* the referenced `.hex` firmware files

A sample metadata file is included at `examples/firmware_index.sample.json`

The built in known devices, using firmware blobs from [OpenHantek6022](https://github.com/OpenHantek/OpenHantek6022) are:

* Hantek6022BE
* Hantek6022BL
* Voltcraft DSO-2020

Minimal example for `index.json`:

```json
[
  {
    "name": "Hantek6022BE",
    "idVendor_NoFW": 1204,
    "idProduct_NoFW": 24610,
    "fwtype": "cypress_fx2",
    "idVendor_FW": 1205,
    "idProduct_FW": 24610,
    "firmware_file": "dso6022be-firmware.hex"
  }
]
```

The firmware `.hex` files can be copied from an [OpenHantek6022](https://github.com/OpenHantek/OpenHantek6022) installation into that directory.

### Firmware Upload

The currently implemented devices are based on [Cypress FX2 microcontrollers](https://www.infineon.com/products/universal-serial-bus/usb-2-0-peripheral-controllers/ez-usb-fx2lp-fx2g2-usb-2-0), which lack persistent storage - except for the vendor- and device ID - and thus always power up in unpersonalized state. Before usage an firmware image has to be uploaded to the device, which also changes its USB device ID. To scan for uninitialized devices and upload firmware to all of them:

```python
from pyhantek6022.firmwareupload import FirmwareUploader

uploaded = FirmwareUploader.init_all()
print(uploaded)
```

After uploading firmware, wait a short moment for the device to attach again and get initialized by the USB driveer framework of the operating system and scan again for initialized devices:

```python
import time

from pyhantek6022.dso6022be import get_dsos
from pyhantek6022.firmwareupload import FirmwareUploader

uploaded = FirmwareUploader.init_all()
if uploaded:
    time.sleep(3)

dsos = get_dsos()
print(dsos)
```

To inspect only devices that still need firmware:

```python
from pyhantek6022.firmwareupload import FirmwareUploader

uploader = FirmwareUploader()
pending = uploader.find_devs()
print(pending)
```

## Calibration

Optional calibration data is read from:

`~/.config/pyhantek6022/modelDSO6022.conf`

The format follows the simple INI layout used by the [OpenHantek6022](https://github.com/OpenHantek/OpenHantek6022) program:

- `[gain]` for gain correction values
- `[offset]` for offset correction values

## Usage

### Basic Example

```python
from pyhantek6022.dso6022be import DSO6022BE, get_dsos

dsos = get_dsos()
dso = DSO6022BE(dsos[0])

dso.set_channel_enable(0, True)
dso.set_channel_enable(1, False)
dso.set_channel_scale(0, 1.0)
dso.set_timebase_scale(1e-3)

data = dso.query_waveform(0)
```

Automatic firmware upload and scan can be combined with normal device opening:

```python
import time

from pyhantek6022.dso6022be import DSO6022BE, get_dsos
from pyhantek6022.firmwareupload import FirmwareUploader

dsos = get_dsos()
if not dsos:
    uploaded = FirmwareUploader.init_all()
    if uploaded:
        time.sleep(3)
    dsos = get_dsos()

dso = DSO6022BE(dsos[0])
data = dso.query_waveform(0)
```

The device object can also be used through the shared connection and context management helpers:

```python
dso = DSO6022BE(dsos[0])
dso.connect()
data = dso.query_waveform(0)
dso.disconnect()

with DSO6022BE(dsos[0]) as dso:
    data = dso.query_waveform(0)
```

### Trigger Configuration

The following trigger helpers are available through the shared oscilloscope API:

- `set_trigger_source(channel, smooth = False)`
- `get_trigger_source()`
- `set_trigger_level(channel, level)`
- `get_trigger_level(channel)`
- `set_trigger_slope(slope)`
- `get_trigger_slope()`
- `set_trigger_position(position)`
- `get_trigger_position()`
- `force_trigger()`

When `stats = [...]` is requested, the shared `pylabdevs` statistics polyfills are available as well. Single channel queries still return `x` and `y` outwardly, multi channel queries keep `x`, `y0` and `y1`.

For single channel queries the statistics dictionaries alias the internal channel name, for example:

- `means["y1_avg"]`, `means["y1_std"]`
- `fft["y1"]`, `fft["y1_real"]`
- `ifft["y1"]`

For multi channel queries the shared `pylabdevs` naming is used directly:

- `means["y0_avg"]`, `means["y1_avg"]`
- `fft["y0_real"]`, `fft["y1_real"]`
- `correlation["y0y1"]`

## GUI

The optional (minimal) GUI can be installed using:

```sh
pip install 'pyhantek6022[gui]'
```

Keep in mind this in no way substitutes a proper application like [OpenHantek6022](https://github.com/OpenHantek/OpenHantek6022). The application can be started with

```sh
hantekgui
```

At startup the GUI scans for uninitialized USB devices, - uploads firmware to all matching devices, - waits for re-enumeration and shows a device selection list for initialized scopes. After connecting and starting aquisition it provides basic control an data visualization.

## Dependencies

This package depends on:

* The [pylabdevs](https://github.com/tspspi/pylabdevs) base class library
* [pyusb](https://pypi.org/project/pyusb/) as a Python wrapper for [libusb](https://github.com/libusb/libusb) and or [OpenUSB](https://sourceforge.net/projects/openusb/)
* [numpy](https://numpy.org/) to wrap data in native arrays
* For the GUI and examples only:
   * [matplotlib](https://matplotlib.org/) for visualization inside the GUI and generation of plots
   * [FreeSimpleGUI](https://freesimplegui.readthedocs.io/en/latest/) for a very simple graphical user interface (only installed if the optional `gui` subpackage is installed

