Skip to content

Commit

Permalink
v0.15.0
Browse files Browse the repository at this point in the history
version 0.15.0
  • Loading branch information
thusser committed Dec 29, 2021
2 parents 205aaf0 + 52dd70a commit 5cf5a82
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 86 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/pypi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Publish package to PyPI
on:
push:
tags:
- 'v*'
jobs:
build-n-publish:
name: Build and publish package to PyPI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: "3.9"
- name: Install poetry
run: |
curl -fsS -o get-poetry.py https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py
python get-poetry.py -y
- name: Publish
env:
PYPI_TOKEN: ${{ secrets.pypi_password }}
run: |
$HOME/.poetry/bin/poetry config pypi-token.pypi $PYPI_TOKEN
$HOME/.poetry/bin/poetry publish --build
8 changes: 2 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,10 @@ Clone the repository:
git clone https://github.com/pyobs/pyobs-asi.git


Install dependencies:

cd pyobs-asi
pip3 install -r requirements
And install it:

python3 setup.py install
cd pyobs-asi
pip3 install .


Configuration
Expand Down
118 changes: 54 additions & 64 deletions pyobs_asi/asicamera.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import asyncio
import logging
import math
import threading
Expand All @@ -11,6 +12,7 @@
from pyobs.modules.camera.basecamera import BaseCamera
from pyobs.utils.enums import ImageFormat, ExposureStatus
from pyobs.images import Image
from pyobs.utils.parallel import event_wait

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -47,9 +49,9 @@ def __init__(self, camera: str, sdk: str = '/usr/local/lib/libASICamera2.so', **
self._binning = 1
self._image_format = ImageFormat.INT16

def open(self) -> None:
async def open(self) -> None:
"""Open module."""
BaseCamera.open(self)
await BaseCamera.open(self)

# init driver
asi.init(self._sdk_path)
Expand Down Expand Up @@ -82,49 +84,39 @@ def open(self) -> None:
self._camera.set_control_value(asi.ASI_FLIP, 0)
self._camera.set_image_type(asi.ASI_IMG_RAW16)

# Enabling stills mode
try:
# Force any single exposure to be halted
self._camera.stop_video_capture()
self._camera.stop_exposure()
except (KeyboardInterrupt, SystemExit):
raise
except:
pass
# enabling image mode
self._camera.stop_video_capture()
self._camera.stop_exposure()

# get initial window and binning
self._binning = self._camera.get_bin()
self._window = self._camera.get_roi()

def close(self) -> None:
"""Close the module."""
BaseCamera.close(self)

def get_full_frame(self, **kwargs: Any) -> Tuple[int, int, int, int]:
async def get_full_frame(self, **kwargs: Any) -> Tuple[int, int, int, int]:
"""Returns full size of CCD.
Returns:
Tuple with left, top, width, and height set.
"""
return 0, 0, self._camera_info['MaxWidth'], self._camera_info['MaxHeight']

def get_window(self, **kwargs: Any) -> Tuple[int, int, int, int]:
async def get_window(self, **kwargs: Any) -> Tuple[int, int, int, int]:
"""Returns the camera window.
Returns:
Tuple with left, top, width, and height set.
"""
return self._window

def get_binning(self, **kwargs: Any) -> Tuple[int, int]:
async def get_binning(self, **kwargs: Any) -> Tuple[int, int]:
"""Returns the camera binning.
Returns:
Tuple with x and y.
"""
return self._binning, self._binning

def set_window(self, left: int, top: int, width: int, height: int, **kwargs: Any) -> None:
async def set_window(self, left: int, top: int, width: int, height: int, **kwargs: Any) -> None:
"""Set the camera window.
Args:
Expand All @@ -139,7 +131,7 @@ def set_window(self, left: int, top: int, width: int, height: int, **kwargs: Any
self._window = (left, top, width, height)
log.info('Setting window to %dx%d at %d,%d...', width, height, left, top)

def set_binning(self, x: int, y: int, **kwargs: Any) -> None:
async def set_binning(self, x: int, y: int, **kwargs: Any) -> None:
"""Set the camera binning.
Args:
Expand All @@ -152,7 +144,7 @@ def set_binning(self, x: int, y: int, **kwargs: Any) -> None:
self._binning = x
log.info('Setting binning to %dx%d...', x, x)

def list_binnings(self, **kwargs: Any) -> List[Tuple[int, int]]:
async def list_binnings(self, **kwargs: Any) -> List[Tuple[int, int]]:
"""List available binnings.
Returns:
Expand All @@ -165,7 +157,7 @@ def list_binnings(self, **kwargs: Any) -> List[Tuple[int, int]]:
else:
return []

def _expose(self, exposure_time: float, open_shutter: bool, abort_event: threading.Event) -> Image:
async def _expose(self, exposure_time: float, open_shutter: bool, abort_event: threading.Event) -> Image:
"""Actually do the exposure, should be implemented by derived classes.
Args:
Expand Down Expand Up @@ -194,7 +186,6 @@ def _expose(self, exposure_time: float, open_shutter: bool, abort_event: threadi
self._binning, image_format)

# set status and exposure time in ms
self._change_exposure_status(ExposureStatus.EXPOSING)
self._camera.set_control_value(asi.ASI_EXPOSURE, int(exposure_time * 1e6))

# get date obs
Expand All @@ -204,17 +195,17 @@ def _expose(self, exposure_time: float, open_shutter: bool, abort_event: threadi

# do exposure
self._camera.start_exposure()
self.closing.wait(0.01)
await asyncio.sleep(0.01)

# wait for image
while self._camera.get_exposure_status() == asi.ASI_EXP_WORKING:
# aborted?
if abort_event.is_set():
self._change_exposure_status(ExposureStatus.IDLE)
await self._change_exposure_status(ExposureStatus.IDLE)
raise ValueError('Aborted exposure.')

# sleep a little
abort_event.wait(0.01)
await event_wait(abort_event, 0.01)

# success?
status = self._camera.get_exposure_status()
Expand All @@ -223,7 +214,7 @@ def _expose(self, exposure_time: float, open_shutter: bool, abort_event: threadi

# get data
log.info('Exposure finished, reading out...')
self._change_exposure_status(ExposureStatus.READOUT)
await self._change_exposure_status(ExposureStatus.READOUT)
buffer = self._camera.get_data_after_exposure()
whbi = self._camera.get_roi_format()

Expand Down Expand Up @@ -286,17 +277,45 @@ def _expose(self, exposure_time: float, open_shutter: bool, abort_event: threadi

# return FITS image
log.info('Readout finished.')
self._change_exposure_status(ExposureStatus.IDLE)
return image

def _abort_exposure(self) -> None:
async def _abort_exposure(self) -> None:
"""Abort the running exposure. Should be implemented by derived class.
Raises:
ValueError: If an error occured.
"""
pass

async def set_image_format(self, fmt: ImageFormat, **kwargs: Any) -> None:
"""Set the camera image format.
Args:
fmt: New image format.
Raises:
ValueError: If format could not be set.
"""
if fmt not in FORMATS:
raise ValueError('Unsupported image format.')
self._image_format = fmt

async def get_image_format(self, **kwargs: Any) -> ImageFormat:
"""Returns the camera image format.
Returns:
Current image format.
"""
return self._image_format

async def list_image_formats(self, **kwargs: Any) -> List[str]:
"""List available image formats.
Returns:
List of available image formats.
"""
return [f.value for f in FORMATS.keys()]


class AsiCoolCamera(AsiCamera, ICooling):
"""A pyobs module for ASI cameras with cooling."""
Expand All @@ -312,18 +331,18 @@ def __init__(self, setpoint: int = -20, **kwargs: Any):
# variables
self._temp_setpoint = setpoint

def open(self) -> None:
async def open(self) -> None:
"""Open module."""
AsiCamera.open(self)
await AsiCamera.open(self)

# no cooling support?
if not self._camera_info['IsCoolerCam']:
raise ValueError('Camera has no support for cooling.')

# activate cooling
self.set_cooling(True, self._temp_setpoint)
await self.set_cooling(True, self._temp_setpoint)

def get_cooling_status(self, **kwargs: Any) -> Tuple[bool, float, float]:
async def get_cooling_status(self, **kwargs: Any) -> Tuple[bool, float, float]:
"""Returns the current status for the cooling.
Returns:
Expand All @@ -343,7 +362,7 @@ def get_cooling_status(self, **kwargs: Any) -> Tuple[bool, float, float]:
power = self._camera.get_control_value(asi.ASI_COOLER_POWER_PERC)[0]
return enabled, temp, power

def get_temperatures(self, **kwargs: Any) -> Dict[str, float]:
async def get_temperatures(self, **kwargs: Any) -> Dict[str, float]:
"""Returns all temperatures measured by this module.
Returns:
Expand All @@ -359,7 +378,7 @@ def get_temperatures(self, **kwargs: Any) -> Dict[str, float]:
'CCD': self._camera.get_control_value(asi.ASI_TEMPERATURE)[0] / 10.
}

def set_cooling(self, enabled: bool, setpoint: float, **kwargs: Any) -> None:
async def set_cooling(self, enabled: bool, setpoint: float, **kwargs: Any) -> None:
"""Enables/disables cooling and sets setpoint.
Args:
Expand All @@ -383,34 +402,5 @@ def set_cooling(self, enabled: bool, setpoint: float, **kwargs: Any) -> None:
log.info('Disabling cooling...')
self._camera.set_control_value(asi.ASI_COOLER_ON, 0)

def set_image_format(self, format: ImageFormat, **kwargs: Any) -> None:
"""Set the camera image format.
Args:
format: New image format.
Raises:
ValueError: If format could not be set.
"""
if format not in FORMATS:
raise ValueError('Unsupported image format.')
self._image_format = format

def get_image_format(self, **kwargs: Any) -> ImageFormat:
"""Returns the camera image format.
Returns:
Current image format.
"""
return self._image_format

def list_image_formats(self, **kwargs: Any) -> List[str]:
"""List available image formats.
Returns:
List of available image formats.
"""
return [f.value for f in FORMATS.keys()]


__all__ = ['AsiCamera', 'AsiCoolCamera']
18 changes: 18 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[tool.poetry]
name = "pyobs-asi"
version = "0.15.0"
description = "pyobs model for ASI cameras"
authors = ["Tim-Oliver Husser <thusser@uni-goettingen.de>"]
license = "MIT"

[tool.poetry.dependencies]

[tool.poetry.dev-dependencies]
python = ">=3.9,<3.11"
astropy = "^5.0"
numpy = "^1.21"
zwoasi = "^0.1"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
1 change: 0 additions & 1 deletion requirements.txt

This file was deleted.

15 changes: 0 additions & 15 deletions setup.py

This file was deleted.

0 comments on commit 5cf5a82

Please sign in to comment.