From 026a9e1d55c0bb65907b83012f64088bacf09bfd Mon Sep 17 00:00:00 2001 From: WWGolay Date: Fri, 20 Dec 2024 09:44:13 -0700 Subject: [PATCH] Hotfix commits (wwg) --- asidemo.py | 244 +++++++++++++++++++++++++++++ pyscope/observatory/observatory.py | 64 +++++--- pyscope/observatory/zwo_camera.py | 106 +++++++------ pyscope/telrun/sch.py | 4 +- pyscope/telrun/schedtab.py | 2 +- pyscope/telrun/schedtel.py | 12 +- pyscope/telrun/telrun_operator.py | 44 +++++- 7 files changed, 389 insertions(+), 87 deletions(-) create mode 100644 asidemo.py diff --git a/asidemo.py b/asidemo.py new file mode 100644 index 00000000..df492046 --- /dev/null +++ b/asidemo.py @@ -0,0 +1,244 @@ +import ctypes +import pathlib +from ctypes import * +from enum import Enum + +import cv2 +import numpy as np + +lib_path = pathlib.Path( + r"C:\Users\MACRO\Downloads\ASI_Camera_SDK\ASI_Camera_SDK\ASI_Windows_SDK_V1.37\ASI SDK\lib\x64\ASICamera2.dll" +) +print(lib_path) +asi = CDLL(lib_path) # 调用SDK lib +cameraID = 0 # 初始化相关全局变量 +width = 0 +height = 0 +bufferSize = 0 + + +class ASI_IMG_TYPE(Enum): # 定义相关枚举量 + ASI_IMG_RAW8 = 0 + ASI_IMG_RGB24 = 1 + ASI_IMG_RAW16 = 2 + ASI_IMG_Y8 = 3 + ASI_IMG_END = -1 + + +class ASI_BOOL(Enum): + ASI_FALSE = 0 + ASI_TRUE = 1 + + +class ASI_BAYER_PATTERN(Enum): + ASI_BAYER_RG = 0 + ASI_BAYER_BG = 1 + ASI_BAYER_GR = 2 + ASI_BAYER_GB = 3 + + +class ASI_EXPOSURE_STATUS(Enum): + ASI_EXP_IDLE = 0 # idle states, you can start exposure now + ASI_EXP_WORKING = 1 # exposing + ASI_EXP_SUCCESS = 2 # exposure finished and waiting for download + ASI_EXP_FAILED = 3 # exposure failed, you need to start exposure again + + +class ASI_ERROR_CODE(Enum): + ASI_SUCCESS = 0 + ASI_ERROR_INVALID_INDEX = 1 # no camera connected or index value out of boundary + ASI_ERROR_INVALID_ID = 2 # invalid ID + ASI_ERROR_INVALID_CONTROL_TYPE = 3 # invalid control type + ASI_ERROR_CAMERA_CLOSED = 4 # camera didn't open + ASI_ERROR_CAMERA_REMOVED = ( + 5 # failed to find the camera, maybe the camera has been removed + ) + ASI_ERROR_INVALID_PATH = 6 # cannot find the path of the file + ASI_ERROR_INVALID_FILEFORMAT = 7 + ASI_ERROR_INVALID_SIZE = 8 # wrong video format size + ASI_ERROR_INVALID_IMGTYPE = 9 # unsupported image formate + ASI_ERROR_OUTOF_BOUNDARY = 10 # the startpos is out of boundary + ASI_ERROR_TIMEOUT = 11 # timeout + ASI_ERROR_INVALID_SEQUENCE = 12 # stop capture first + ASI_ERROR_BUFFER_TOO_SMALL = 13 # buffer size is not big enough + ASI_ERROR_VIDEO_MODE_ACTIVE = 14 + ASI_ERROR_EXPOSURE_IN_PROGRESS = 15 + ASI_ERROR_GENERAL_ERROR = 16 # general error, eg: value is out of valid range + ASI_ERROR_INVALID_MODE = 17 # the current mode is wrong + ASI_ERROR_END = 18 + + +class ASI_CAMERA_INFO(Structure): # 定义ASI_CAMERA_INFO 结构体 + _fields_ = [ + ("Name", c_char * 64), + ("CameraID", c_int), + ("MaxHeight", c_long), + ("MaxWidth", c_long), + ("IsColorCam", c_int), + ("BayerPattern", c_int), + ("SupportedBins", c_int * 16), + ("SupportedVideoFormat", c_int * 8), + ("PixelSize", c_double), + ("MechanicalShutter", c_int), + ("ST4Port", c_int), + ("IsCoolerCam", c_int), + ("IsUSB3Host", c_int), + ("IsUSB3Camera", c_int), + ("ElecPerADU", c_float), + ("BitDepth", c_int), + ("IsTriggerCam", c_int), + ("Unused", c_char * 16), + ] + + +class ASI_CONTROL_TYPE(Enum): # Control type 定义 ASI_CONTROL_TYPE 结构体 + ASI_GAIN = 0 + ASI_EXPOSURE = 1 + ASI_GAMMA = 2 + ASI_WB_R = 3 + ASI_WB_B = 4 + ASI_OFFSET = 5 + ASI_BANDWIDTHOVERLOAD = 6 + ASI_OVERCLOCK = 7 + ASI_TEMPERATURE = 8 # return 10*temperature + ASI_FLIP = 9 + ASI_AUTO_MAX_GAIN = 10 + ASI_AUTO_MAX_EXP = 11 # micro second + ASI_AUTO_TARGET_BRIGHTNESS = 12 # target brightness + ASI_HARDWARE_BIN = 13 + ASI_HIGH_SPEED_MODE = 14 + ASI_COOLER_POWER_PERC = 15 + ASI_TARGET_TEMP = 16 # not need *10 + ASI_COOLER_ON = 17 + ASI_MONO_BIN = 18 # lead to less grid at software bin mode for color camera + ASI_FAN_ON = 19 + ASI_PATTERN_ADJUST = 20 + ASI_ANTI_DEW_HEATER = 21 + + +def init(): # 相机初始化 + global width + global height + global cameraID + + num = asi.ASIGetNumOfConnectedCameras() # 获取连接相机数 + if num == 0: + print("No camera connection!") + return + print("Number of connected cameras: ", num) + + camInfo = ASI_CAMERA_INFO() + getCameraProperty = asi.ASIGetCameraProperty + getCameraProperty.argtypes = [POINTER(ASI_CAMERA_INFO), c_int] + getCameraProperty.restype = c_int + + for i in range(0, num): + asi.ASIGetCameraProperty(camInfo, i) # 以index获取相机属性 + + cameraID = camInfo.CameraID + + err = asi.ASIOpenCamera(cameraID) # 打开相机 + if err != 0: + return + print("Open Camera Success") + + err = asi.ASIInitCamera(cameraID) # 初始化相机 + if err != 0: + return + print("Init Camera Success") + + width = camInfo.MaxWidth + height = camInfo.MaxHeight + _bin = 1 + startx = 0 + starty = 0 + imageType = ASI_IMG_TYPE.ASI_IMG_RAW16 + + err = asi.ASISetROIFormat( + cameraID, width, height, _bin, imageType.value + ) # SetROIFormat + if err != 0: + print("Set ROI Format Fail") + return + print("Set ROI Format Success") + + asi.ASISetStartPos(cameraID, startx, starty) # SetStartPos + + +def getFrame(): # 获取图像帧 + global bufferSize + buffersize = width * height + + buffer = (c_ubyte * 2 * buffersize)() + + err = asi.ASIStartExposure(cameraID, 0) # 开始曝光 + if err != 0: + print("Start Exposure Fail") + return + print("Start Exposure Success") + + getExpStatus = asi.ASIGetExpStatus + getExpStatus.argtypes = [c_int, POINTER(c_int)] + getExpStatus.restype = c_int + expStatus = c_int() + + getDataAfterExp = asi.ASIGetDataAfterExp + getDataAfterExp.argtypes = [c_int, POINTER(c_ubyte) * 2, c_int] + getDataAfterExp.restype = c_int + buffer = (c_ubyte * 2 * buffersize)() + + getExpStatus(cameraID, expStatus) + print("status: ", expStatus) + + # c_int转int + sts = expStatus.value + while sts == ASI_EXPOSURE_STATUS.ASI_EXP_WORKING.value: # 获取曝光状态 + getExpStatus(cameraID, expStatus) + sts = expStatus.value + + if sts != ASI_EXPOSURE_STATUS.ASI_EXP_SUCCESS.value: + print("Exposure Fail") + return + + err = getDataAfterExp(cameraID, buffer, buffersize) # 曝光成功,获取buffer + if err != 0: + print("GetDataAfterExp Fail") + return + print("getDataAfterExp Success") + + return buffer + + +def closeCamera(): # 关闭相机 + err = asi.ASIStopVideoCapture(cameraID) + if err != 0: + print("Stop Capture Fail") + return + print("Stop Capture Success") + + err = asi.ASICloseCamera(cameraID) + if err != 0: + print("Close Camera Fail") + return + print("Close Camera Success") + + +def setExpValue(val): # 设置曝光时间 + # setControlValue = asi.ASISetControlValue + # setControlValue.argtypes = [c_int, ASI_CONTROL_TYPE, c_long, ASI_BOOL] + # setControlValue.restype = c_int + err = asi.ASISetControlValue( + cameraID, ASI_CONTROL_TYPE.ASI_EXPOSURE.value, val, ASI_BOOL.ASI_FALSE.value + ) + + +def setGainValue(val): # 设置增益 + err = asi.ASISetControlValue( + cameraID, ASI_CONTROL_TYPE.ASI_GAIN.value, val, ASI_BOOL.ASI_FALSE.value + ) + + +def setBiasValue(val): + err = asi.ASISetControlValue( + cameraID, ASI_CONTROL_TYPE.ASI_OFFSET.value, val, ASI_BOOL.ASI_FALSE.value + ) diff --git a/pyscope/observatory/observatory.py b/pyscope/observatory/observatory.py index 07b4fb19..239949c4 100644 --- a/pyscope/observatory/observatory.py +++ b/pyscope/observatory/observatory.py @@ -1312,7 +1312,7 @@ def save_last_image( hdr = self.generate_header_info( filename, frametyp, custom_header, history, maxim, allowed_overwrite ) - # update RADECSYS key to RADECSYSa + # update RADECSYS key to RADECSYSa if "RADECSYS" in hdr: hdr["RADECSYSa"] = hdr["RADECSYS"] hdr.pop("RADECSYS", None) @@ -1321,7 +1321,6 @@ def save_last_image( logger.exception(f"Error generating header information: {e}") hdu = fits.PrimaryHDU(img_array) - hdu.writeto(filename, overwrite=overwrite) return True @@ -1760,6 +1759,7 @@ def run_autofocus( use_current_pointing=False, save_images=False, save_path=None, + binning=2, # HOTFIX for 6200 ): """Runs the autofocus routine""" @@ -1800,7 +1800,9 @@ def run_autofocus( autofocus_time = self.observatory_time.isot.replace(":", "-") if save_path is None: - save_path = Path(os.getcwd(),"images","autofocus",f"{autofocus_time}").resolve() + save_path = Path( + os.getcwd(), "images", "autofocus", f"{autofocus_time}" + ).resolve() focus_values = [] for i, position in enumerate(test_positions): @@ -1819,6 +1821,9 @@ def run_autofocus( time.sleep(0.1) logger.info("Focuser moved.") + # Set binning + self.camera.BinX = binning + logger.info("Taking %s second exposure..." % exposure) self.camera.StartExposure(exposure, True) while not self.camera.ImageReady: @@ -1827,15 +1832,14 @@ def run_autofocus( logger.info("Calculating mean star fwhm...") if not save_images: - save_path = Path(tempfile.gettempdir(),f"{autofocus_time}").resolve() + save_path = Path( + tempfile.gettempdir(), f"{autofocus_time}" + ).resolve() if not save_path.exists(): save_path.mkdir(parents=True) - fname = ( - save_path - / f"FOCUS{int(position)}.fit" - ) + fname = save_path / f"FOCUS{int(position)}.fit" - self.save_last_image(fname, frametyp="Focus") + self.save_last_image(fname, frametyp="Focus", overwrite=True) # Removed for hotfix for PlateSolve2 # cat = detect_sources_photutils( @@ -1865,32 +1869,42 @@ def run_autofocus( # result_err = np.sqrt(np.diag(pcov))[0] # Run platesolve 2 from cmd line - platesolve2_path = Path(r'C:\Program Files (x86)\PlaneWave Instruments\PlaneWave Interface 4\PlateSolve2\PlateSolve2.exe').resolve() - #print(platesolve2_path) - results_path = Path(save_path / 'results.txt').resolve() - #print(f"Results path: {results_path}") - image_path = glob.glob(str(save_path / '*.fit'))[0] - #print(f"Image path: {image_path}") + platesolve2_path = Path( + r"C:\Program Files (x86)\PlaneWave Instruments\PlaneWave Interface 4\PlateSolve2\PlateSolve2.exe" + ).resolve() + # print(platesolve2_path) + results_path = Path(save_path / "results.txt").resolve() + # print(f"Results path: {results_path}") + image_path = glob.glob(str(save_path / "*.fit"))[0] + # print(f"Image path: {image_path}") logger.info("Running PlateSolve2...") - procid = os.spawnv(os.P_NOWAIT, platesolve2_path, [f'"{platesolve2_path}" {image_path},{results_path},180']) - + procid = os.spawnv( + os.P_NOWAIT, + platesolve2_path, + [f'"{platesolve2_path}" {image_path},{results_path},180'], + ) + while results_path.exists() == False: time.sleep(0.1) # Read results - with open(results_path, 'r') as f: + with open(results_path, "r") as f: results = f.read() - while results == '': + while results == "": time.sleep(0.1) - with open(results_path, 'r') as f: + with open(results_path, "r") as f: results = f.read() print(f"Results path: {results_path}") print(results) - results = results.split(',') + results = results.split(",") # Remove whitespace results = [x.strip() for x in results] print(results) - result, fwhm, result_err = float(results[0]), float(results[1]), float(results[2]) + result, fwhm, result_err = ( + float(results[0]), + float(results[1]), + float(results[2]), + ) print(f"Best focus: {result}") print(f"FWHM: {fwhm}") print(f"Focus error: {result_err}") @@ -1973,6 +1987,7 @@ def repositioning( tolerance=3, exposure=10, readout=0, + binning=2, # HOTFIX: HARDCODED FOR ASI6200 save_images=False, save_path="./", settle_time=5, @@ -2096,6 +2111,7 @@ def repositioning( logger.info("Taking %.2f second exposure" % exposure) self.camera.ReadoutMode = readout + self.camera.BinX = binning self.camera.StartExposure(exposure, True) while not self.camera.ImageReady: time.sleep(0.1) @@ -2303,11 +2319,11 @@ def take_flats( self.telescope.Tracking = False logger.info("Tracking off") - '''if self.cover_calibrator is not None: + """if self.cover_calibrator is not None: if self.cover_calibrator.CoverState != "NotPresent": logger.info("Opening the cover calibrator") self.cover_calibrator.OpenCover() - logger.info("Cover open")''' + logger.info("Cover open")""" if gain is not None: logger.info("Setting the camera gain to %i" % gain) diff --git a/pyscope/observatory/zwo_camera.py b/pyscope/observatory/zwo_camera.py index 27b444c4..eefe0d45 100644 --- a/pyscope/observatory/zwo_camera.py +++ b/pyscope/observatory/zwo_camera.py @@ -1,16 +1,17 @@ import logging - +import pathlib import numpy as np -from astropy.time import Time -import pathlib import zwoasi as asi +from astropy.time import Time from .camera import Camera logger = logging.getLogger(__name__) -lib_path = pathlib.Path(r'C:\Users\MACRO\Downloads\ASI_Camera_SDK\ASI_Camera_SDK\ASI_Windows_SDK_V1.37\ASI SDK\lib\x64\ASICamera2.dll') +lib_path = pathlib.Path( + r"C:\Users\MACRO\Downloads\ASI_Camera_SDK\ASI_Camera_SDK\ASI_Windows_SDK_V1.37\ASI SDK\lib\x64\ASICamera2.dll" +) print(lib_path) asi.init(lib_path) @@ -20,12 +21,13 @@ class ZWOCamera(Camera): def __init__(self, device_number=0): logger.debug(f"ZWOCamera.__init__({device_number})") - - self._device = asi.Camera(device_number) self._camera_info = self._device.get_camera_property() # Use minimum USB bandwidth permitted - self._device.set_control_value(asi.ASI_BANDWIDTHOVERLOAD, self._device.get_controls()['BandWidth']['MinValue']) + self._device.set_control_value( + asi.ASI_BANDWIDTHOVERLOAD, + self._device.get_controls()["BandWidth"]["MinValue"], + ) self._controls = self._device.get_controls() self._last_exposure_duration = None self._last_exposure_start_time = None @@ -33,8 +35,8 @@ def __init__(self, device_number=0): self._DoTranspose = True self._camera_time = True self._binX = 1 - self._NumX = self._camera_info['MaxWidth'] - self._NumY = self._camera_info['MaxHeight'] + self._NumX = self._camera_info["MaxWidth"] + self._NumY = self._camera_info["MaxHeight"] self._StartX = 0 self._StartY = 0 @@ -94,7 +96,7 @@ def StartExposure(self, Duration, Light): width = self.NumX height = self.NumY - + image_type = asi.ASI_IMG_RAW16 whbi_old = self._device.get_roi_format() whbi_new = [width, height, bins, image_type] @@ -142,12 +144,16 @@ def BinX(self, value): @property def BinY(self): - logger.debug(f"ASCOMCamera.BinY property called - Symmetric binning only - use BinX property") + logger.debug( + f"ASCOMCamera.BinY property called - Symmetric binning only - use BinX property" + ) return self.BinX @BinY.setter def BinY(self, value): - logger.debug(f"ASCOMCamera.BinY setter called - Symmetric binning only - use BinX property") + logger.debug( + f"ASCOMCamera.BinY setter called - Symmetric binning only - use BinX property" + ) pass @property @@ -158,12 +164,12 @@ def CameraState(self): @property def CameraXSize(self): logger.debug(f"ASCOMCamera.CameraXSize property called") - return self._camera_info['MaxWidth'] + return self._camera_info["MaxWidth"] @property def CameraYSize(self): logger.debug(f"ASCOMCamera.CameraYSize property called") - return self._camera_info['MaxHeight'] + return self._camera_info["MaxHeight"] @property def CameraTime(self): @@ -198,7 +204,7 @@ def CanPulseGuide(self): @property def CanSetCCDTemperature(self): logger.debug(f"ASCOMCamera.CanSetCCDTemperature property called") - return self._camera_info['IsCoolerCam'] + return self._camera_info["IsCoolerCam"] @property def CanStopExposure(self): @@ -208,7 +214,7 @@ def CanStopExposure(self): @property def CCDTemperature(self): logger.debug(f"ASCOMCamera.CCDTemperature property called") - return self._device.get_control_value(asi.ASI_TEMPERATURE)[0]/10 + return self._device.get_control_value(asi.ASI_TEMPERATURE)[0] / 10 @property def CoolerOn(self): @@ -229,46 +235,46 @@ def CoolerPower(self): def DriverVersion(self): logger.debug(f"ASCOMCamera.DriverVersion property called") return "Custom Driver" - + @property def DriverInfo(self): logger.debug(f"ASCOMCamera.DriverInfo property called") return ["Custom Driver for ZWO ASI Cameras", 1] - + @property def Description(self): logger.debug(f"ASCOMCamera.Description property called") - return self._camera_info['Name'] + return self._camera_info["Name"] @property def ElectronsPerADU(self): logger.debug(f"ASCOMCamera.ElectronsPerADU() property called") - return self._device.get_camera_property()['ElecPerADU']*self.BinX*self.BinY - + return self._device.get_camera_property()["ElecPerADU"] * self.BinX * self.BinY + @property def Exposure(self): - ''' + """ Get the exposure time in seconds. The exposure time is the time that the camera will be collecting light from the sky. The exposure time must be greater than or equal to the minimum exposure time and less than or equal to the maximum exposure time. The exposure time is specified in seconds. - + Returns ------- float The exposure time in seconds. - ''' + """ logger.debug(f"ZWOASI.Exposure property called") # Convert to seconds return self._device.get_control_value(asi.ASI_EXPOSURE)[0] / 1e6 - + @Exposure.setter def Exposure(self, value): - ''' + """ Set the exposure time in seconds. The exposure time is the time that the camera will be collecting light from the sky. The exposure time must be greater than or equal to the minimum exposure time and less than or equal to the maximum exposure time. The exposure time is specified in seconds. - + Parameters ---------- value : float The exposure time in seconds. - ''' + """ logger.debug(f"ZWOASI.Exposure property set to {value}") # Convert to microseconds value = int(value * 1e6) @@ -277,15 +283,15 @@ def Exposure(self, value): @property def ExposureMax(self): logger.debug(f"ASCOMCamera.ExposureMax property called") - exp_max = self._controls['Exposure']['MaxValue'] - exp_max /= 1E6 + exp_max = self._controls["Exposure"]["MaxValue"] + exp_max /= 1e6 return exp_max @property def ExposureMin(self): logger.debug(f"ASCOMCamera.ExposureMin property called") - exp_min = self._controls['Exposure']['MinValue'] - exp_min /= 1E6 + exp_min = self._controls["Exposure"]["MinValue"] + exp_min /= 1e6 return exp_min @property @@ -324,12 +330,12 @@ def Gain(self, value): @property def GainMax(self): logger.debug(f"ASCOMCamera.GainMax property called") - return self._controls['Gain']['MaxValue'] + return self._controls["Gain"]["MaxValue"] @property def GainMin(self): logger.debug(f"ASCOMCamera.GainMin property called") - return self._controls['Gain']['MinValue'] + return self._controls["Gain"]["MinValue"] @property def Gains(self): @@ -396,7 +402,7 @@ def ImageArray(self): img = np.frombuffer(data, dtype=np.uint8) shape.append(3) else: - raise ValueError('Unsupported image type') + raise ValueError("Unsupported image type") img = img.reshape(shape) # Done by default in zwoasi # if self._DoTranspose: @@ -453,12 +459,12 @@ def MaxADU(self): @property def MaxBinX(self): logger.debug(f"ASCOMCamera.MaxBinX property called") - return self._camera_info['SupportedBins'][-1] + return self._camera_info["SupportedBins"][-1] @property def MaxBinY(self): logger.debug(f"ASCOMCamera.MaxBinY property called") - return self._camera_info['SupportedBins'][-1] + return self._camera_info["SupportedBins"][-1] @property def NumX(self): @@ -469,8 +475,8 @@ def NumX(self): def NumX(self, value): logger.debug(f"ASCOMCamera.NumX property set to {value}") width = value - if width%8 != 0: - width -= width%8 # Make width a multiple of 8 + if width % 8 != 0: + width -= width % 8 # Make width a multiple of 8 self._NumX = width @property @@ -483,8 +489,8 @@ def NumY(self, value): logger.debug(f"ASCOMCamera.NumY property set to {value}") # Set height to next multiple of 2 height = value - if height%2 != 0: - height -= 1 # Make height even + if height % 2 != 0: + height -= 1 # Make height even self._NumY = height @property @@ -500,12 +506,12 @@ def Offset(self, value): @property def OffsetMax(self): logger.debug(f"ASCOMCamera.OffsetMax property called") - return self._controls['Offset']['MaxValue'] + return self._controls["Offset"]["MaxValue"] @property def OffsetMin(self): logger.debug(f"ASCOMCamera.OffsetMin property called") - return self._controls['Offset']['MinValue'] + return self._controls["Offset"]["MinValue"] @property def Offsets(self): @@ -520,13 +526,13 @@ def PercentCompleted(self): @property def PixelSizeX(self): logger.debug(f"ASCOMCamera.PixelSizeX property called") - return self._camera_info['PixelSize']*self.BinX + return self._camera_info["PixelSize"] * self.BinX @property def PixelSizeY(self): logger.debug(f"ASCOMCamera.PixelSizeY property called") - return self._camera_info['PixelSize']*self.BinY - + return self._camera_info["PixelSize"] * self.BinY + @property def ReadoutMode(self): logger.debug(f"ASCOMCamera.ReadoutMode property called") @@ -545,17 +551,17 @@ def ReadoutModes(self): @property def SensorName(self): logger.debug(f"ASCOMCamera.SensorName property called") - return self._camera_info['Name'] - + return self._camera_info["Name"] + @property def Name(self): logger.debug(f"ASCOMCamera.Name property called") - return self._camera_info['Name'] + return self._camera_info["Name"] @property def SensorType(self): logger.debug(f"ASCOMCamera.SensorType property called") - return 'CMOS' + return "CMOS" @property def SetCCDTemperature(self): diff --git a/pyscope/telrun/sch.py b/pyscope/telrun/sch.py index fd584047..b932d439 100644 --- a/pyscope/telrun/sch.py +++ b/pyscope/telrun/sch.py @@ -537,13 +537,13 @@ def read( if "repositioning" in line.keys(): if ( line["repositioning"].startswith("t") - or line["repositioning"]=="1" + or line["repositioning"] == "1" or line["repositioning"].startswith("y") ): repositioning = (-1, -1) elif ( line["repositioning"].startswith("f") - or line["repositioning"]=="0" + or line["repositioning"] == "0" or line["repositioning"].startswith("n") ): repositioning = (0, 0) diff --git a/pyscope/telrun/schedtab.py b/pyscope/telrun/schedtab.py index 91fc9ac3..a809e347 100644 --- a/pyscope/telrun/schedtab.py +++ b/pyscope/telrun/schedtab.py @@ -93,7 +93,7 @@ def blocks_to_table(observing_blocks): ), format="mjd", ) - + # print(f"Target to hmsdms: {block.target.to_string('hmsdms')}") t["target"] = coord.SkyCoord( [ ( diff --git a/pyscope/telrun/schedtel.py b/pyscope/telrun/schedtel.py index 65894208..a685ee7b 100644 --- a/pyscope/telrun/schedtel.py +++ b/pyscope/telrun/schedtel.py @@ -1159,15 +1159,15 @@ def plot_schedule_sky_cli(schedule_table, observatory): # ] # ax.legend(*zip(*unique), loc=(1.1, 0)) - - # Add title to plot based on date - t0 = np.min(schedule_table["start_time"]) # -1 corrects for UTC to local time, should be cleaned up + t0 = np.min( + schedule_table["start_time"] + ) # -1 corrects for UTC to local time, should be cleaned up t0 = astrotime.Time(t0, format="mjd") t0.format = "iso" - - ax.set_title(f"Observing Schedule: Night of {t0.to_string().split(' ')[0]} UTC", - fontsize=14 + + ax.set_title( + f"Observing Schedule: Night of {t0.to_string().split(' ')[0]} UTC", fontsize=14 ) ax.legend(labels, loc=(1.1, 0)) diff --git a/pyscope/telrun/telrun_operator.py b/pyscope/telrun/telrun_operator.py index 277c472f..ff0be2ff 100644 --- a/pyscope/telrun/telrun_operator.py +++ b/pyscope/telrun/telrun_operator.py @@ -195,6 +195,7 @@ def __init__(self, telhome="./", gui=True, **kwargs): self._autofocus_nsteps = 5 self._autofocus_step_size = 500 self._autofocus_use_current_pointing = False + self._autofocus_binning = 1 self._autofocus_timeout = 180 self._repositioning_wcs_solver = "astrometry_net_wcs" self._repositioning_max_stability_time = 600 # seconds @@ -208,6 +209,7 @@ def __init__(self, telhome="./", gui=True, **kwargs): self._repositioning_save_images = False self._repositioning_save_path = self._images_path / "repositioning" self._repositioning_timeout = 180 + self._repositioning_binning = 1 self._wcs_solver = "astrometry_net_wcs" self._wcs_filters = None self._wcs_timeout = 30 @@ -305,6 +307,9 @@ def __init__(self, telhome="./", gui=True, **kwargs): "autofocus_use_current_pointing", fallback=self._autofocus_use_current_pointing, ) + self._autofocus_binning = self._config.getint( + "autofocus", "autofocus_binning", fallback=self._autofocus_binning + ) self._autofocus_timeout = self._config.getfloat( "autofocus", "autofocus_timeout", fallback=self._autofocus_timeout ) @@ -370,6 +375,11 @@ def __init__(self, telhome="./", gui=True, **kwargs): "repositioning_timeout", fallback=self._repositioning_timeout, ) + self._repositioning_binning = self._config.getint( + "repositioning", + "repositioning_binning", + fallback=self._repositioning_binning, + ) self._wcs_solver = self._config.get( "wcs", "wcs_solver", fallback=self._wcs_solver ) @@ -498,6 +508,9 @@ def __init__(self, telhome="./", gui=True, **kwargs): self.autofocus_use_current_pointing = kwargs.get( "autofocus_use_current_pointing", self._autofocus_use_current_pointing ) + self.autofocus_binning = kwargs.get( + "autofocus_binning", self._autofocus_binning + ) self.autofocus_timeout = kwargs.get( "autofocus_timeout", self._autofocus_timeout ) @@ -537,6 +550,9 @@ def __init__(self, telhome="./", gui=True, **kwargs): self.repositioning_timeout = kwargs.get( "repositioning_timeout", self._repositioning_timeout ) + self.repositioning_binning = kwargs.get( + "repositioning_binning", self._repositioning_binning + ) self.wcs_solver = kwargs.get("wcs_solver", self._wcs_solver) self.wcs_filters = kwargs.get("wcs_filters", self._wcs_filters) self.wcs_timeout = kwargs.get("wcs_timeout", self._wcs_timeout) @@ -1449,10 +1465,8 @@ def execute_block(self, *args, slew=True, **kwargs): # TODO: Make this better - temporary fix 2024-11-15 # Check if focuser is outside of self.autofocus_midpoint +/- 1000 if ( - self.observatory.focuser.Position - < self.autofocus_midpoint - 1000 - or self.observatory.focuser.Position - > self.autofocus_midpoint + 1000 + self.observatory.focuser.Position < self.autofocus_midpoint - 1000 + or self.observatory.focuser.Position > self.autofocus_midpoint + 1000 ): logger.info( "Focuser position is outside of autofocus_midpoint +/- 1000, moving to autofocus_midpoint..." @@ -1479,6 +1493,7 @@ def execute_block(self, *args, slew=True, **kwargs): nsteps=self.autofocus_nsteps, step_size=self.autofocus_step_size, use_current_pointing=self.autofocus_use_current_pointing, + binning=self.autofocus_binning, ) self._status_event.set() t.join() @@ -1725,6 +1740,7 @@ def execute_block(self, *args, slew=True, **kwargs): do_initial_slew=slew, readout=self.default_readout, solver=self.repositioning_wcs_solver, + binning=self.repositioning_binning, ) self._camera_status = "Idle" self._telescope_status = "Idle" @@ -2862,6 +2878,15 @@ def autofocus_use_current_pointing(self, value): self._autofocus_use_current_pointing ) + @property + def autofocus_binning(self): + return self._autofocus_binning + + @autofocus_binning.setter + def autofocus_binning(self, value): + self._autofocus_binning = int(value) + self._config["autofocus"]["autofocus_binning"] = str(self._autofocus_binning) + @property def autofocus_timeout(self): return self._autofocus_timeout @@ -3025,6 +3050,17 @@ def repositioning_timeout(self, value): self._repositioning_timeout ) + @property + def repositioning_binning(self): + return self._repositioning_binning + + @repositioning_binning.setter + def repositioning_binning(self, value): + self._repositioning_binning = value + self._config["repositioning"]["repositioning_binning"] = str( + self._repositioning_binning + ) + @property def wcs_solver(self): return self._wcs_solver