Skip to content

Commit

Permalink
Merge pull request #127 from pgriffin17/exptime
Browse files Browse the repository at this point in the history
Adjustments for pyscope on real hardware
  • Loading branch information
WWGolay authored Jan 9, 2024
2 parents 6e2d5dc + 0f92d03 commit a9d9dc9
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 25 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,4 @@ dmypy.json
*OmniSim*
!coverage.xml
docs/source/api/auto_api/
pgHardware
80 changes: 77 additions & 3 deletions pyscope/observatory/ascom_camera.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import logging

import numpy as np
from astropy.time import Time

from .ascom_device import ASCOMDevice
from .camera import Camera

Expand All @@ -15,17 +18,54 @@ def __init__(self, identifier, alpaca=False, device_number=0, protocol="http"):
device_number=device_number,
protocol=protocol,
)
self._last_exposure_duration = None
self._last_exposure_start_time = None
self._image_data_type = None
self._DoTranspose = True
self._camera_time = True

def AbortExposure(self):
logger.debug(f"ASCOMCamera.AbortExposure() called")
self._device.AbortExposure()

def SetImageDataType(self):
"""Determine the data type of the image array based on the MaxADU property.
This method is called automatically when the ImageArray property is called
if it has not already been set (initializes to `None`).
It will choose from the following data types based on the MaxADU property:
- numpy.uint8 : (if MaxADU <= 255)
- numpy.uint16 : (default if MaxADU is not defined, or if MaxADU <= 65535)
- numpy.uint32 : (if MaxADU > 65535)
See Also
--------
numpy.uint8
numpy.uint16
numpy.uint32
MaxADU : ASCOM Camera interface property `ASCOM Documentation <https://ascom-standards.org/Help/Developer/html/P_ASCOM_DriverAccess_Camera_MaxADU.htm>`_
"""
logger.debug(f"ASCOMCamera.SetImageDataType() called")
try:
max_adu = self.MaxADU
if max_adu <= 255:
self._image_data_type = np.uint8
elif max_adu <= 65535:
self._image_data_type = np.uint16
else:
self._image_data_type = np.uint32
except:
self._image_data_type = np.uint16

def PulseGuide(self, Direction, Duration):
logger.debug(f"ASCOMCamera.PulseGuide({Direction}, {Duration}) called")
self._device.PulseGuide(Direction, Duration)

def StartExposure(self, Duration, Light):
logger.debug(f"ASCOMCamera.StartExposure({Duration}, {Light}) called")
self._last_exposure_duration = Duration
self._last_exposure_start_time = str(Time.now())
self._device.StartExposure(Duration, Light)

def StopExposure(self):
Expand Down Expand Up @@ -85,6 +125,11 @@ def CameraYSize(self):
logger.debug(f"ASCOMCamera.CameraYSize property called")
return self._device.CameraYSize

@property
def CameraTime(self):
logger.debug(f"ASCOMCamera.CameraTime property called")
return self._camera_time

@property
def CanAbortExposure(self):
logger.debug(f"ASCOMCamera.CanAbortExposure property called")
Expand Down Expand Up @@ -213,7 +258,14 @@ def HeatSinkTemperature(self):
@property
def ImageArray(self):
logger.debug(f"ASCOMCamera.ImageArray property called")
return self._device.ImageArray
img_array = self._device.ImageArray
# Convert to numpy array and check if it is the correct data type
if self._image_data_type is None:
self.SetImageDataType()
img_array = np.array(img_array, dtype=self._image_data_type)
if self._DoTranspose:
img_array = np.transpose(img_array)
return img_array

@property
def ImageReady(self):
Expand All @@ -228,12 +280,34 @@ def IsPulseGuiding(self):
@property
def LastExposureDuration(self):
logger.debug(f"ASCOMCamera.LastExposureDuration property called")
return self._device.LastExposureDuration
last_exposure_duration = self._device.LastExposureDuration
if last_exposure_duration is None or last_exposure_duration == 0:
last_exposure_duration = self.LastInputExposureDuration
self._camera_time = False
return last_exposure_duration

@property
def LastExposureStartTime(self):
logger.debug(f"ASCOMCamera.LastExposureStartTime property called")
return self._device.LastExposureStartTime
last_time = self._device.LastExposureStartTime
""" This code is needed to handle the case of the ASCOM ZWO driver
which returns an empty string instead of None if the camera does not
support the property """
return (
last_time
if last_time != "" and last_time != None
else self._last_exposure_start_time
)

@property
def LastInputExposureDuration(self):
logger.debug(f"ASCOMCamera.LastInputExposureDuration property called")
return self._last_exposure_duration

@LastInputExposureDuration.setter
def LastInputExposureDuration(self, value):
logger.debug(f"ASCOMCamera.LastInputExposureDuration property set to {value}")
self._last_exposure_duration = value

@property
def MaxADU(self):
Expand Down
4 changes: 3 additions & 1 deletion pyscope/observatory/maxim.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import platform
import time

from astropy.time import Time

from .autofocus import Autofocus
from .camera import Camera
from .device import Device
Expand Down Expand Up @@ -124,7 +126,7 @@ def PulseGuide(self, Direction, Duration):
def StartExposure(self, Duration, Light):
logger.debug(f"StartExposure called with Duration={Duration}, Light={Light}")
self._last_exposure_duration = Duration
self._last_exposure_start_time = time.time()
self._last_exposure_start_time = str(Time.now())
self._com_object.Expose(Duration, Light)

def StopExposure(self):
Expand Down
56 changes: 35 additions & 21 deletions pyscope/observatory/observatory.py
Original file line number Diff line number Diff line change
Expand Up @@ -1169,11 +1169,10 @@ def save_last_image(
logger.exception("Image is not ready, cannot be saved")
return False

if (
self.camera.ImageArray is None
or len(self.camera.ImageArray) == 0
or len(self.camera.ImageArray[0]) == 0
):
# Read out the image array
img_array = self.camera.ImageArray

if img_array is None or len(img_array) == 0 or len(img_array) == 0:
logger.exception("Image array is empty, cannot be saved")
return False

Expand All @@ -1182,9 +1181,9 @@ def save_last_image(
hdr["SIMPLE"] = True
hdr["BITPIX"] = (16, "8 unsigned int, 16 & 32 int, -32 & -64 real")
hdr["NAXIS"] = (2, "number of axes")
hdr["NAXIS1"] = (len(self.camera.ImageArray), "fastest changing axis")
hdr["NAXIS1"] = (len(img_array), "fastest changing axis")
hdr["NAXIS2"] = (
len(self.camera.ImageArray[0]),
len(img_array[0]),
"next to fastest changing axis",
)
hdr["BSCALE"] = (1, "physical=BZERO + BSCALE*array_value")
Expand Down Expand Up @@ -1224,7 +1223,7 @@ def save_last_image(
for hist in history:
hdr["HISTORY"] = hist

hdu = fits.PrimaryHDU(self.camera.ImageArray, header=hdr)
hdu = fits.PrimaryHDU(img_array, header=hdr)
hdu.writeto(filename, overwrite=overwrite)

if do_fwhm:
Expand Down Expand Up @@ -2375,13 +2374,17 @@ def _read_out_kwargs(self, dictionary):
"filter_focus_offsets", self.filter_focus_offsets
)

self.rotator_reverse = dictionary.get("rotator_reverse", self.rotator_reverse)
self.rotator_min_angle = dictionary.get(
"rotator_min_angle", self.rotator_min_angle
)
self.rotator_max_angle = dictionary.get(
"rotator_max_angle", self.rotator_max_angle
)
# Not sure if this if statement is a good idea here...
if dictionary.get("rotator_driver", self.rotator_driver) is not None:
self.rotator_reverse = dictionary.get(
"rotator_reverse", self.rotator_reverse
)
self.rotator_min_angle = dictionary.get(
"rotator_min_angle", self.rotator_min_angle
)
self.rotator_max_angle = dictionary.get(
"rotator_max_angle", self.rotator_max_angle
)

self.min_altitude = dictionary.get("min_altitude", self.min_altitude)
self.settle_time = dictionary.get("settle_time", self.settle_time)
Expand Down Expand Up @@ -2422,6 +2425,7 @@ def camera_info(self):
"JD": (None, "Julian date"),
"MJD": (None, "Modified Julian date"),
"MJD-OBS": (None, "Modified Julian date"),
"CAMTIME": (None, "Exposure time from camera (T) or user (F)"),
"EXPTIME": (None, "Exposure time [seconds]"),
"EXPOSURE": (None, "Exposure time [seconds]"),
"SUBEXP": (None, "Subexposure time [seconds]"),
Expand Down Expand Up @@ -2518,8 +2522,13 @@ def camera_info(self):
except:
pass
try:
info["EXPTIME"] = (self.camera.ExposureTime, info["EXPTIME"][1])
info["EXPOSURE"] = (self.camera.ExposureTime, info["EXPOSURE"][1])
last_exposure_duration = self.camera.LastExposureDuration
info["EXPTIME"] = (last_exposure_duration, info["EXPTIME"][1])
info["EXPOSURE"] = (last_exposure_duration, info["EXPOSURE"][1])
except:
pass
try:
info["CAMTIME"] = (self.camera.CameraTime, info["CAMTIME"][1])
except:
pass
try:
Expand Down Expand Up @@ -3684,7 +3693,9 @@ def latitude(self, value):
self._latitude = (
coord.Latitude(value) if value is not None or value != "" else None
)
self.telescope.SiteLatitude = self._latitude.deg
# If connected, set the telescope site latitude
if self.telescope.Connected:
self.telescope.SiteLatitude = self._latitude.deg
self._config["site"]["latitude"] = (
self._latitude.to_string(unit=u.degree, sep="dms", precision=5)
if self._latitude is not None
Expand All @@ -3704,7 +3715,8 @@ def longitude(self, value):
if value is not None or value != ""
else None
)
self.telescope.SiteLongitude = self._longitude.deg
if self.telescope.Connected:
self.telescope.SiteLongitude = self._longitude.deg
self._config["site"]["longitude"] = (
self._longitude.to_string(unit=u.degree, sep="dms", precision=5)
if self._longitude is not None
Expand Down Expand Up @@ -3855,7 +3867,7 @@ def cover_calibrator_alt(self):
def cover_calibrator_alt(self, value):
logger.debug(f"Observatory.cover_calibrator_alt = {value} called")
self._cover_calibrator_alt = (
min(max(float(value), 0), 90) if value is not None or value != "" else None
min(max(float(value), 0), 90) if value is not None and value != "" else None
)
self._config["cover_calibrator"]["cover_calibrator_alt"] = (
str(self._cover_calibrator_alt)
Expand All @@ -3872,7 +3884,9 @@ def cover_calibrator_az(self):
def cover_calibrator_az(self, value):
logger.debug(f"Observatory.cover_calibrator_az = {value} called")
self._cover_calibrator_az = (
min(max(float(value), 0), 360) if value is not None or value != "" else None
min(max(float(value), 0), 360)
if value is not None and value != ""
else None
)
self._config["cover_calibrator"]["cover_calibrator_az"] = (
str(self._cover_calibrator_az)
Expand Down

0 comments on commit a9d9dc9

Please sign in to comment.