Source code for flicamera.lib

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# @Author: José Sánchez-Gallego (gallegoj@uw.edu)
# @Date: 2019-12-18
# @Filename: lib.py
# @License: BSD 3-clause (http://www.opensource.org/licenses/BSD-3-Clause)

# Heavily copied from Craig Wm. Versek's code at http://bit.ly/2M6ESHq.

from __future__ import annotations

import ctypes
import logging
import os
import pathlib
from ctypes import (
    POINTER,
    byref,
    c_char_p,
    c_double,
    c_int,
    c_long,
    c_size_t,
    c_ulong,
    c_void_p,
)
from functools import partial

from typing import Callable, Dict, Optional, Tuple

import numpy


__ALL__ = ["LibFLI", "FLIWarning", "FLIError", "chk_err"]


c_double_p = POINTER(c_double)
c_long_p = POINTER(c_long)

FLI_INVALID_DEVICE = -1


flidev_t = c_long
flidomain_t = c_long

FLIDOMAIN_NONE = 0x00
FLIDOMAIN_PARALLEL_PORT = 0x01
FLIDOMAIN_USB = 0x02
FLIDOMAIN_SERIAL = 0x03
FLIDOMAIN_INET = 0x04
FLIDOMAIN_SERIAL_19200 = 0x05
FLIDOMAIN_SERIAL_1200 = 0x06

FLIDEVICE_NONE = 0x000
FLIDEVICE_CAMERA = 0x100
FLIDEVICE_FILTERWHEEL = 0x200
FLIDEVICE_FOCUSER = 0x300
FLIDEVICE_HS_FILTERWHEEL = 0x0400
FLIDEVICE_RAW = 0x0F00
FLIDEVICE_ENUMERATE_BY_CONNECTION = 0x8000


# The frame type for an FLI CCD camera device.

fliframe_t = c_long

FLI_FRAME_TYPE_NORMAL = 0
FLI_FRAME_TYPE_DARK = 1
FLI_FRAME_TYPE_FLOOD = 2
FLI_FRAME_TYPE_RBI_FLUSH = FLI_FRAME_TYPE_FLOOD | FLI_FRAME_TYPE_DARK


# The gray-scale bit depth for an FLI camera device.

flibitdepth_t = c_long

FLI_MODE_8BIT = flibitdepth_t(0)
FLI_MODE_16BIT = flibitdepth_t(1)


# Type used for shutter operations for an FLI camera device.

flishutter_t = c_long

FLI_SHUTTER_CLOSE = 0x0000
FLI_SHUTTER_OPEN = 0x0001
FLI_SHUTTER_EXTERNAL_TRIGGER = 0x0002
FLI_SHUTTER_EXTERNAL_TRIGGER_LOW = 0x0002
FLI_SHUTTER_EXTERNAL_TRIGGER_HIGH = 0x0004
FLI_SHUTTER_EXTERNAL_EXPOSURE_CONTROL = 0x0008


# Type used for background flush operations for an FLI camera device.

flibgflush_t = c_long

FLI_BGFLUSH_STOP = 0x0000
FLI_BGFLUSH_START = 0x0001


# Type used to determine which temperature channel to read.

flichannel_t = c_long

FLI_TEMPERATURE_INTERNAL = 0x0000
FLI_TEMPERATURE_EXTERNAL = 0x0001
FLI_TEMPERATURE_CCD = 0x0000
FLI_TEMPERATURE_BASE = 0x0001


# Type specifying library debug levels.

flidebug_t = c_long
flimode_t = c_long
flistatus_t = c_long
flitdirate_t = c_long
flitdiflags_t = c_long


# Status settings

FLI_CAMERA_STATUS_UNKNOWN = 0xFFFFFFFF
FLI_CAMERA_STATUS_MASK = 0x00000003
FLI_CAMERA_STATUS_IDLE = 0x00
FLI_CAMERA_STATUS_WAITING_FOR_TRIGGER = 0x01
FLI_CAMERA_STATUS_EXPOSING = 0x02
FLI_CAMERA_STATUS_READING_CCD = 0x03
FLI_CAMERA_DATA_READY = 0x80000000

FLI_FOCUSER_STATUS_UNKNOWN = 0xFFFFFFFF
FLI_FOCUSER_STATUS_HOMING = 0x00000004
FLI_FOCUSER_STATUS_MOVING_IN = 0x00000001
FLI_FOCUSER_STATUS_MOVING_OUT = 0x00000002
FLI_FOCUSER_STATUS_MOVING_MASK = 0x00000007
FLI_FOCUSER_STATUS_HOME = 0x00000080
FLI_FOCUSER_STATUS_LIMIT = 0x00000040
FLI_FOCUSER_STATUS_LEGACY = 0x10000000

FLI_FILTER_WHEEL_PHYSICAL = 0x100
FLI_FILTER_WHEEL_VIRTUAL = 0
FLI_FILTER_WHEEL_LEFT = FLI_FILTER_WHEEL_PHYSICAL | 0x00
FLI_FILTER_WHEEL_RIGHT = FLI_FILTER_WHEEL_PHYSICAL | 0x01
FLI_FILTER_STATUS_MOVING_CCW = 0x01
FLI_FILTER_STATUS_MOVING_CW = 0x02
FLI_FILTER_POSITION_UNKNOWN = 0xFF
FLI_FILTER_POSITION_CURRENT = 0x200
FLI_FILTER_STATUS_HOMING = 0x00000004
FLI_FILTER_STATUS_HOME = 0x00000080
FLI_FILTER_STATUS_HOME_LEFT = 0x00000080
FLI_FILTER_STATUS_HOME_RIGHT = 0x00000040
FLI_FILTER_STATUS_HOME_SUCCEEDED = 0x00000008

FLIDEBUG_NONE = 0x00
FLIDEBUG_INFO = 0x01
FLIDEBUG_WARN = 0x02
FLIDEBUG_FAIL = 0x04
FLIDEBUG_IO = 0x08
FLIDEBUG_ALL = FLIDEBUG_INFO | FLIDEBUG_WARN | FLIDEBUG_FAIL

FLI_IO_P0 = 0x01
FLI_IO_P1 = 0x02
FLI_IO_P2 = 0x04
FLI_IO_P3 = 0x08

FLI_FAN_SPEED_OFF = 0x00
FLI_FAN_SPEED_ON = 0xFFFFFFFF

FLI_EEPROM_USER = 0x00
FLI_EEPROM_PIXEL_MAP = 0x01

FLI_PIXEL_DEFECT_COLUMN = 0x00
FLI_PIXEL_DEFECT_CLUSTER = 0x10
FLI_PIXEL_DEFECT_POINT_BRIGHT = 0x20
FLI_PIXEL_DEFECT_POINT_DARK = 0x30


_API_FUNCTION_PROTOTYPES = [
    (
        "FLIOpen",
        [POINTER(flidev_t), c_char_p, flidomain_t],
    ),  # (flidev_t *dev, char *name, flidomain_t domain);
    ("FLIClose", [flidev_t]),  # (flidev_t dev);
    ("FLISetDebugLevel", [c_char_p, flidebug_t]),  # (char *host, flidebug_t level);
    ("FLIGetLibVersion", [c_char_p, c_size_t]),  # (char* ver, size_t len);
    (
        "FLIGetModel",
        [flidev_t, c_char_p, c_size_t],
    ),  # (flidev_t dev, char* model, size_t len);
    (
        "FLIGetArrayArea",
        [flidev_t, c_long_p, c_long_p, c_long_p, c_long_p],
    ),  # (flidev_t dev, long* ul_x, long* ul_y,long* lr_x, long* lr_y);
    (
        "FLIGetVisibleArea",
        [flidev_t, c_long_p, c_long_p, c_long_p, c_long_p],
    ),  # (flidev_t dev, long* ul_x, long* ul_y,long* lr_x, long* lr_y);
    ("FLIExposeFrame", [flidev_t]),  # (flidev_t dev);
    ("FLICancelExposure", [flidev_t]),  # (flidev_t dev);
    ("FLIGetExposureStatus", [flidev_t, c_long_p]),  # (flidev_t dev, long *timeleft);
    ("FLISetTemperature", [flidev_t, c_double]),  # (flidev_t dev, double temperature);
    (
        "FLIGetTemperature",
        [flidev_t, c_double_p],
    ),  # (flidev_t dev, double *temperature);
    (
        "FLIGrabRow",
        [flidev_t, c_void_p, c_size_t],
    ),  # (flidev_t dev, void *buff, size_t width);
    (
        "FLIGrabFrame",
        [flidev_t, c_void_p, c_size_t, POINTER(c_size_t)],
    ),  # (flidev_t dev, void* buff, size_t buffsize, size_t* bytesgrabbed);
    (
        "FLIFlushRow",
        [flidev_t, c_long, c_long],
    ),  # (flidev_t dev, long rows, long repeat);
    ("FLISetExposureTime", [flidev_t, c_long]),  # (flidev_t dev, long exptime);
    (
        "FLISetFrameType",
        [flidev_t, fliframe_t],
    ),  # (flidev_t dev, fliframe_t frametype);
    (
        "FLISetImageArea",
        [flidev_t, c_long, c_long, c_long, c_long],
    ),  # (flidev_t dev, long ul_x, long ul_y, long lr_x, long lr_y);
    ("FLISetHBin", [flidev_t, c_long]),  # (flidev_t dev, long hbin);
    ("FLISetVBin", [flidev_t, c_long]),  # (flidev_t dev, long vbin);
    ("FLISetNFlushes", [flidev_t, c_long]),  # (flidev_t dev, long nflushes);
    (
        "FLISetBitDepth",
        [flidev_t, flibitdepth_t],
    ),  # (flidev_t dev, flibitdepth_t bitdepth);
    ("FLIReadIOPort", [flidev_t, c_long_p]),  # (flidev_t dev, long *ioportset);
    ("FLIWriteIOPort", [flidev_t, c_long]),  # (flidev_t dev, long ioportset);
    ("FLIConfigureIOPort", [flidev_t, c_long]),  # (flidev_t dev, long ioportset);
    (
        "FLIControlShutter",
        [flidev_t, flishutter_t],
    ),  # (flidev_t dev, flishutter_t shutter);
    ("FLILockDevice", [flidev_t]),  # (flidev_t dev);
    ("FLIUnlockDevice", [flidev_t]),  # (flidev_t dev);
    (
        "FLIList",
        [flidomain_t, POINTER(POINTER(c_char_p))],
    ),  # (flidomain_t domain, char ***names);
    ("FLIFreeList", [POINTER(c_char_p)]),  # (char **names);
    ("FLISetFilterPos", [flidev_t, c_long]),  # (flidev_t dev, long filter);
    ("FLIGetFilterPos", [flidev_t, c_long_p]),  # (flidev_t dev, long *filter);
    ("FLIGetFilterCount", [flidev_t, c_long_p]),  # (flidev_t dev, long *filter);
    ("FLIStepMotor", [flidev_t, c_long]),  # (flidev_t dev, long steps);
    ("FLIGetStepperPosition", [flidev_t, c_long_p]),  # (flidev_t dev, long *position);
    ("FLIGetHWRevision", [flidev_t, c_long_p]),  # (flidev_t dev, long *hwrev);
    (
        "FLIGetPixelSize",
        [flidev_t, c_double_p, c_double_p],
    ),  # (flidev_t dev, double *pixel_x, double *pixel_y);
    ("FLIGetFWRevision", [flidev_t, c_long_p]),  # (flidev_t dev, long *fwrev);
    ("FLIHomeFocuser", [flidev_t]),  # (flidev_t dev);
    ("FLICreateList", [flidomain_t]),  # (flidomain_t domain);
    ("FLIDeleteList", []),  # (void);
    (
        "FLIListFirst",
        [POINTER(flidomain_t), c_char_p, c_size_t, c_char_p, c_size_t],
    ),  # (flidomain_t *domain, char *filename,size_t fnlen, char *name, size_t namelen);  # noqa E501
    (
        "FLIListNext",
        [POINTER(flidomain_t), c_char_p, c_size_t, c_char_p, c_size_t],
    ),  # (flidomain_t *domain, char *filename,size_t fnlen, char *name, size_t namelen); # noqa E501
    (
        "FLIControlBackgroundFlush",
        [flidev_t, flibgflush_t],
    ),  # (flidev_t dev, flibgflush_t bgflush);
    ("FLISetDAC", [flidev_t, c_ulong]),  # (flidev_t dev, unsigned long dacset);
    ("FLIGetStepsRemaining", [flidev_t, c_long_p]),  # (flidev_t dev, long *steps);
    ("FLIStepMotorAsync", [flidev_t, c_long]),  # (flidev_t dev, long steps);
    (
        "FLIReadTemperature",
        [flidev_t, flichannel_t, c_double_p],
    ),  # (flidev_t dev,flichannel_t channel, double *temperature);
    ("FLIGetFocuserExtent", [flidev_t, c_long_p]),  # (flidev_t dev, long *extent);
    (
        "FLIUsbBulkIO",
        [flidev_t, c_int, c_void_p, c_long_p],
    ),  # (flidev_t dev, int ep, void *buf, long *len);
    ("FLIGetCoolerPower", [flidev_t, c_double_p]),  # (flidev_t dev, double *power);
    ("FLIGetDeviceStatus", [flidev_t, c_long_p]),  # (flidev_t dev, long *status);
    (
        "FLIGetCameraModeString",
        [flidev_t, flimode_t, c_char_p, c_size_t],
    ),  # (flidev_t dev, flimode_t mode_index, char *mode_string, size_t siz);
    (
        "FLIGetCameraMode",
        [flidev_t, POINTER(flimode_t)],
    ),  # (flidev_t dev, flimode_t *mode_index);
    (
        "FLISetCameraMode",
        [flidev_t, flimode_t],
    ),  # (flidev_t dev, flimode_t mode_index);
    ("FLIHomeDevice", [flidev_t]),  # (flidev_t dev);
    (
        "FLIGrabVideoFrame",
        [flidev_t, c_void_p, c_size_t],
    ),  # (flidev_t dev, void *buff, size_t size);
    ("FLIStopVideoMode", [flidev_t]),  # (flidev_t dev);
    ("FLIStartVideoMode", [flidev_t]),  # (flidev_t dev);
    (
        "FLIGetSerialString",
        [flidev_t, c_char_p, c_size_t],
    ),  # (flidev_t dev, char* serial, size_t len);
    ("FLIEndExposure", [flidev_t]),  # (flidev_t dev);
    ("FLITriggerExposure", [flidev_t]),  # (flidev_t dev);
    ("FLISetFanSpeed", [flidev_t, c_long]),  # (flidev_t dev, long fan_speed);
    (
        "FLISetVerticalTableEntry",
        [flidev_t, c_long, c_long, c_long, c_long],
    ),  # (flidev_t dev, long index, long height, long bin, long mode);
    (
        "FLIGetVerticalTableEntry",
        [flidev_t, c_long, c_long_p, c_long_p, c_long_p],
    ),  # (flidev_t dev, long index, long *height, long *bin, long *mode);
    (
        "FLIGetReadoutDimensions",
        [flidev_t, c_long_p, c_long_p, c_long_p, c_long_p, c_long_p, c_long_p],
    ),  # (flidev_t dev, long *width, long *hoffset, long *hbin, long *height, long *voffset, long *vbin);  # noqa E501
    (
        "FLIEnableVerticalTable",
        [flidev_t, c_long, c_long, c_long],
    ),  # (flidev_t dev, long width, long offset, long flags);
    (
        "FLIReadUserEEPROM",
        [flidev_t, c_long, c_long, c_long, c_void_p],
    ),  # (flidev_t dev, long loc, long address, long length, void *rbuf);
    (
        "FLIWriteUserEEPROM",
        [flidev_t, c_long, c_long, c_long, c_void_p],
    ),  # (flidev_t dev, long loc, long address, long length, void *wbuf);
    ("FLISetActiveWheel", [flidev_t, c_long]),  # (flidev_t dev, long wheel);
    (
        "FLIGetFilterName",
        [flidev_t, c_long, c_char_p, c_size_t],
    ),  # (flidev_t dev, long filter, char *name, size_t len);
    (
        "FLISetTDI",
        [flidev_t, flitdirate_t, flitdiflags_t],
    ),  # (flidev_t dev, flitdirate_t tdi_rate, flitdiflags_t flags);
]


[docs] def chk_err(err): """Wraps a libFLI C function call with error checking code.""" if err < 0: msg = os.strerror(abs(err)) # err is always negative raise FLIError(msg + f" ({err})") if err > 0: msg = os.strerror(err) raise FLIWarning(msg + f" ({err})") return err
[docs] class FLIWarning(UserWarning): """A warning from the FLI library.""" pass
[docs] class FLIError(Exception): """An error from the FLI library.""" pass
[docs] class LibFLI(ctypes.CDLL): """Wrapper for the FLI library. Parameters ---------- shared_object The path to the FLI library shared object. If not provided, uses the default, internal version. debug Whether to use the debug mode. """ def __init__( self, shared_object: Optional[str] = None, debug: bool = False, simulation_mode: bool = False, log: Callable[[str], None] | None = None, ): self.domain = flidomain_t(FLIDOMAIN_USB | FLIDEVICE_CAMERA) if not shared_object: if not simulation_mode: # pragma: no cover workdir = pathlib.Path(__file__).parent shared_object_list = list(workdir.glob("libfli*.so")) if len(shared_object_list) == 0: raise OSError("The library was compiled without a copy of libfli.") shared_object = str(shared_object_list[0]) else: shared_object = "libflimock.so" self.libc = ctypes.cdll.LoadLibrary(shared_object) if self.libc is None: raise RuntimeError("Cannot load the libfli shared library.") # Sets the argtypes and restype. for funcname, argtypes in _API_FUNCTION_PROTOTYPES: so_func = self.libc.__getattr__(funcname) so_func.argtypes = argtypes so_func.restype = chk_err self.log = log or partial(logging.log, logging.DEBUG) if debug: self.set_debug(True) @staticmethod def _convert_to_list(ptr): if not ptr: return [] list_ = [] ii = 0 while ptr[ii]: list_.append(ptr[ii].decode().split(";")[0]) ii += 1 return list_
[docs] def set_debug(self, debug=True): """Turns the debug system on/off.""" debug_level = flidebug_t(FLIDEBUG_ALL if debug else FLIDEBUG_NONE) self.libc.FLISetDebugLevel(c_char_p(None), debug_level)
[docs] def list_cameras(self): """Returns a list of connected camera names.""" names_ptr = POINTER(ctypes.c_char_p)() self.libc.FLIList(self.domain, byref(names_ptr)) cameras = self._convert_to_list(names_ptr) # Free the list if names_ptr: self.libc.FLIFreeList(names_ptr) return cameras
[docs] def get_camera(self, serial): """Gets a camera by its serial string.""" camera_names = self.list_cameras() for camera_name in camera_names: fli_camera = LibFLIDevice(camera_name, self) if fli_camera.serial == serial: return fli_camera return None
[docs] class LibFLIDevice(object): """A FLI device.""" _instances = {} def __new__(cls, name, lib): # Create a singleton to avoid opening the camera multiple times. if name not in cls._instances: cls._instances[name] = super(LibFLIDevice, cls).__new__(cls) cls._instances[name].is_open = False return cls._instances[name] def __init__(self, name, lib): if not self.is_open: self.domain = flidomain_t(FLIDOMAIN_USB | FLIDEVICE_CAMERA) self._str_size = 100 self.name = name if isinstance(name, str) else name.decode() self.lib = lib self.libc = lib.libc self.dev = flidev_t() self._model = ctypes.create_string_buffer(self._str_size) self._serial = ctypes.create_string_buffer(self._str_size) self.fwrev: int self.hwrev: int self.hbin: int self.vbin: int # The image area (ul_x, ul_y, lr_x, lr_y) self.area: Tuple[int, int, int, int] self.shutter = False self._temperature: Dict[str, float] = {"CCD": 0.0, "base": 0.0} self.open() @property def model(self): """The model of the device.""" return self._model.value.decode() @property def serial(self): """The serial string of the device.""" return self._serial.value.decode() @property def temperature(self): """Returns a dictionary of temperatures at different locations.""" self._update_temperature() return self._temperature
[docs] def open(self): """Opens the device and grabs information.""" self.libc.FLIOpen(byref(self.dev), self.name.encode(), self.domain) self.libc.FLILockDevice(self.dev) fwrev = c_long() self.libc.FLIGetFWRevision(self.dev, byref(fwrev)) self.fwrev = fwrev.value hwrev = c_long() self.libc.FLIGetHWRevision(self.dev, byref(hwrev)) self.hwrev = hwrev.value self.libc.FLIGetModel(self.dev, self._model, self._str_size) self.libc.FLIGetSerialString(self.dev, self._serial, self._str_size) # The camera doesn't allow to get the status of the shutter so we # close it on initialisation to be sure we know where it is. self.set_shutter(False) # Set bit depth (all our cameras should support 16bits) # self.libc.FLISetBitDepth(self.dev, FLI_MODE_16BIT) self._update_temperature() # Sets the binning to (1, 1) and resets the image area. self.set_binning(1, 1) self.is_open = True
[docs] def disconnect(self): """Disconnects and frees the device.""" self.libc.FLIUnlockDevice(self.dev) if self.is_open: self.libc.FLIClose(self.dev) LibFLIDevice._instances.pop(self.name, None)
def _update_temperature(self): """Gets the temperatures and updates the ``temperature`` dict.""" temp = c_double() self.libc.FLIReadTemperature(self.dev, FLI_TEMPERATURE_BASE, byref(temp)) self._temperature["base"] = temp.value self.libc.FLIReadTemperature(self.dev, FLI_TEMPERATURE_CCD, byref(temp)) self._temperature["CCD"] = temp.value
[docs] def set_temperature(self, temp: float): """Sets the temperature of the CCD. Parameters ---------- temp Temperature in the range -55 to 45C. """ self.libc.FLISetTemperature(self.dev, c_double(temp))
[docs] def set_shutter(self, shutter_value: bool): """Controls the shutter of the camera. Parameters ---------- shutter_value If `True`, opens the shutter, otherwise closes it. """ shutter_flag = FLI_SHUTTER_OPEN if shutter_value else FLI_SHUTTER_CLOSE self.libc.FLIControlShutter(self.dev, shutter_flag) self.shutter = shutter_value
[docs] def get_cooler_power(self): """Returns the cooler power.""" cooler = c_double() self.libc.FLIGetCoolerPower(self.dev, byref(cooler)) return cooler.value
[docs] def set_exposure_time(self, exp_time: float): """Sets the exposure time. Parameters ---------- exp_time The exposure time, in seconds. """ self.libc.FLISetExposureTime(self.dev, int(exp_time * 1000.0))
[docs] def set_image_area(self, area: Optional[Tuple[int, int, int, int]] = None): r"""Sets the area of the image to exposure. Parameters ---------- area : tuple The area in the format :math:`(ul_x, ul_y, lr_x, lr_y)` where :math:`ul` refers to the upper-left corner of the CCD, and :math:`lr` to the lower right. The lower-right values to pass must take into account the horizontal and vertical binning factors. In practice one must pass :math:`lr_x^\prime` and :math:`lr_y^\prime` defined as .. math:: lr_x^\prime = ul_x + (lr_x - ul_x) / hbin \\ lr_y^\prime = ul_y + (lr_y - ul_y) / vbin If you pass the area, it is assumed that you are using the full image area as reference; the binning is automatically taken into account. Although the visible area does not necessary start at ``(0, 0)``, the area passed to this function should assume an origin at ``(0, 0)``. If ``area`` is `None`, the full area of the chip is used, using the current binning. """ assert self.hbin and self.vbin, "set the binning before the area." v_ul_x, v_ul_y, v_lr_x, v_lr_y = self.get_visible_area() if area: ul_x = v_ul_x + area[0] ul_y = v_ul_y + area[1] lr_x = v_ul_x + area[2] lr_y = v_ul_y + area[3] else: ul_x = v_ul_x ul_y = v_ul_y lr_x = v_lr_x lr_y = v_lr_y lr_x_prime = int(ul_x + (lr_x - ul_x) / self.hbin) lr_y_prime = int(ul_y + (lr_y - ul_y) / self.vbin) self.libc.FLISetImageArea(self.dev, ul_x, ul_y, lr_x_prime, lr_y_prime) self.area = (ul_x - v_ul_x, ul_y - v_ul_y, lr_x - v_ul_x, lr_y - v_ul_y)
[docs] def get_visible_area(self) -> Tuple[int, int, int, int]: """Returns the visible area. Returns ------- visible_area The visible area in the format :math:`(ul_x, ul_y, lr_x, lr_y)`. See `.set_image_area` for details. The visible area does not change regardless of the binning or image area defined; it's the total available area. Note that :math:`lr_x-ul_x` and :math:`lr_y-ul_y` match the total size of the CCD, but :math:`lr_x` or :math:`lr_y` are not necessarily zero. """ ul_x = c_long() ul_y = c_long() lr_x = c_long() lr_y = c_long() self.libc.FLIGetVisibleArea( self.dev, byref(ul_x), byref(ul_y), byref(lr_x), byref(lr_y) ) return (ul_x.value, ul_y.value, lr_x.value, lr_y.value)
[docs] def set_binning(self, hbin, vbin): """Sets the binning. The image area is automatically re-adjusted to be the full visible area. If you want to change this, use `.set_image_area` manually. """ assert hbin >= 1 and hbin <= 16, "invalid hbin value." assert int(hbin) == hbin, "hbin is not an integer" assert vbin >= 1 and vbin <= 16, "invalid vbin value." assert int(vbin) == vbin, "vbin is not an integer" self.libc.FLISetHBin(self.dev, c_long(hbin)) self.libc.FLISetVBin(self.dev, c_long(vbin)) self.hbin = hbin self.vbin = vbin # Sets the image area. Passing it without arguments it uses the full # visible area but takes into account the just set binning values. self.set_image_area()
[docs] def get_exposure_time_left(self): """Returns the remaining exposure time, in milliseconds..""" timeleft = c_long() self.libc.FLIGetExposureStatus(self.dev, byref(timeleft)) return timeleft.value
[docs] def cancel_exposure(self): """Cancels an exposure.""" self.libc.FLICancelExposure(self.dev)
[docs] def start_exposure(self, frametype="normal"): """Starts and exposure and returns immediately.""" if frametype == "dark": frametype = FLI_FRAME_TYPE_DARK else: frametype = FLI_FRAME_TYPE_NORMAL self.libc.FLISetFrameType(self.dev, fliframe_t(frametype)) self.libc.FLISetTDI(self.dev, 0, 0) self.libc.FLIExposeFrame(self.dev)
[docs] def read_frame(self): """Reads the image frame. This function reads the image buffer row by row. In principle it could be optimised by writing a C function to encapsulate the loop, but tests show that the gain is marginal so it's ok to keep it as a pure Python method. """ if self.get_exposure_time_left() > 0: raise FLIError("the camera is still exposing.") (ul_x, ul_y, lr_x, lr_y) = self.area n_cols = int((lr_x - ul_x) / self.hbin) n_rows = int((lr_y - ul_y) / self.vbin) array = numpy.empty((n_rows, n_cols), dtype=numpy.uint16) img_ptr = array.ctypes.data_as(POINTER(ctypes.c_uint16)) for row in range(n_rows): offset = row * n_cols * ctypes.sizeof(ctypes.c_uint16) self.libc.FLIGrabRow(self.dev, byref(img_ptr.contents, offset), n_cols) return array