Metadata-Version: 2.5
Name: plesty-taiko-pdl-m1
Version: 0.1.1
Summary: Plesty PicoQuant Taiko PDL M1 picosecond laser driver device.
Author-email: Yunshuang Yuan <yuanyunshuang@gmail.com>
Maintainer-email: Plesty Development Team <plesty.dev@example.com>
License-Expression: LGPL-3.0-or-later
License-File: LICENSE
License-File: LICENSES/LGPL-3.0-or-later.txt
Requires-Python: >=3.12
Requires-Dist: plesty-lib>=0.5.2
Description-Content-Type: text/markdown

# Plesty Taiko PDL M1 Device

A PLESTY device module for the PicoQuant Taiko PDL M1: a laser driver that powers a
pulsed/continuous-wave (cw) diode laser head. The module sets the pulse repetition rate and the
power, switches between pulsed and cw operation, and reads the lock state, the calibrated
average power and the temperatures.

The module is built so the laser never stays on by accident: see [Safety](#safety).

## Supported hardware

The Taiko PDL M1 driver with a laser head connected, over USB. It talks to the driver through
PicoQuant's own library, `PDLM_Lib.dll`, so it runs on Windows. Everything else, including the
simulator (`mock`), works without the library or the hardware.

## Installation

```bash
git clone https://gitlab.com/plesty/hub/devices/picoquant/plesty-taiko-pdl-m1.git
cd plesty-taiko-pdl-m1
uv sync
```

### PicoQuant's software and `PDLM_Lib.dll`

`PDLM_Lib.dll` is PicoQuant's. Its license forbids passing it on, so it is **not** in this
repository and never will be. Install PicoQuant's Taiko software yourself (from PicoQuant); it
puts the library on the PC.

The module runs in 64-bit Python and needs the **64-bit** library. It looks for it at

```
C:\ProgramData\Taiko PDL M1\API\x64\PDLM_Lib.dll
```

If yours is elsewhere, set `TAIKO_DLL_PATH` (in `.env` or the environment) to the file or its
folder. Many lab scripts use the 32-bit copy (`...\API\Win32\`) with 32-bit Python; the module
refuses that file and says so, because a 64-bit program cannot load it.

## Quick start

Copy `.env.example` to `.env`, then connect the driver by USB, switch it on, and close
PicoQuant's own Taiko program (only one program can hold the driver).

```python
from plesty.taiko_pdl_m1 import Device

# address: empty = the first Taiko found, "0".."7" = a USB index, a serial number,
# or "mock" = the simulator
with Device(address="mock") as laser:
    laser.write("laser_mode", "pulsed")
    laser.write("repetition_rate", 10_000_000)  # Hz
    laser.emission_on()  # releases the lock, power stays 0
    laser.write("power_permille", 100)  # 10 % of the maximum at this rate
    print(laser.query("power"))  # calibrated average power, mW
    laser.emission_off()  # power 0, then lock
```

As a server for experiments: `uv run python -m plesty.taiko_pdl_m1` (port from
`TAIKO_TCP_PORT`, default 5555).

## Safety

- **Power 0 before every lock.** With the laser locked the driver reads 0 and refuses power
  changes, but it keeps the setting and brings it back when the lock is released. So the module
  sets the power to 0 first, and after each unlock reads the power back and sets a restored
  value to 0.
- **Locked on disconnect.** `disconnect` (also when the server stops) sets power 0 and engages
  the software lock before it closes the driver.
- **A driver found emitting is secured.** Connecting to a driver that is unlocked with power
  above 0 (for example after a killed server) sets power 0 and the lock, with a warning.
- **Key and interlock cannot be overridden.** The software lock is separate from the key switch
  and the interlock connector; the module can release only its own lock. `locked` shows the
  overall state, `emission_on` reports why it is still locked.
- `power_permille` is refused while locked and on an external trigger; a `laser_mode` switch is
  refused while locked (call `emission_on` first); writing `soft_lock` goes through
  `emission_on` / `emission_off`.

## Reference

Power is given in permille (thousandths) of the maximum, which depends on the repetition rate;
average power in linear power mode grows with the rate at a fixed permille.

| Parameter | Type | Unit / range | Access |
|---|---|---|---|
| `laser_mode` | str | `cw`, `pulsed` | read/write |
| `repetition_rate` | int | Hz, 1 to 100 000 000 | read/write |
| `power_permille` | int | permille, 0 to 1000 | read/write |
| `power` | float | mW, calibrated average | read |
| `soft_lock` | bool | the module's own lock | read/write |
| `locked` | bool | any lock active (soft, key, interlock, safety) | read |
| `trigger_mode` | str | `internal`, `external_falling`, `external_rising` | read |
| `pulse_power_table` | str | `linear`, `max_power` | read |
| `wavelength` | float | nm, estimate from calibration | read |
| `head_temperature` | float | degC | read |
| `target_temperature` | float | degC | read |
| `case_temperature` | float | degC | read |

| Operation | What it does |
|---|---|
| `scan_devices` | List the drivers on USB without opening them |
| `get_info` | Identify the driver and head and give the head's limits |
| `get_status` | Decode the status word: locks, head, temperatures |
| `read_errors` | Drain the driver's error queue |
| `emission_on` | Release the soft lock; power stays 0 |
| `emission_off` | Power 0, then the soft lock |

## Communication

USB through `PDLM_Lib.dll`, driver index 0 to 7 (up to eight drivers). Environment, see
`.env.example`: `TAIKO_ADDRESS`, `TAIKO_DLL_PATH`, `TAIKO_TCP_PORT`.

## Field test

`tests/field_test.py` (generic gates, never turns the laser on) and `tests/bench_questions.py`
(unlocks the laser briefly at up to 5 % power; run it with the beam blocked or on a power
meter) are run by hand on the PC with the driver; see `docs/index.md`.
