Ejemplo n.º 1
0
    def take_exposure(self, timeout: float = inf) -> np.ndarray:
        """Take an exposure in non-video mode.

        This method will poll the camera driver in a loop until the camera indicates that the
        exposure has completed. To avoid pegging the CPU in this loop, a sleep statement is used
        with a period of 10 ms. This approach should be reasonable for single exposures. Use other
        methods when video is desired.

        Args:
            timeout: How long to wait for exposure to complete in seconds. The default value inf
                can be used to disable timeout.

        Returns:
            A numpy array containing a raw camera frame. No debayering is performed.

        Raises:
            RuntimeError if the exposure failed.
            CameraTimeout if the timeout expires before exposure completes.
        """
        ASICheck(asi.ASIStartExposure(self.info.CameraID, asi.ASI_FALSE))
        start_time = time.perf_counter()
        while True:
            time.sleep(0.01)
            status = ASICheck(asi.ASIGetExpStatus(self.info.CameraID))
            if status == asi.ASI_EXP_SUCCESS:
                break
            if status == asi.ASI_EXP_FAILED:
                raise RuntimeError('Exposure failed')
            if time.perf_counter() - start_time > timeout:
                raise CameraTimeout('Timeout waiting for exposure completion')
        frame = ASICheck(
            asi.ASIGetDataAfterExp(self.info.CameraID, self._frame_size_bytes))
        return self._reshape_frame_data(frame)
Ejemplo n.º 2
0
    def __init__(
        self,
        pixel_scale: float,
        binning: int = 1,
        video_mode: bool = False,
        name: str = None,
    ):
        """Initialize and configure ZWO ASI camera.

        Args:
            pixel_scale: Scale of a pixel in degrees per pixel before binning.
            binning: Camera binning.
            video_mode: False for one-shot mode, True for video mode.
            name: Connect only to a camera where the Name member of the info struct matches this
                string. If None the name is not checked and the first camera is used. Note that
                multiple cameras could have the same Name; when this is the case, this constructor
                will connect to the first camera having a Name that matches.

        Raises:
            ASIError for any camera related problems.
        """
        num_connected = asi.ASIGetNumOfConnectedCameras()
        if num_connected == 0:
            raise ASIError('No cameras connected')

        # find the right camera
        self.info = None
        for idx in range(num_connected):
            # pylint does not seem to handle SWIG bindings perfectly
            # pylint: disable=no-value-for-parameter
            info = ASICheck(asi.ASIGetCameraProperty(idx))
            if name is None or name == info.Name:
                self.info = info
                break

        if self.info is None:
            raise ASIError(f'Could not find a camera with name "{name}"')

        self._pixel_scale = pixel_scale
        self._binning = binning
        width = self.info.MaxWidth // binning
        height = self.info.MaxHeight // binning
        self._frame_shape = (height, width)
        ASICheck(asi.ASIOpenCamera(self.info.CameraID))
        ASICheck(asi.ASIInitCamera(self.info.CameraID))
        ASICheck(
            asi.ASISetControlValue(self.info.CameraID, asi.ASI_MONO_BIN, 1,
                                   asi.ASI_FALSE))
        ASICheck(
            asi.ASISetControlValue(self.info.CameraID,
                                   asi.ASI_BANDWIDTHOVERLOAD, 94,
                                   asi.ASI_FALSE))
        self.video_mode = video_mode
Ejemplo n.º 3
0
 def video_mode(self, enabled: bool) -> None:
     """Enable or disable video mode"""
     self._bit_depth = self.BitDepth.RAW8 if enabled else self.BitDepth.RAW16
     height, width = self._frame_shape
     self._frame_size_bytes = width * height * self._bit_depth.bytes_per_pixel(
     )
     ASICheck(
         asi.ASISetROIFormat(self.info.CameraID, width, height,
                             self._binning, self._bit_depth))
     if enabled:
         ASICheck(
             asi.ASISetControlValue(self.info.CameraID,
                                    asi.ASI_HIGH_SPEED_MODE, 1,
                                    asi.ASI_FALSE))
         ASICheck(asi.ASIStartVideoCapture(self.info.CameraID))
     else:
         ASICheck(
             asi.ASISetControlValue(self.info.CameraID,
                                    asi.ASI_HIGH_SPEED_MODE, 0,
                                    asi.ASI_FALSE))
         ASICheck(asi.ASIStopVideoCapture(self.info.CameraID))
     self._video_mode = enabled
Ejemplo n.º 4
0
 def get_dropped_frames(self) -> int:
     """Get number of dropped frames so far"""
     return ASICheck(asi.ASIGetDroppedFrames(self.info.CameraID))
Ejemplo n.º 5
0
 def _get_ctrl(self, ctrl):
     return ASICheck(asi.ASIGetControlValue(self.info.CameraID, ctrl))
Ejemplo n.º 6
0
 def _set_ctrl(self, ctrl, value: int):
     # auto mode always disabled since we generally don't trust it
     ASICheck(
         asi.ASISetControlValue(self.info.CameraID, ctrl, value,
                                asi.ASI_FALSE))
Ejemplo n.º 7
0
 def __del__(self):
     if hasattr(self, 'info') and self.info is not None:
         ASICheck(asi.ASICloseCamera(self.info.CameraID))