Module matisse_controller.shamrock_ple.shamrock

Source code
from ctypes import *
from bidict import bidict

from matisse_controller.shamrock_ple.utils import load_lib


# TODO: Note that for some reason the Shamrock will not be found unless SOLIS is installed. Probably a driver issue :(
class Shamrock:
    LIBRARY_NAME = 'ShamrockCIF.dll'
    DEVICE_ID = c_int(0)

    # Below constants are calculated using a pre-calibrated wavelength axis on SOLIS (end - start) / 1024
    GRATINGS_NM_PER_PIXEL = {
        300: 0.116523437,
        1200: 0.0273535156,
        1799: 0.01578125
    }

    # The offset to add, in nanometers, to data in a spectrum taken with a given grating.
    # These tend to change over time, so update accordingly. Offset is abs(pixel shift) * (nm per pixel)
    GRATINGS_OFFSET_NM = {
        300: 1.325,  # -11.4 px, last calibrated Aug 2019
        1200: 0.109,  # -4 px, last calibrated Apr 2015
        1799: 0.470  # -29.8 px, last calibrated Dec 2018
    }

    def __init__(self):
        try:
            self.lib = load_lib(Shamrock.LIBRARY_NAME)
            self.lib.ShamrockInitialize()

            num_devices = c_int()
            self.lib.ShamrockGetNumberDevices(pointer(num_devices))
            assert num_devices.value > 0, 'No spectrometer found.'

            self.gratings = bidict()
            self.setup_grating_info()
        except OSError as err:
            raise RuntimeError('Unable to initialize Andor Shamrock API.') from err

    def __del__(self):
        self.shutdown()

    def setup_grating_info(self):
        """
        Fill out the bidirectional dictionary responsible for holding information about spectrometer gratings.
        """
        number = c_int()
        self.lib.ShamrockGetNumberGratings(Shamrock.DEVICE_ID, pointer(number))
        blaze = create_string_buffer(8)
        for index in range(1, number.value + 1):
            lines, home, offset = c_float(), c_int(), c_int()
            self.lib.ShamrockGetGratingInfo(Shamrock.DEVICE_ID, c_int(index), pointer(lines), blaze, pointer(home),
                                            pointer(offset))
            self.gratings[round(lines.value)] = index

    def get_grating_grooves(self) -> int:
        """
        Returns
        -------
        int
            the number of grooves in the current spectrometer grating
        """
        index = c_int()
        self.lib.ShamrockGetGrating(Shamrock.DEVICE_ID, pointer(index))
        return self.gratings.inverse[index.value]

    def set_grating_grooves(self, num_grooves: int):
        """
        Use the spectrometer grating with the specified number of grooves.

        Parameters
        ----------
        num_grooves
            the desired number of grooves
        """
        if num_grooves != self.get_grating_grooves():
            self.lib.ShamrockSetGrating(Shamrock.DEVICE_ID, c_int(self.gratings[num_grooves]))

    def get_center_wavelength(self) -> float:
        """
        Returns
        -------
        float
            the current center wavelength of the spectrometer
        """
        wavelength = c_float()
        self.lib.ShamrockGetWavelength(Shamrock.DEVICE_ID, pointer(wavelength))
        return wavelength.value

    def set_center_wavelength(self, wavelength: float):
        """
        Set the spectrometer wavelength at the specified value.

        Parameters
        ----------
        wavelength
            the desired center wavelength to set
        """
        if wavelength != self.get_center_wavelength():
            self.lib.ShamrockSetWavelength(Shamrock.DEVICE_ID, c_float(wavelength))

    def shutdown(self):
        """Run Shamrock-related cleanup and shutdown procedures."""
        self.lib.ShamrockClose()

Classes

class Shamrock
Source code
class Shamrock:
    LIBRARY_NAME = 'ShamrockCIF.dll'
    DEVICE_ID = c_int(0)

    # Below constants are calculated using a pre-calibrated wavelength axis on SOLIS (end - start) / 1024
    GRATINGS_NM_PER_PIXEL = {
        300: 0.116523437,
        1200: 0.0273535156,
        1799: 0.01578125
    }

    # The offset to add, in nanometers, to data in a spectrum taken with a given grating.
    # These tend to change over time, so update accordingly. Offset is abs(pixel shift) * (nm per pixel)
    GRATINGS_OFFSET_NM = {
        300: 1.325,  # -11.4 px, last calibrated Aug 2019
        1200: 0.109,  # -4 px, last calibrated Apr 2015
        1799: 0.470  # -29.8 px, last calibrated Dec 2018
    }

    def __init__(self):
        try:
            self.lib = load_lib(Shamrock.LIBRARY_NAME)
            self.lib.ShamrockInitialize()

            num_devices = c_int()
            self.lib.ShamrockGetNumberDevices(pointer(num_devices))
            assert num_devices.value > 0, 'No spectrometer found.'

            self.gratings = bidict()
            self.setup_grating_info()
        except OSError as err:
            raise RuntimeError('Unable to initialize Andor Shamrock API.') from err

    def __del__(self):
        self.shutdown()

    def setup_grating_info(self):
        """
        Fill out the bidirectional dictionary responsible for holding information about spectrometer gratings.
        """
        number = c_int()
        self.lib.ShamrockGetNumberGratings(Shamrock.DEVICE_ID, pointer(number))
        blaze = create_string_buffer(8)
        for index in range(1, number.value + 1):
            lines, home, offset = c_float(), c_int(), c_int()
            self.lib.ShamrockGetGratingInfo(Shamrock.DEVICE_ID, c_int(index), pointer(lines), blaze, pointer(home),
                                            pointer(offset))
            self.gratings[round(lines.value)] = index

    def get_grating_grooves(self) -> int:
        """
        Returns
        -------
        int
            the number of grooves in the current spectrometer grating
        """
        index = c_int()
        self.lib.ShamrockGetGrating(Shamrock.DEVICE_ID, pointer(index))
        return self.gratings.inverse[index.value]

    def set_grating_grooves(self, num_grooves: int):
        """
        Use the spectrometer grating with the specified number of grooves.

        Parameters
        ----------
        num_grooves
            the desired number of grooves
        """
        if num_grooves != self.get_grating_grooves():
            self.lib.ShamrockSetGrating(Shamrock.DEVICE_ID, c_int(self.gratings[num_grooves]))

    def get_center_wavelength(self) -> float:
        """
        Returns
        -------
        float
            the current center wavelength of the spectrometer
        """
        wavelength = c_float()
        self.lib.ShamrockGetWavelength(Shamrock.DEVICE_ID, pointer(wavelength))
        return wavelength.value

    def set_center_wavelength(self, wavelength: float):
        """
        Set the spectrometer wavelength at the specified value.

        Parameters
        ----------
        wavelength
            the desired center wavelength to set
        """
        if wavelength != self.get_center_wavelength():
            self.lib.ShamrockSetWavelength(Shamrock.DEVICE_ID, c_float(wavelength))

    def shutdown(self):
        """Run Shamrock-related cleanup and shutdown procedures."""
        self.lib.ShamrockClose()

Class variables

var DEVICE_ID
var GRATINGS_NM_PER_PIXEL
var GRATINGS_OFFSET_NM
var LIBRARY_NAME

Methods

def get_center_wavelength(self)

Returns

float
the current center wavelength of the spectrometer
Source code
def get_center_wavelength(self) -> float:
    """
    Returns
    -------
    float
        the current center wavelength of the spectrometer
    """
    wavelength = c_float()
    self.lib.ShamrockGetWavelength(Shamrock.DEVICE_ID, pointer(wavelength))
    return wavelength.value
def get_grating_grooves(self)

Returns

int
the number of grooves in the current spectrometer grating
Source code
def get_grating_grooves(self) -> int:
    """
    Returns
    -------
    int
        the number of grooves in the current spectrometer grating
    """
    index = c_int()
    self.lib.ShamrockGetGrating(Shamrock.DEVICE_ID, pointer(index))
    return self.gratings.inverse[index.value]
def set_center_wavelength(self, wavelength)

Set the spectrometer wavelength at the specified value.

Parameters

wavelength
the desired center wavelength to set
Source code
def set_center_wavelength(self, wavelength: float):
    """
    Set the spectrometer wavelength at the specified value.

    Parameters
    ----------
    wavelength
        the desired center wavelength to set
    """
    if wavelength != self.get_center_wavelength():
        self.lib.ShamrockSetWavelength(Shamrock.DEVICE_ID, c_float(wavelength))
def set_grating_grooves(self, num_grooves)

Use the spectrometer grating with the specified number of grooves.

Parameters

num_grooves
the desired number of grooves
Source code
def set_grating_grooves(self, num_grooves: int):
    """
    Use the spectrometer grating with the specified number of grooves.

    Parameters
    ----------
    num_grooves
        the desired number of grooves
    """
    if num_grooves != self.get_grating_grooves():
        self.lib.ShamrockSetGrating(Shamrock.DEVICE_ID, c_int(self.gratings[num_grooves]))
def setup_grating_info(self)

Fill out the bidirectional dictionary responsible for holding information about spectrometer gratings.

Source code
def setup_grating_info(self):
    """
    Fill out the bidirectional dictionary responsible for holding information about spectrometer gratings.
    """
    number = c_int()
    self.lib.ShamrockGetNumberGratings(Shamrock.DEVICE_ID, pointer(number))
    blaze = create_string_buffer(8)
    for index in range(1, number.value + 1):
        lines, home, offset = c_float(), c_int(), c_int()
        self.lib.ShamrockGetGratingInfo(Shamrock.DEVICE_ID, c_int(index), pointer(lines), blaze, pointer(home),
                                        pointer(offset))
        self.gratings[round(lines.value)] = index
def shutdown(self)

Run Shamrock-related cleanup and shutdown procedures.

Source code
def shutdown(self):
    """Run Shamrock-related cleanup and shutdown procedures."""
    self.lib.ShamrockClose()