Source code for pzp_hardware.thorlabs.powermeter

# This file is a part of pzp-hardware, a library of laboratory hardware support Pieces
# for the puzzlepiece GUI & automation framework. Check out https://pzp-hardware.readthedocs.io
# Licensed under the Apache License 2.0 - https://github.com/jdranczewski/pzp-hardware/blob/main/LICENSE

r"""
:module_title:`ThorLabs Powermeter`

Pieces for interacting with
`ThorLabs powermeter consoles <https://www.thorlabs.com/navigation.cfm?guide_id=37>`__
using the `puzzlepiece <https://puzzlepiece.readthedocs.io>`__ framework.

Example usage (see :ref:`getting-started` for more details on using Pieces in general)::

    import puzzlepiece as pzp
    from pzp_hardware.thorlabs import powermeter

    app = pzp.QApp()
    puzzle = pzp.Puzzle(debug=False)
    puzzle.add_piece("powermeter", powermeter.Piece, row=0, column=0)
    puzzle.show()
    app.exec()

Installation
------------
* Install the ThorLabs Optical Power Monitor (OPM) software from https://www.thorlabs.com/software_pages/ViewSoftwarePage.cfm?Code=OPM
* Make sure your powermeter console is using the ``TLPM`` or ``WinUSB`` driver, this can be done using the "Driver Switcher" app
  installed with the OPM software.
* The OPM software will install relevant files in two locations, a 32bit and a 64bit one.

  - Locate the TLPM Python examples directory, usually ``C:\Program Files (x86)\IVI Foundation\VISA\WinNT\TLPM\Examples\Python``
    (this will always be in the 32bit installation folder, so 'Program Files (x86)').
  - Locate the TLPM DLL directory, which will be either in 'Program Files' on 64 bit systems, or in 'Program Files (x86)' on 32bit
    systems. It will be of the form ``C:\Program Files\IVI Foundation\VISA\Win64\Bin``.

* Copy the paths of the above directories and paste them into the terminal when prompted at runtime.

Requirements
------------
.. pzp_requirements:: pzp_hardware.thorlabs.powermeter

Available Pieces
----------------
"""

import puzzlepiece as pzp
from puzzlepiece.extras import hardware_tools as pht

#MARK: Piece
[docs] class Piece(pzp.Piece): """ Main ThorLabs powermeter Piece. .. image:: ../images/pzp_hardware.thorlabs.powermeter.Piece.png """ def define_params(self): @pzp.param.dropdown(self, "device", "") def list_devices(): if self.puzzle.debug: return None # Based on the TLPM "PowermeterSample.py" tlPM = self._TLPM_class() try: # How many devices can we detect? deviceCount = pht.c.c_uint32() tlPM.findRsrc(pht.c.byref(deviceCount)) # Iterate over the devices and yield their ids resourceName = pht.c.create_string_buffer(1024) for i in range(0, deviceCount.value): tlPM.getRsrcName(pht.c.c_int(i), resourceName) yield resourceName.value.decode() tlPM.close() except NameError: # A NameError is raised if no powermeters connected to the system tlPM.close() return None @pzp.param.connect(self) def connect(): if self.puzzle.debug: return 1 if self._ensure(capture_exception=True): # Close any existing instance self.tlPM.close() # Instance the TLPM class self.tlPM = self._TLPM_class() # Connect to the meter specified by the "device" param resourceName = pht.c.create_string_buffer(self["device"].value.encode()) try: result = self.tlPM.open(resourceName, True, True) except NameError as e: result = e # If the connection was not succesful, clean up and raise an exception if result: self.tlPM.close() del self.tlPM raise Exception(f"Powermeter init failed with code {result}") return 1 @pzp.param.disconnect(self) def disconnect(): if self.puzzle.debug: return 0 if self._ensure(capture_exception=True): self.tlPM.close() del self.tlPM return 0 @pzp.param.spinbox(self, 'wavelength', 633, visible=False) @self._ensure def set_wavelength(value): if self.puzzle.debug: return value self.tlPM.setWavelength(pht.c.c_double(value)) # Don't return a value, so that the getter gets called # to double-check (values outside the correct range won't be set) @set_wavelength.set_getter(self) @self._ensure def get_wavelength(): if self.puzzle.debug: return set_wavelength.value or 500 value = pht.c.c_double() self.tlPM.getWavelength(0, pht.c.byref(value)) return value.value @pzp.param.spinbox(self, 'avg_time', 10., visible=False) @self._ensure def set_avg_time(value): if self.puzzle.debug: return value self.tlPM.setAvgTime(pht.c.c_double(value*1e-3)) @set_avg_time.set_getter(self) @self._ensure def get_avg_time(): if self.puzzle.debug: return set_avg_time.value or 1 value = pht.c.c_double() self.tlPM.getAvgTime(0, pht.c.byref(value)) return value.value*1e3 @pzp.param.readout(self, "power", "{:.2e}") @self._ensure def read_power(): if self.puzzle.debug: return 0 power = pht.c.c_double() self.tlPM.measPower(pht.c.byref(power)) return power.value def define_actions(self): @pzp.action.define(self, 'Zero') @self._ensure def zero(self): if self.puzzle.debug: return self.tlPM.startDarkAdjust() pzp.action.settings(self) #MARK: API setup def setup(self): pht.add_path_directory( pht.config( "tlmp_examples_python_directory", default=r"C:\Program Files (x86)\IVI Foundation\VISA\WinNT\TLPM\Examples\Python", description="the Python files only exist in the (x86) IVI installation directory", validator=pht.validator_path_exists ) ) pht.requirements({"TLPM": { "url": "https://pzp-hardware.readthedocs.io/en/latest/auto/pzp_hardware.thorlabs.powermeter.html#installation" }}) from TLPM import TLPM pht.add_dll_directory( pht.config( "tlmp_dll_directory", default=r"C:\Program Files\IVI Foundation\VISA\Win64\Bin", description="look for the DLL matching your system - 32 bit in 'Program Files (x86)', 64 bit in 'Program Files'", validator=pht.validator_path_exists ) ) self._TLPM_class = TLPM @pzp.piece.ensurer def _ensure(self): if not self.puzzle.debug and not hasattr(self, "tlPM"): raise Exception("Powermeter not connected") return
if __name__ == "__main__": app = pzp.QApp() puzzle = pzp.Puzzle(name="Powermeter", debug=pht.debug_prompt()) puzzle.add_piece("powermeter", Piece, 0, 0) puzzle.show() app.exec()