#!/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);
(
"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);
(
"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);
(
"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