Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ventus, T7 and Prior's NanoScanZ drivers and bug fixes #70

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 88 additions & 54 deletions lantz/drivers/legacy/andor/ccd.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,18 @@

- Andor SDK 2.96 Manual

:copyright: 2015 by Lantz Authors, see AUTHORS for more details.
:copyright: 2016 by Lantz Authors, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
import numpy as np
import ctypes as ct
from collections import namedtuple

from lantz import Driver, Feat, Action, DictFeat
from lantz import Feat, Action, DictFeat
from lantz.errors import InstrumentError
from lantz.foreign import LibraryDriver
from lantz import Q_

degC = Q_(1, 'degC')
us = Q_(1, 'us')
MHz = Q_(1, 'MHz')
seg = Q_(1, 's')
Expand All @@ -50,7 +49,7 @@
20014: 'DRV_ERROR_UP_FIFO',
20015: 'DRV_ERROR_PATTERN',
20017: 'DRV_ACQUISITION_ERRORS',
20018: 'Computer unable to read the data via the ISA slot at the required rate.',
20018: 'PC unable to read the data via the ISA slot at the required rate.',
20019: 'DRV_ACQ_DOWNFIFO_FULL',
20020: 'RV_PROC_UNKNOWN_INSTRUCTION',
20021: 'DRV_ILLEGAL_OP_CODE',
Expand Down Expand Up @@ -114,6 +113,7 @@
20121: 'DRV_ERROR_NOHANDLE',
20130: 'DRV_GATING_NOT_AVAILABLE',
20131: 'DRV_FPGA_VOLTAGE_ERROR',
20099: 'DRV_BINNING_ERROR',
20100: 'DRV_INVALID_AMPLIFIER',
20101: 'DRV_INVALID_COUNTCONVERT_MODE'
}
Expand Down Expand Up @@ -171,10 +171,14 @@ def initialize(self):
self.frame_transfer_mode_state = False
self.frame_transfer_mode = self.frame_transfer_mode_state

self.crop_mode_state = False
self.crop_mode_shape = self.detector_shape
self.crop_mode_binning = (1, 1)

self.fan_mode_index = 'onfull'
self.fan_mode = self.fan_mode_index

self.EM_gain_mode_index = 'DAC255'
self.EM_gain_mode_index = 'RealGain'
self.EM_gain_mode = self.EM_gain_mode_index

self.cooled_on_shutdown_value = False
Expand All @@ -200,7 +204,7 @@ def initialize(self):
self.preamp_index = 0
self.preamp = self.preamp_index

self.temperature_sp = 0 * degC
self.temperature_sp = 0
self.temperature_setpoint = self.temperature_sp

self.auxout = np.zeros(4, dtype=bool)
Expand All @@ -219,7 +223,7 @@ def finalize(self):
self.free_int_mem()
self.lib.ShutDown()

### SYSTEM INFORMATION
# SYSTEM INFORMATION

@Feat(read_once=True)
def ncameras(self):
Expand Down Expand Up @@ -403,7 +407,7 @@ def count_convert_available(self, mode):
else:
return False

### SHUTTER # I couldn't find a better way to do this... sorry
# SHUTTER # I couldn't find a better way to do this... sorry
@Action()
def shutter(self, typ, mode, ext_closing, ext_opening, ext_mode):
""" This function expands the control offered by SetShutter to allow an
Expand Down Expand Up @@ -434,12 +438,6 @@ def shutter(self, typ, mode, ext_closing, ext_opening, ext_mode):
5 Open for any series
int closingtime: time shutter takes to close (milliseconds)
int openingtime: Time shutter takes to open (milliseconds)
int mode:
0 Fully Auto
1 Permanently Open
2 Permanently Closed
4 Open for FVB series
5 Open for any series
"""
self.lib.SetShutterEx(ct.c_int(typ), ct.c_int(mode),
ct.c_int(ext_closing), ct.c_int(ext_opening),
Expand All @@ -460,9 +458,9 @@ def has_mechanical_shutter(self):
self.lib.IsInternalMechanicalShutter(ct.pointer(state))
return bool(state.value)

### TEMPERATURE
# TEMPERATURE (EVERYTHING IS IN DEGC)

@Feat(read_once=True, units='degC')
@Feat(read_once=True)
def min_temperature(self):
""" This function returns the valid range of temperatures in centigrads
to which the detector can be cooled.
Expand All @@ -471,7 +469,7 @@ def min_temperature(self):
self.lib.GetTemperatureRange(ct.pointer(mini), ct.pointer(maxi))
return mini.value

@Feat(read_once=True, units='degC')
@Feat(read_once=True)
def max_temperature(self):
""" This function returns the valid range of temperatures in centigrads
to which the detector can be cooled.
Expand All @@ -489,16 +487,16 @@ def temperature_status(self):
ans = self.lib.GetTemperatureF(ct.pointer(temp))
return _ERRORS[ans]

@Feat(units='degC')
@Feat()
def temperature(self):
""" This function returns the temperature of the detector to the
nearest degree. It also gives the status of cooling process.
nearest celsius degree. It also gives the status of cooling process.
"""
temp = ct.c_float()
self.lib.GetTemperatureF(ct.pointer(temp))
return temp.value

@Feat(units='degC')
@Feat()
def temperature_setpoint(self):
return self.temperature_sp

Expand Down Expand Up @@ -552,7 +550,7 @@ def fan_mode(self, mode):
if ans == 20002:
self.fan_mode_index = mode

### FILTERS
# FILTERS

@Feat()
def averaging_factor(self):
Expand Down Expand Up @@ -649,7 +647,7 @@ def cr_filter_enabled(self):
def cr_filter_enabled(self, value):
self.lib.SetFilterMode(ct.c_int(value))

### PHOTON COUNTING MODE
# PHOTON COUNTING MODE

@Feat(values={True: 1, False: 0}) # FIXME: untested
def photon_counting_mode(self):
Expand Down Expand Up @@ -688,7 +686,7 @@ def set_photon_counting_thres(self, mini, maxi):
"""
self.lib.SetPhotonCountingThreshold(ct.c_long(mini), ct.c_long(maxi))

### FAST KINETICS MODE
# FAST KINETICS MODE

@Feat(units='s')
def FK_exposure_time(self):
Expand All @@ -702,7 +700,7 @@ def FK_exposure_time(self):
self.lib.GetFKExposureTime(ct.pointer(f))
return f.value

### ACQUISITION HANDLING
# ACQUISITION HANDLING

@Feat(values={'Single Scan': 1, 'Accumulate': 2, 'Kinetics': 3,
'Fast Kinetics': 4, 'Run till abort': 5})
Expand Down Expand Up @@ -917,7 +915,7 @@ def readout_packing(self, state):
if ans == 20002:
self.readout_packing_state = state

### DATA HANDLING
# DATA HANDLING

@Feat(read_once=True)
def min_image_length(self):
Expand Down Expand Up @@ -960,7 +958,7 @@ def acquired_data16(self, shape):
large enough to hold the complete data set.
"""
size = np.array(shape).prod()
arr = np.ascontiguousarray(np.zeros(size, dtype=np.int16))
arr = np.ascontiguousarray(np.zeros(size, dtype=np.uint16))
self.lib.GetAcquiredData16(arr.ctypes.data_as(ct.POINTER(ct.c_int16)),
ct.c_ulong(size))
return arr.reshape(shape)
Expand All @@ -982,7 +980,7 @@ def oldest_image16(self, shape):
""" 16-bit version of the GetOldestImage function.
"""
size = np.array(shape).prod()
array = np.ascontiguousarray(np.zeros(size, dtype=np.int16))
array = np.ascontiguousarray(np.zeros(size, dtype=np.uint16))
self.lib.GetOldestImage16(array.ctypes.data_as(ct.POINTER(ct.c_int16)),
ct.c_ulong(size))
return array.reshape(shape)
Expand All @@ -1003,7 +1001,7 @@ def most_recent_image16(self, shape):
""" 16-bit version of the GetMostRecentImage function.
"""
size = np.array(shape).prod()
arr = np.ascontiguousarray(np.zeros(size, dtype=np.int16))
arr = np.ascontiguousarray(np.zeros(size, dtype=np.uint16))
pt = ct.POINTER(ct.c_int16)
self.lib.GetMostRecentImage16(arr.ctypes.data_as(pt), ct.c_ulong(size))
return arr.reshape(shape)
Expand Down Expand Up @@ -1034,7 +1032,7 @@ def images16(self, first, last, shape, validfirst, validlast):
""" 16-bit version of the GetImages function.
"""
size = shape[0] * shape[1] * (1 + last - first)
array = np.ascontiguousarray(np.zeros(size, dtype=np.int16))
array = np.ascontiguousarray(np.zeros(size, dtype=np.uint16))
self.lib.GetImages16(ct.c_long(first), ct.c_long(last),
array.ctypes.data_as(ct.POINTER(ct.c_int16)),
ct.c_ulong(size),
Expand Down Expand Up @@ -1124,7 +1122,7 @@ def save_raw(self, filename, typ):
self.lib.SaveAsRaw(ct.c_char_p(str.encode(filename)),
ct.c_int(self.savetypes[typ]))

### EXPOSURE SETTINGS
# EXPOSURE SETTINGS

@Feat()
def acquisition_timings(self):
Expand All @@ -1144,7 +1142,7 @@ def acquisition_timings(self):
kine = ct.c_float()
self.lib.GetAcquisitionTimings(ct.pointer(exp), ct.pointer(accum),
ct.pointer(kine))
return exp.value, accum.value, kine.value
return exp.value * seg, accum.value * seg, kine.value * seg

@Action()
def set_exposure_time(self, time):
Expand Down Expand Up @@ -1278,7 +1276,29 @@ def frame_transfer_mode(self, state):
if ans == 20002:
self.frame_transfer_mode_state = state

### AMPLIFIERS, GAIN, SPEEDS
@Feat(values={True: 1, False: 0})
def crop_mode(self):
""" This function effectively reduces the dimensions of the CCD by
excluding some rows or columns to achieve higher throughput. In
isolated crop mode iXon, Newton and iKon cameras can operate in either
Full Vertical Binning or Imaging read modes. iDus can operate in Full
Vertical Binning read mode only.
Note: It is important to ensure that no light falls on the excluded
region otherwise the acquired data will be corrupted.
"""
return self.crop_mode_state

@crop_mode.setter
def crop_mode(self, state):
ans = self.lib.SetIsolatedCropMode(ct.c_int(state),
ct.c_int(self.crop_mode_shape[1]),
ct.c_int(self.crop_mode_shape[0]),
ct.c_int(self.crop_mode_binning[1]),
ct.c_int(self.crop_mode_binning[0]))
if ans == 20002:
self.crop_mode_state = state

# AMPLIFIERS, GAIN, SPEEDS

@Feat(read_once=True)
def n_preamps(self):
Expand Down Expand Up @@ -1330,7 +1350,7 @@ def true_preamp(self, index):
index = ct.c_int(index)
gain = ct.c_float()
self.lib.GetPreAmpGain(index, ct.pointer(gain))
return gain
return gain.value

@Feat()
def preamp(self):
Expand Down Expand Up @@ -1629,7 +1649,7 @@ def n_vert_shift_speeds(self):
self.lib.GetNumberVSSpeeds(ct.pointer(n))
return n.value

def true_vert_shift_speed(self, index=0):
def true_vert_shift_speed(self, index):
"""As your Andor SDK system may be capable of operating at more than
one vertical shift speed this function will return the actual speeds
available. The value returned is in microseconds.
Expand All @@ -1650,7 +1670,7 @@ def vert_shift_speed(self, index):
self.vert_shift_speed_index = index
self.lib.SetVSSpeed(ct.c_int(index))

### BASELINE
# BASELINE

@Feat(values={True: 1, False: 0})
def baseline_clamp(self):
Expand Down Expand Up @@ -1682,7 +1702,7 @@ def baseline_offset(self, value):
if ans == 20002:
self.baseline_offset_value = value

### BIT DEPTH
# BIT DEPTH

def bit_depth(self, ch):
""" This function will retrieve the size in bits of the dynamic range
Expand All @@ -1693,7 +1713,7 @@ def bit_depth(self, ch):
self.lib.GetBitDepth(ch, ct.pointer(depth))
return depth.value

### TRIGGER
# TRIGGER

@Feat(values={True: 1, False: 0})
def adv_trigger_mode(self):
Expand Down Expand Up @@ -1759,7 +1779,7 @@ def trigger_level(self, value):
"""
self.lib.SetTriggerLevel(ct.c_float(value))

### AUXPORT
# AUXPORT

@DictFeat(values={True: not(0), False: 0}, keys=list(range(1, 5)))
def in_aux_port(self, port):
Expand Down Expand Up @@ -1890,11 +1910,8 @@ def flush(self):
self.lib.AT_Flush(self.AT_H)

if __name__ == '__main__':
from matplotlib import pyplot as plt
from lantz import Q_
import time

degC = Q_(1, 'degC')
us = Q_(1, 'us')
MHz = Q_(1, 'MHz')
s = Q_(1, 's')
Expand All @@ -1912,12 +1929,20 @@ def flush(self):
andor.set_exposure_time(0.03 * s)
andor.trigger_mode = 'Internal'
andor.amp_typ = 0
andor.horiz_shift_speed = 0
andor.horiz_shift_speed = 3
andor.vert_shift_speed = 0
andor.shutter(0, 0, 0, 0, 0)
# andor.shutter(0, 1, 0, 0, 0)
andor.preamp = 0
andor.EM_advanced_enabled = False
andor.EM_gain_mode = 'RealGain'
andor.set_n_accum(1) # No accumulation of exposures
# andor.set_accum_time(0 * s) # Minimum accumulation and kinetic
andor.set_kinetic_cycle_time(0 * s) # times
andor.frame_transfer_mode = True
andor.crop_mode = True

# # Temperature stabilization
# andor.temperature_setpoint = -30 * degC
# andor.temperature_setpoint = -30
# andor.cooler_on = True
# stable = 'Temperature has stabilized at set point.'
# print('Temperature set point =', andor.temperature_setpoint)
Expand All @@ -1927,13 +1952,22 @@ def flush(self):
# print('Temperature has stabilized at set point')

# Acquisition
andor.start_acquisition()
time.sleep(2)
data = andor.most_recent_image(shape=andor.detector_shape)
andor.abort_acquisition()

plt.imshow(data, cmap='gray', interpolation='None')
plt.colorbar()
plt.show()

print(data.min(), data.max(), data.mean())
# andor.start_acquisition()
# time.sleep(2)
# shape = andor.detector_shape
# data = andor.most_recent_image(shape=shape)
#
# i, j = andor.new_images_index
# n = j - i
## stack = np.zeros((n, shape[0], shape[1]),
#
# stack = andor.images16(i, j, andor.detector_shape, 1, n)
#
# andor.abort_acquisition()
# andor.shutter(0, 2, 0, 0, 0)

# plt.imshow(data, cmap='gray', interpolation='None')
# plt.colorbar()
# plt.show()

# print(data.min(), data.max(), data.mean())
Loading