API
qhyccd.py
Go to the documentation of this file.
1 #!/bin/python3
2 # Descended from code in https://github.com/JiangXL/qhyccd-python (GPLv3)
3 # authored by H.F <moyuejian@outlook.com>
4 import ctypes
5 import numpy as np
6 import logging
7 from .libqhy import *
8 
9 log = logging.getLogger(__name__)
10 
11 TYPE_CHAR20 = ctypes.c_char * 20
12 TYPE_CHAR32 = ctypes.c_char * 32
13 
14 class QHYCCDSDK():
15  '''Class interface for the QHYCCD SDK
16  '''
17  def __init__(self, dll_path='/usr/local/lib/libqhyccd.so'):
18  '''
19  '''
20  # create sdk handle
21  self._sdk_sdk = ctypes.CDLL(dll_path)
22 
23  self._sdk_sdk.GetQHYCCDParam.restype = ctypes.c_double
24  self._sdk_sdk.OpenQHYCCD.restype = ctypes.POINTER(ctypes.c_uint32)
25 
26  ret = self._sdk_sdk.InitQHYCCDResource()
27 
28  self._number_of_cameras_number_of_cameras = self._sdk_sdk.ScanQHYCCD()
29  self._ids_ids = []
30  for i in range(self._number_of_cameras_number_of_cameras):
31  self._ids_ids.append( TYPE_CHAR32() )
32  self._sdk_sdk.GetQHYCCDId(ctypes.c_int(i), self._ids_ids[-1])
33  log.debug("Cameras {:d} ID {:s}".format(i, self._ids_ids[-1].value.decode('utf8')))
34 
35  self._camera_handles_camera_handles = {}
36 
37  def __del__(self):
38  '''
39  '''
40  # Go through all camera handles and close the ones that are open
41  for cam_handle in self._camera_handles_camera_handles:
42  try:
43  self._sdk_sdk.CloseQHYCCD(cam_handle)
44  except Exception:
45  pass
46  self._sdk_sdk.ReleaseQHYCCDResource()
47 
48  def list_cameras(self) -> list:
49  '''
50  '''
51  return [_id.value for _id in self._ids_ids]
52 
53  def open_camera(self, camera_id):
54  '''
55  '''
56  if camera_id in self._camera_handles_camera_handles:
57  return self._camera_handles_camera_handles[camera_id]
58  else:
59  # Open connection to the camera and initialize its resources
60  self._camera_handles_camera_handles[camera_id] = self._sdk_sdk.OpenQHYCCD(self._ids_ids[camera_id])
61  self._sdk_sdk.InitQHYCCD(self._camera_handles_camera_handles[camera_id])
62 
63  return self._camera_handles_camera_handles[camera_id]
64 
65  def close_camera(self, camera_id):
66  '''
67  '''
68  if camera_id in self._camera_handles_camera_handles:
69  # Close connection to camera
70  self._sdk_sdk.CloseQHYCCD(self._camera_handles_camera_handles[camera_id])
71 
72  # Remove camera from active list
73  del self._camera_handles_camera_handles[camera_id]
74 
75  #def get_camera_properties(self, camera_handle):
76  # GetQHYCCDModel(TYPE_CHAR20)
77 
78  def get_parameter_limits(self, camera_handle, parameter):
79  param_min = ctypes.c_double
80  param_max = ctypes.c_double
81  param_step = ctypes.c_double
82 
83  self._sdk_sdk.GetQHYCCDParamMinMaxStep(camera_handle, parameter, ctypes.byref(param_min), ctypes.byref(param_max), ctypes.byref(param_step))
84 
85  return param_min.value, param_max.value, param_step.value
86 
87  def get_all_limits(self, camera_handle):
88  min_gain, max_gain, step_gain = self.get_parameter_limitsget_parameter_limits(camera_handle, CONTROL_ID.CONTROL_GAIN)
89  min_exp, max_exp, step_exp = self.get_parameter_limitsget_parameter_limits(camera_handle, CONTROL_ID.CONTROL_EXPOSURE)
90 
91  parameter_limits = {
92  'exp' : [min_exp, max_exp, step_exp],
93  'gain' : [min_gain, max_gain, step_gain]
94  }
95 
96  return parameter_limits
97 
98  def get_chip_info(self, camera_handle):
99  # Get Camera Parameters
100  chip_width = ctypes.c_double()
101  chip_height = ctypes.c_double()
102  width = ctypes.c_uint()
103  height = ctypes.c_uint()
104  pixel_width = ctypes.c_double()
105  pixel_height = ctypes.c_double()
106  channels = ctypes.c_uint32(1)
107  bpp = ctypes.c_uint()
108 
109  self._sdk_sdk.GetQHYCCDChipInfo(camera_handle, ctypes.byref(chip_width), ctypes.byref(chip_height), ctypes.byref(width), ctypes.byref(height), ctypes.byref(pixel_width), ctypes.byref(pixel_height), ctypes.byref(bpp))
110 
111  chip_info = {
112  'physical' : [chip_width.value, chip_height.value],
113  'size' : [width.value, height.value],
114  'pixel_size' : [pixel_width.value, pixel_height.value],
115  'channels' : channels.value,
116  'bpp' : bpp.value
117  }
118 
119  return chip_info
120 
121 
122  @property
123  def number_of_cameras(self):
124  return self._number_of_cameras_number_of_cameras
125 
126  @property
127  def version(self):
128  year = ctypes.c_uint32()
129  month = ctypes.c_uint32()
130  day = ctypes.c_uint32()
131  subday = ctypes.c_uint32()
132 
133  # Year starts counting at 2000 so we add 2000 to the returned value
134  ret = self._sdk_sdk.GetQHYCCDSDKVersion(ctypes.byref(year), ctypes.byref(month), ctypes.byref(day), ctypes.byref(subday))
135  return '{:d}-{:>02d}-{:>02d}'.format(2000 + year.value, month.value, day.value)
136 
137  def set_parameter(self, camera_handle, parameter, value):
138  self._sdk_sdk.SetQHYCCDParam(camera_handle, parameter, value)
139 
140  def get_parameter(self, camera_handle, parameter):
141  return self._sdk_sdk.GetQHYCCDParam(camera_handle, parameter)
142 
143 class QHYCCDCamera():
144  ''' A class that interface with the QHYCCD series of cameras.
145  '''
146  def __init__(self, sdk, camera_id, new_bpp=16):
147  # create sdk handle
148  self._sdk_sdk = sdk
149  self._camera_camera = self._sdk_sdk.open_camera(camera_id)
150 
151  self._stream_mode_stream_mode = 0 # set default mode to stream mode, otherwise set 0 for single frame mode
152 
153  self.bppbppbppbpp = new_bpp
155  self.gaingaingaingain = 1.0
156 
157  # Get Camera Parameters
158  self._chip_info_chip_info = self._sdk_sdk.get_chip_info(self._camera_camera)
159  self._width_width = self._chip_info_chip_info['size'][0]
160  self._height_height = self._chip_info_chip_info['size'][1]
161  self._channels_channels = ctypes.c_uint32(self._chip_info_chip_info['channels'])
162 
163  # Always cool to ten at startup.
165 
166  # Set ROI and readout parameters
167  self._roi_w_roi_w, self._roi_h_roi_h = ctypes.c_uint(self._width_width), ctypes.c_uint(self._height_height)
168  self.set_roiset_roi(0, 0, self._width_width, self._height_height)
169  self._sdk_sdk.set_parameter(self._camera_camera, CONTROL_ID.CONTROL_USBTRAFFIC, ctypes.c_double(50))
170  self._sdk_sdk.set_parameter(self._camera_camera, CONTROL_ID.CONTROL_TRANSFERBIT, self._bpp_bpp)
171 
172  def cancel_exposure(self):
173  pass
174 
175  @property
176  def temperature(self):
177  self._temperature_temperature = self._sdk_sdk.get_parameter(self._camera_camera, CONTROL_ID.CONTROL_CURTEMP)
178  return self._temperature_temperature
179 
180  @property
182  return self._sdk_sdk.get_parameter(self._camera_camera, CONTROL_ID.CONTROL_COOLER)
183 
184  @target_temperature.setter
185  def target_temperature(self, new_temperature):
186  self._target_temperature_target_temperature = new_temperature
187  self._sdk_sdk.set_parameter(self._camera_camera, CONTROL_ID.CONTROL_COOLER, ctypes.c_double(self._target_temperature_target_temperature))
188 
189  @property
190  def exposure_time(self):
191  return self._exposure_time_exposure_time
192 
193  @exposure_time.setter
194  def exposure_time(self, new_exposure_time):
195  # QHYCCD SDK uses microseconds as unit
196  # The QHYCCD VIS-X interface uses seconds as the unit. Carefull with converting units!
197  self._exposure_time_exposure_time = new_exposure_time
198  self._sdk_sdk.set_parameter(self._camera_camera, CONTROL_ID.CONTROL_EXPOSURE, ctypes.c_double(self._exposure_time_exposure_time * 1e6))
199  log.debug("Set exposure time to", self._sdk_sdk.get_parameter(self._camera_camera, CONTROL_ID.CONTROL_EXPOSURE) / 1e6)
200 
201  @property
202  def gain(self):
203  return self._gain_gain
204 
205  @gain.setter
206  def gain(self, new_gain):
207  self._gain_gain = new_gain
208  self._sdk_sdk.set_parameter(self._camera_camera, CONTROL_ID.CONTROL_GAIN, ctypes.c_double(self._gain_gain))
209 
210  #""" Set camera depth """
211  @property
212  def bpp(self):
213  return self._bpp_bpp.value
214 
215  @bpp.setter
216  def bpp(self, new_bpp):
217  self._bpp_bpp = ctypes.c_double(new_bpp)
218  self._sdk_sdk.set_parameter(self._camera_camera, CONTROL_ID.CONTROL_TRANSFERBIT, self._bpp_bpp)
219 
220  #""" Set camera ROI """
221  def set_roi(self, x0, y0, roi_w, roi_h):
222  self._roi_w_roi_w = ctypes.c_uint(roi_w)
223  self._roi_h_roi_h = ctypes.c_uint(roi_h)
224  # update the image buffer
225  if self._bpp_bpp.value == 16:
226  self._imgdata_imgdata = (ctypes.c_uint16 * roi_w * roi_h)()
227  self._sdk_sdk._sdk.SetQHYCCDResolution(self._camera_camera, ctypes.c_uint(x0), ctypes.c_uint(y0), self._roi_w_roi_w, self._roi_h_roi_h)
228  else: # 8 bit
229  self._imgdata_imgdata = (ctypes.c_uint8 * roi_w * roi_h)()
230  self._sdk_sdk._sdk.SetQHYCCDResolution(self._camera_camera, ctypes.c_uint(x0), ctypes.c_uint(y0), self._roi_w_roi_w, self._roi_h_roi_h)
231 
232  def start_exposure(self):
233  ret = self._sdk_sdk._sdk.ExpQHYCCDSingleFrame(self._camera_camera)
234 
235  def remaining_time(self):
236  percentage_complete = self._sdk_sdk._sdk.GetQHYCCDExposureRemaining(self._camera_camera) # This counts the completion rate in percentages
237  remaining = (100.0 - percentage_complete)/100.0 * self.exposure_timeexposure_timeexposure_timeexposure_time
238  return remaining
239 
241  if self.remaining_timeremaining_time < 1.0:
242  return True
243  else:
244  return False
245 
246  def readout(self):
247  ret = self._sdk_sdk._sdk.GetQHYCCDSingleFrame(self._camera_camera, ctypes.byref(self._roi_w_roi_w), ctypes.byref(self._roi_h_roi_h), ctypes.byref(self._bpp_bpp), ctypes.byref(self._channels_channels), self._imgdata_imgdata)
248  return np.asarray(self._imgdata_imgdata)
249 
250  def get_singleframe(self):
251  ret = self._sdk_sdk._sdk.ExpQHYCCDSingleFrame(self._camera_camera)
252  ret = self._sdk_sdk._sdk.GetQHYCCDSingleFrame(self._camera_camera, ctypes.byref(self._roi_w_roi_w), ctypes.byref(self._roi_h_roi_h), ctypes.byref(self._bpp_bpp), ctypes.byref(self._channels_channels), self._imgdata_imgdata)
253  return np.asarray(self._imgdata_imgdata)
254 
255  @property
256  def read_mode(self):
257  return self._read_mode_read_mode
258 
259  @read_mode.setter
260  def set_readout_modes(self, new_read_mode):
261  if new_read_mode == 0 or new_read_mode == 1:
262  self._read_mode_read_mode = new_read_mode
263  self._sdk_sdk.sdk.SetQHYCCDReadMode(self._camera_camera, ctypes.c_uint32(self._read_mode_read_mode))
def exposure_time(self, new_exposure_time)
Definition: qhyccd.py:194
def target_temperature(self, new_temperature)
Definition: qhyccd.py:185
def set_readout_modes(self, new_read_mode)
Definition: qhyccd.py:260
def set_roi(self, x0, y0, roi_w, roi_h)
Definition: qhyccd.py:221
def target_temperature(self)
Definition: qhyccd.py:181
def __init__(self, sdk, camera_id, new_bpp=16)
Definition: qhyccd.py:146
def is_exposure_finished(self)
Definition: qhyccd.py:240
def gain(self, new_gain)
Definition: qhyccd.py:206
def bpp(self, new_bpp)
Definition: qhyccd.py:216
def get_all_limits(self, camera_handle)
Definition: qhyccd.py:87
def set_parameter(self, camera_handle, parameter, value)
Definition: qhyccd.py:137
def open_camera(self, camera_id)
Definition: qhyccd.py:53
def close_camera(self, camera_id)
Definition: qhyccd.py:65
def number_of_cameras(self)
Definition: qhyccd.py:123
list list_cameras(self)
Definition: qhyccd.py:48
def get_parameter(self, camera_handle, parameter)
Definition: qhyccd.py:140
def get_parameter_limits(self, camera_handle, parameter)
Definition: qhyccd.py:78
def __init__(self, dll_path='/usr/local/lib/libqhyccd.so')
Definition: qhyccd.py:17
def get_chip_info(self, camera_handle)
Definition: qhyccd.py:98
GeneratorWrapper< T > range(T const &start, T const &end, T const &step)
Definition: catch.hpp:4700
int TYPE_CHAR32
Definition: qhyccd.py:12