diff --git a/.github/workflows/deploy_gcp.yml b/.github/workflows/deploy_gcp.yml index 8211e2ed..359d2e46 100644 --- a/.github/workflows/deploy_gcp.yml +++ b/.github/workflows/deploy_gcp.yml @@ -79,7 +79,7 @@ jobs: version: '>= 416.0.0' - id: 'build_docker_and_push' run: |- - mkdir -p ./astrometry_data/ && gsutil -m cp -n gs://astrometry_data/* ./astrometry_data/ + mkdir -p ./astrometry_data/ && gcloud storage rsync gs://astrometry_data/ ./astrometry_data/ --recursive --verbosity=info --delete-unmatched-destination-objects docker image prune -f && docker buildx prune -f && docker container prune -f docker buildx build --platform linux/amd64 -t ${{ vars.AR_URL }}/${{ vars.IMAGE_NAME }} . docker tag ${{ vars.AR_URL }}/${{ vars.IMAGE_NAME }} ${{ vars.AR_URL }}/${{ vars.IMAGE_NAME }}:${{ github.sha }} diff --git a/Camera/AbstractCamera.py b/Camera/AbstractCamera.py index 5a4d56a1..c0aaa465 100644 --- a/Camera/AbstractCamera.py +++ b/Camera/AbstractCamera.py @@ -262,7 +262,7 @@ def get_calibration_directory(self, "calibrations", calibration_name, self.uid) - if calibration_name == "dark": + if calibration_name in ["dark", "offset"]: if temperature is None: temperature = self.get_temperature() image_dir = os.path.join( diff --git a/Camera/IndiASICamera290MMMini.py b/Camera/IndiASICamera290MMMini.py deleted file mode 100644 index 5c167395..00000000 --- a/Camera/IndiASICamera290MMMini.py +++ /dev/null @@ -1,19 +0,0 @@ -# Basic stuff -import numpy as np - -# Local stuff -from Camera.IndiASICamera import IndiASICamera -from Camera.IndiASICameraNonCool import IndiASICameraNonCool - -class IndiASICamera290MMMini(IndiASICameraNonCool): - def __init__(self, serv_time, config=None, - connect_on_create=True): - - # Parent initialization - super().__init__( - serv_time=serv_time, - config=config, - connect_on_create=connect_on_create) - - def set_offset(self, value): - pass diff --git a/Camera/IndiAbstractCamera.py b/Camera/IndiAbstractCamera.py index 1e4e0f7d..3362656e 100644 --- a/Camera/IndiAbstractCamera.py +++ b/Camera/IndiAbstractCamera.py @@ -27,7 +27,10 @@ def __init__(self, serv_time, config=None, connect_on_create=True): def park(self): self.logger.debug(f"Parking camera {self.camera_name}") - self.focuser.park() + if self.focuser: + self.focuser.park_focuser() + # if self.filter_wheel: + # self.filter_wheel.park_filterwheel() self.deinitialize_working_conditions() self.disconnect() self.stop_indi_server() @@ -37,7 +40,10 @@ def park(self): def unpark(self): self.logger.debug(f"Unparking camera {self.camera_name} with a reset-like behaviour") self.park() - self.focuser.unpark() + if self.focuser: + self.focuser.unpark_focuser() + # if self.filter_wheel: + # self.filter_wheel.unpark_filterwheel() self.start_indi_server() self.start_indi_driver() self.connect(connect_device=True) @@ -89,10 +95,12 @@ def initialize_working_conditions(self): self.logger.debug(f"Camera {self.camera_name} successfully initialized to working conditions") def deinitialize_working_conditions(self): - if self.is_initialized: - self.logger.debug(f"Camera {self.camera_name} deinitializing from working conditions") + self.logger.debug(f"Camera {self.camera_name} deinitializing from working conditions") + if self._is_initialized: self.set_cooling_off() - self.logger.debug(f"Camera {self.camera_name} successfully deinitialized") + else: + self.logger.debug(f"Camera {self.camera_name} No need to deinit, as camera was not in initialized state") + self.logger.debug(f"Camera {self.camera_name} successfully deinitialized") def take_exposure(self, exposure_time, filename, *args, **kwargs): """ diff --git a/Camera/IndiCamera.py b/Camera/IndiCamera.py index 4eafebd0..50cd3e62 100644 --- a/Camera/IndiCamera.py +++ b/Camera/IndiCamera.py @@ -51,7 +51,7 @@ def __init__(self, logger=None, config=None, connect_on_create=True): autofocus_merit_function="half_flux_radius", focuser=dict( module="IndiFocuser", - focuser_name="Focuser Simulator", + device_name="Focuser Simulator", port="/dev/ttyUSB0", focus_range=dict( min=1, @@ -82,17 +82,17 @@ def __init__(self, logger=None, config=None, connect_on_create=True): self.connect() # Specific initialization - self.pointing_seconds = float(config['pointing_seconds']) + self.pointing_seconds = float(config.get('pointing_seconds',5)) self.default_exp_time_sec = config.get('default_exp_time_sec', 5) self.default_gain = config.get('default_gain', 50) self.default_offset = config.get('default_offset', 30) - self.adjust_center_x = float(config['adjust_center_x']) - self.adjust_center_y = float(config['adjust_center_y']) - self.adjust_roi_search_size = int(config['adjust_roi_search_size']) - self.adjust_pointing_seconds = float(config['adjust_pointing_seconds']) - self.autofocus_seconds = float(config['autofocus_seconds']) - self.autofocus_roi_size = int(config['autofocus_roi_size']) - self.autofocus_merit_function = config['autofocus_merit_function'] + self.adjust_center_x = float(config.get('adjust_center_x',500)) + self.adjust_center_y = float(config.get('adjust_center_y',500)) + self.adjust_roi_search_size = int(config.get('adjust_roi_search_size',50)) + self.adjust_pointing_seconds = float(config.get('adjust_pointing_seconds',5)) + self.autofocus_seconds = float(config.get('autofocus_seconds',5)) + self.autofocus_roi_size = config.get('autofocus_roi_size', None) + self.autofocus_merit_function = config.get("autofocus_merit_function", "half_flux_radius") self._setup_focuser(config, connect_on_create) self._setup_filter_wheel(config, connect_on_create) @@ -115,7 +115,6 @@ def _setup_focuser(self, config, connect_on_create): focuser = load_module('Focuser.'+focuser_name) #TODO TN, we need to better handle optional connection of focuser self.focuser = getattr(focuser, focuser_name)( - logger=None, config=cfg, connect_on_create=connect_on_create) except Exception as e: @@ -199,7 +198,7 @@ def shoot_async(self): def get_remaining_exposure_time(self): return self.get_number('CCD_EXPOSURE')['CCD_EXPOSURE_VALUE'] - def get_thumbnail(self, exp_time_sec, thumbnail_size): + def get_thumbnail(self, exp_time_sec, thumbnail_size=None): """ There are 4 cases: -ccd size is even, thumb size is even @@ -215,26 +214,28 @@ def get_thumbnail(self, exp_time_sec, thumbnail_size): 2.5-2.5 = 0 ok 2.5-1.5 = 1 ok """ - sensor_size = self.get_sensor_size() - thumbnail_size = min(thumbnail_size, sensor_size["CCD_MAX_X"]) - thumbnail_size = min(thumbnail_size, sensor_size["CCD_MAX_Y"]) - center_x = sensor_size["CCD_MAX_X"] / 2 - center_y = sensor_size["CCD_MAX_Y"] / 2 - left_most = np.floor(center_x - thumbnail_size / 2) - top_most = np.floor(center_y - thumbnail_size / 2) - roi = {'X': left_most, 'Y': top_most, 'WIDTH': thumbnail_size, - 'HEIGHT': thumbnail_size} - self.logger.debug(f"Setting camera {self.name} roi to {roi}") - self.set_roi(roi) + if thumbnail_size is not None: + sensor_size = self.get_sensor_size() + thumbnail_size = min(thumbnail_size, sensor_size["CCD_MAX_X"]) + thumbnail_size = min(thumbnail_size, sensor_size["CCD_MAX_Y"]) + center_x = sensor_size["CCD_MAX_X"] / 2 + center_y = sensor_size["CCD_MAX_Y"] / 2 + left_most = np.floor(center_x - thumbnail_size / 2) + top_most = np.floor(center_y - thumbnail_size / 2) + roi = {'X': left_most, 'Y': top_most, 'WIDTH': thumbnail_size, + 'HEIGHT': thumbnail_size} + self.logger.debug(f"Setting camera {self.name} roi to {roi}") + self.set_roi(roi) old_exp_time_sec = self.exp_time_sec self.exp_time_sec = exp_time_sec self.shoot_async() self.synchronize_with_image_reception() fits = self.get_received_image() - roi = {'X': 0, 'Y': 0, 'WIDTH': sensor_size["CCD_MAX_X"], - 'HEIGHT': sensor_size["CCD_MAX_Y"]} - self.logger.debug(f"Resetting camera {self.name} roi to {roi}") - self.set_roi(roi) + if thumbnail_size is not None: + roi = {'X': 0, 'Y': 0, 'WIDTH': sensor_size["CCD_MAX_X"], + 'HEIGHT': sensor_size["CCD_MAX_Y"]} + self.logger.debug(f"Resetting camera {self.name} roi to {roi}") + self.set_roi(roi) self.exp_time_sec = old_exp_time_sec return fits @@ -300,6 +301,7 @@ def set_temperature(self, temperature): if isinstance(temperature, u.Quantity): temperature = temperature.to(u.deg_C).value if np.isfinite(temperature): + self.logger.debug(f"Camera {self.device_name} setting temperature to {temperature}") self.set_number('CCD_TEMPERATURE', {'CCD_TEMPERATURE_VALUE': temperature}, sync=True, timeout=1200) @@ -308,6 +310,9 @@ def set_cooling_on(self): # No sync, because that's the way it works on indi for the CCD_COOLER property, it stays yellow in the interface self.set_switch('CCD_COOLER', ['COOLER_ON'], sync=False) + def get_cooling_power(self): + self.get_number("CCD_COOLER_POWER")["CCD_COOLER_VALUE"] + def set_cooling_off(self): self.set_switch('CCD_COOLER', ['COOLER_OFF'], sync=True, timeout=self.timeout) diff --git a/Dockerfile b/Dockerfile index dbf05b07..a902acff 100644 --- a/Dockerfile +++ b/Dockerfile @@ -150,7 +150,7 @@ RUN mkdir -p $HOME/projects/astrometry.net \ # Now Download astrometry.net index files -- This needs to be moved when gsutil is updated # RUN pyenv install 3.11 \ # && pyenv global 3.11 \ -# && gsutil -m cp gs://astrometry_data/* /usr/local/astrometry/data/ \ +# && gcloud storage rsync gs://astrometry_data/ /usr/local/astrometry/data/ --recursive --verbosity=info --delete-unmatched-destination-objects \ # && pyenv global $PYTHON_VERSION #RUN mv /opt/remote_observatory/astrometry_data/* /usr/local/astrometry/data/ USER root @@ -255,13 +255,15 @@ RUN for i in indi-duino libasi indi-asi libplayerone indi-playerone indi-shelyak # Dependencies to build phd2 from sources USER root RUN apt-get --assume-yes --quiet install --no-install-recommends \ + libeigen3-dev \ + libopencv-dev \ libwxgtk3.2-dev USER $USERNAME RUN --mount=type=cache,target=$HOME/.cache,uid=$USERID \ mkdir -p $HOME/projects/phd2 \ && git -C $HOME/.cache/phd2/ fetch || retry -t 8 -d 10 git clone https://github.com/gnthibault/phd2.git $HOME/.cache/phd2/ \ - && cd $HOME/.cache/phd2/ && git checkout master && cp -r --parents ./* $HOME/projects/phd2/ \ + && cd $HOME/.cache/phd2/ && git checkout thibault/fix_find_star && cp -r --parents ./* $HOME/projects/phd2/ \ && mkdir -p $HOME/projects/build/phd2 \ && cd $HOME/projects/build/phd2 \ && cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_SYSTEM_PROCESSOR=$BARCH $HOME/projects/phd2 \ diff --git a/Observatory/AggregatedCustomScopeController.py b/Focuser/AggregatedCustomScopeControllerPPBA.py similarity index 85% rename from Observatory/AggregatedCustomScopeController.py rename to Focuser/AggregatedCustomScopeControllerPPBA.py index b0c78155..dc250269 100644 --- a/Observatory/AggregatedCustomScopeController.py +++ b/Focuser/AggregatedCustomScopeControllerPPBA.py @@ -6,57 +6,111 @@ # Local from Base.Base import Base from helper.IndiDevice import IndiDevice +from Focuser.IndiFocuserMixin import IndiFocuserMixin from utils.error import ScopeControllerError from utils.error import IndiClientPredicateTimeoutError -class UPBV2(IndiDevice, Base): +class PPBA(IndiDevice, IndiFocuserMixin): """ - 'CONNECTION': , - 'DRIVER_INFO': , - 'DEBUG': , - 'SIMULATION': , - 'CONFIG_PROCESS': , - 'POLLING_PERIOD': , - 'CONNECTION_MODE': , - 'SYSTEM_PORTS': , - 'DEVICE_PORT': , - 'DEVICE_BAUD_RATE': , - 'DEVICE_AUTO_SEARCH': , - 'DEVICE_PORT_SCAN': , - 'FIRMWARE_INFO': , - 'POWER_CYCLE': , - 'POWER_SENSORS': , - 'POWER_CONSUMPTION': , - 'REBOOT_DEVICE': , - 'POWER_CONTROL': , - 'POWER_CONTROL_LABEL': , - 'POWER_CURRENT': , - 'POWER_ON_BOOT': , - 'POWER_OVER_CURRENT': , - 'ADJUSTABLE_VOLTAGE': , - 'AUTO_DEW': , - 'DEW_CONTROL_LABEL': , - 'AUTO_DEW_AGG': , - 'DEW_PWM': , - 'DEW_CURRENT': , - 'USB_HUB_CONTROL': , - 'USB_PORT_CONTROL': , - 'USB_CONTROL_LABEL': , - 'FOCUS_MOTION': , - 'REL_FOCUS_POSITION': , - 'ABS_FOCUS_POSITION': , - 'FOCUS_MAX': , - 'FOCUS_ABORT_MOTION': , - 'FOCUS_SYNC': , - 'FOCUS_REVERSE_MOTION': , - 'FOCUS_BACKLASH_TOGGLE': , - 'FOCUS_BACKLASH_STEPS': , - 'FOCUSER_SETTINGS': , - 'WEATHER_STATUS': , - 'WEATHER_PARAMETERS': , - 'WEATHER_TEMPERATURE': , - 'WEATHER_HUMIDITY': , - 'WEATHER_DEWPOINT': } + Return of indi_getprop -h 192.168.8.202 -p 7624 "Pegasus PPBA.*.*" + Pegasus PPBA.CONNECTION.CONNECT=On + Pegasus PPBA.CONNECTION.DISCONNECT=Off + Pegasus PPBA.DRIVER_INFO.DRIVER_NAME=Pegasus PPBA + Pegasus PPBA.DRIVER_INFO.DRIVER_EXEC=indi_pegasus_ppba + Pegasus PPBA.DRIVER_INFO.DRIVER_VERSION=1.3 + Pegasus PPBA.DRIVER_INFO.DRIVER_INTERFACE=32904 + Pegasus PPBA.DEBUG.ENABLE=Off + Pegasus PPBA.DEBUG.DISABLE=On + Pegasus PPBA.SIMULATION.ENABLE=Off + Pegasus PPBA.SIMULATION.DISABLE=On + Pegasus PPBA.CONFIG_PROCESS.CONFIG_LOAD=Off + Pegasus PPBA.CONFIG_PROCESS.CONFIG_SAVE=Off + Pegasus PPBA.CONFIG_PROCESS.CONFIG_DEFAULT=Off + Pegasus PPBA.CONFIG_PROCESS.CONFIG_PURGE=Off + Pegasus PPBA.POLLING_PERIOD.PERIOD_MS=1000 + Pegasus PPBA.CONNECTION_MODE.CONNECTION_SERIAL=On + Pegasus PPBA.SYSTEM_PORTS.Pegasus_Astro_UPBv2_revD_UPB25S4VWV=Off + Pegasus PPBA.SYSTEM_PORTS.Pegasus_Astro_PPBADV_Gen2C_PPBA93RF0I=Off + Pegasus PPBA.SYSTEM_PORTS.Arduino_Uranus_MeteoSensor_95A5389D50555233342E3120FF12122B=Off + Pegasus PPBA.SYSTEM_PORTS.1a86_USB_Serial=Off + Pegasus PPBA.DEVICE_PORT.PORT=/dev/serial/by-id/usb-Pegasus_Astro_PPBADV_Gen2C_PPBA93RF0I-if00-port0 + Pegasus PPBA.DEVICE_BAUD_RATE.9600=On + Pegasus PPBA.DEVICE_BAUD_RATE.19200=Off + Pegasus PPBA.DEVICE_BAUD_RATE.38400=Off + Pegasus PPBA.DEVICE_BAUD_RATE.57600=Off + Pegasus PPBA.DEVICE_BAUD_RATE.115200=Off + Pegasus PPBA.DEVICE_BAUD_RATE.230400=Off + Pegasus PPBA.DEVICE_AUTO_SEARCH.INDI_ENABLED=Off + Pegasus PPBA.DEVICE_AUTO_SEARCH.INDI_DISABLED=On + Pegasus PPBA.DEVICE_PORT_SCAN.Scan Ports=Off + Pegasus PPBA.QUADOUT_POWER.QUADOUT_ON=On + Pegasus PPBA.QUADOUT_POWER.QUADOUT_OFF=Off + Pegasus PPBA.ADJOUT_VOLTAGE.ADJOUT_OFF=On + Pegasus PPBA.ADJOUT_VOLTAGE.ADJOUT_3V=Off + Pegasus PPBA.ADJOUT_VOLTAGE.ADJOUT_5V=Off + Pegasus PPBA.ADJOUT_VOLTAGE.ADJOUT_8V=Off + Pegasus PPBA.ADJOUT_VOLTAGE.ADJOUT_9V=Off + Pegasus PPBA.ADJOUT_VOLTAGE.ADJOUT_12V=Off + Pegasus PPBA.POWER_SENSORS.SENSOR_VOLTAGE=13.699999999999999289 + Pegasus PPBA.POWER_SENSORS.SENSOR_CURRENT=0.015384615384615385469 + Pegasus PPBA.POWER_SENSORS.SENSOR_AVG_AMPS=0.02999999999999999889 + Pegasus PPBA.POWER_SENSORS.SENSOR_AMP_HOURS=0.040000000000000000833 + Pegasus PPBA.POWER_SENSORS.SENSOR_WATT_HOURS=0.56999999999999995115 + Pegasus PPBA.POWER_SENSORS.SENSOR_TOTAL_CURRENT=0 + Pegasus PPBA.POWER_SENSORS.SENSOR_12V_CURRENT=0.2000000000000000111 + Pegasus PPBA.POWER_SENSORS.SENSOR_DEWA_CURRENT=0 + Pegasus PPBA.POWER_SENSORS.SENSOR_DEWB_CURRENT=0 + Pegasus PPBA.POWER_ON_BOOT.POWER_PORT_1=On + Pegasus PPBA.POWER_ON_BOOT.POWER_PORT_2=On + Pegasus PPBA.POWER_ON_BOOT.POWER_PORT_3=On + Pegasus PPBA.POWER_ON_BOOT.POWER_PORT_4=On + Pegasus PPBA.REBOOT_DEVICE.REBOOT=Off + Pegasus PPBA.POWER_WARM.POWER_WARN_ON=Ok + Pegasus PPBA.LED_INDICATOR.LED_ON=On + Pegasus PPBA.LED_INDICATOR.LED_OFF=Off + Pegasus PPBA.AUTO_DEW.INDI_ENABLED=Off + Pegasus PPBA.AUTO_DEW.INDI_DISABLED=On + Pegasus PPBA.AUTO_DEW_SETTINGS.AGGRESSION=0 + Pegasus PPBA.DEW_PWM.DEW_A=82.352941176470579876 + Pegasus PPBA.DEW_PWM.DEW_B=82.352941176470579876 + Pegasus PPBA.FOCUS_MOTION.FOCUS_INWARD=On + Pegasus PPBA.FOCUS_MOTION.FOCUS_OUTWARD=Off + Pegasus PPBA.REL_FOCUS_POSITION.FOCUS_RELATIVE_POSITION=0 + Pegasus PPBA.ABS_FOCUS_POSITION.FOCUS_ABSOLUTE_POSITION=0 + Pegasus PPBA.FOCUS_MAX.FOCUS_MAX_VALUE=100000 + Pegasus PPBA.FOCUS_ABORT_MOTION.ABORT=Off + Pegasus PPBA.FOCUS_SYNC.FOCUS_SYNC_VALUE=0 + Pegasus PPBA.FOCUS_REVERSE_MOTION.INDI_ENABLED=Off + Pegasus PPBA.FOCUS_REVERSE_MOTION.INDI_DISABLED=On + Pegasus PPBA.FOCUS_BACKLASH_TOGGLE.INDI_ENABLED=Off + Pegasus PPBA.FOCUS_BACKLASH_TOGGLE.INDI_DISABLED=On + Pegasus PPBA.FOCUS_BACKLASH_STEPS.FOCUS_BACKLASH_VALUE=0 + Pegasus PPBA.FOCUSER_SETTINGS.SETTING_MAX_SPEED=400 + Pegasus PPBA.FOCUSER_DRIVE.STEP_FULL=Off + Pegasus PPBA.FOCUSER_DRIVE.STEP_HALF=On + Pegasus PPBA.FOCUSER_DRIVE.STEP_FORTH=Off + Pegasus PPBA.FOCUSER_DRIVE.STEP_EIGHTH=Off + Pegasus PPBA.WEATHER_UPDATE.PERIOD=60 + Pegasus PPBA.WEATHER_REFRESH.REFRESH=Off + Pegasus PPBA.WEATHER_OVERRIDE.OVERRIDE=Off + Pegasus PPBA.WEATHER_STATUS.WEATHER_TEMPERATURE=Ok + Pegasus PPBA.WEATHER_PARAMETERS.WEATHER_TEMPERATURE=0 + Pegasus PPBA.WEATHER_PARAMETERS.WEATHER_HUMIDITY=0 + Pegasus PPBA.WEATHER_PARAMETERS.WEATHER_DEWPOINT=0 + Pegasus PPBA.WEATHER_TEMPERATURE.MIN_OK=-15 + Pegasus PPBA.WEATHER_TEMPERATURE.MAX_OK=35 + Pegasus PPBA.WEATHER_TEMPERATURE.PERC_WARN=15 + Pegasus PPBA.WEATHER_TEMPERATURE.ALERT_TYPE=0 + Pegasus PPBA.WEATHER_HUMIDITY.MIN_OK=0 + Pegasus PPBA.WEATHER_HUMIDITY.MAX_OK=100 + Pegasus PPBA.WEATHER_HUMIDITY.PERC_WARN=15 + Pegasus PPBA.WEATHER_HUMIDITY.ALERT_TYPE=0 + Pegasus PPBA.WEATHER_DEWPOINT.MIN_OK=0 + Pegasus PPBA.WEATHER_DEWPOINT.MAX_OK=100 + Pegasus PPBA.WEATHER_DEWPOINT.PERC_WARN=15 + Pegasus PPBA.WEATHER_DEWPOINT.ALERT_TYPE=0 + Pegasus PPBA.FIRMWARE_INFO.VERSION=2.12.3 + Pegasus PPBA.FIRMWARE_INFO.UPTIME=1.207 """ def __init__(self, config=None, @@ -130,6 +184,12 @@ def __init__(self, self.auto_dew_identifiers = config["auto_dew_identifiers"] self.auto_dew_aggressivity = str(config["auto_dew_aggressivity"]) + # Focus parameters + self.focus_range = config['focus_range'] + self.autofocus_step = config['autofocus_step'] + self.autofocus_range = config['autofocus_range'] + + # device related intialization IndiDevice.__init__(self, device_name=config["device_name"], diff --git a/Focuser/AggregatedCustomScopeControllerUPBv2.py b/Focuser/AggregatedCustomScopeControllerUPBv2.py new file mode 100644 index 00000000..81f40c49 --- /dev/null +++ b/Focuser/AggregatedCustomScopeControllerUPBv2.py @@ -0,0 +1,1005 @@ +# Basic stuff +import json +import logging +import time + +# Local +from Base.Base import Base +from helper.IndiDevice import IndiDevice +from Focuser.IndiFocuserMixin import IndiFocuserMixin +from utils.error import ScopeControllerError +from utils.error import IndiClientPredicateTimeoutError + +class AggregatedCustomScopeControllerUPBv2(IndiDevice, IndiFocuserMixin): + """ + 'CONNECTION': , + 'DRIVER_INFO': , + 'DEBUG': , + 'SIMULATION': , + 'CONFIG_PROCESS': , + 'POLLING_PERIOD': , + 'CONNECTION_MODE': , + 'SYSTEM_PORTS': , + 'DEVICE_PORT': , + 'DEVICE_BAUD_RATE': , + 'DEVICE_AUTO_SEARCH': , + 'DEVICE_PORT_SCAN': , + 'FIRMWARE_INFO': , + 'POWER_CYCLE': , + 'POWER_SENSORS': , + 'POWER_CONSUMPTION': , + 'REBOOT_DEVICE': , + 'POWER_CONTROL': , + 'POWER_CONTROL_LABEL': , + 'POWER_CURRENT': , + 'POWER_ON_BOOT': , + 'POWER_OVER_CURRENT': , + 'ADJUSTABLE_VOLTAGE': , + 'AUTO_DEW': , + 'DEW_CONTROL_LABEL': , + 'AUTO_DEW_AGG': , + 'DEW_PWM': , + 'DEW_CURRENT': , + 'USB_HUB_CONTROL': , + 'USB_PORT_CONTROL': , + 'USB_CONTROL_LABEL': , + 'FOCUS_MOTION': , + 'REL_FOCUS_POSITION': , + 'ABS_FOCUS_POSITION': , + 'FOCUS_MAX': , + 'FOCUS_ABORT_MOTION': , + 'FOCUS_SYNC': , + 'FOCUS_REVERSE_MOTION': , + 'FOCUS_BACKLASH_TOGGLE': , + 'FOCUS_BACKLASH_STEPS': , + 'FOCUSER_SETTINGS': , + 'WEATHER_STATUS': , + 'WEATHER_PARAMETERS': , + 'WEATHER_TEMPERATURE': , + 'WEATHER_HUMIDITY': , + 'WEATHER_DEWPOINT': } + + Return of indi_getprop -h 192.168.8.202 -p 7624 "Pegasus UPB.*.*" + Pegasus UPB.CONNECTION.CONNECT=On + Pegasus UPB.CONNECTION.DISCONNECT=Off + Pegasus UPB.DRIVER_INFO.DRIVER_NAME=Pegasus UPB + Pegasus UPB.DRIVER_INFO.DRIVER_EXEC=indi_pegasus_upb + Pegasus UPB.DRIVER_INFO.DRIVER_VERSION=1.6 + Pegasus UPB.DRIVER_INFO.DRIVER_INTERFACE=32904 + Pegasus UPB.DEBUG.ENABLE=Off + Pegasus UPB.DEBUG.DISABLE=On + Pegasus UPB.SIMULATION.ENABLE=Off + Pegasus UPB.SIMULATION.DISABLE=On + Pegasus UPB.CONFIG_PROCESS.CONFIG_LOAD=Off + Pegasus UPB.CONFIG_PROCESS.CONFIG_SAVE=Off + Pegasus UPB.CONFIG_PROCESS.CONFIG_DEFAULT=Off + Pegasus UPB.CONFIG_PROCESS.CONFIG_PURGE=Off + Pegasus UPB.POLLING_PERIOD.PERIOD_MS=1000 + Pegasus UPB.CONNECTION_MODE.CONNECTION_SERIAL=On + Pegasus UPB.SYSTEM_PORTS.Pegasus_Astro_UPBv2_revD_UPB25S4VWV=Off + Pegasus UPB.SYSTEM_PORTS.FTDI_FT232R_USB_UART_AD0JE0ID=Off + Pegasus UPB.DEVICE_PORT.PORT=/dev/serial/by-id/usb-Pegasus_Astro_UPBv2_revD_UPB25S4VWV-if00-port0 + Pegasus UPB.DEVICE_BAUD_RATE.9600=On + Pegasus UPB.DEVICE_BAUD_RATE.19200=Off + Pegasus UPB.DEVICE_BAUD_RATE.38400=Off + Pegasus UPB.DEVICE_BAUD_RATE.57600=Off + Pegasus UPB.DEVICE_BAUD_RATE.115200=Off + Pegasus UPB.DEVICE_BAUD_RATE.230400=Off + Pegasus UPB.DEVICE_AUTO_SEARCH.INDI_ENABLED=Off + Pegasus UPB.DEVICE_AUTO_SEARCH.INDI_DISABLED=On + Pegasus UPB.DEVICE_PORT_SCAN.Scan Ports=Off + Pegasus UPB.POWER_CYCLE.POWER_CYCLE_OFF=Off + Pegasus UPB.POWER_CYCLE.POWER_CYCLE_ON=Off + Pegasus UPB.POWER_SENSORS.SENSOR_VOLTAGE=13.800000000000000711 + Pegasus UPB.POWER_SENSORS.SENSOR_CURRENT=1.1999999999999999556 + Pegasus UPB.POWER_SENSORS.SENSOR_POWER=16 + Pegasus UPB.POWER_CONSUMPTION.CONSUMPTION_AVG_AMPS=1.2900000000000000355 + Pegasus UPB.POWER_CONSUMPTION.CONSUMPTION_AMP_HOURS=6.7199999999999997513 + Pegasus UPB.POWER_CONSUMPTION.CONSUMPTION_WATT_HOURS=92.989999999999994884 + Pegasus UPB.REBOOT_DEVICE.REBOOT=Off + Pegasus UPB.POWER_CONTROL.POWER_CONTROL_1=Off + Pegasus UPB.POWER_CONTROL.POWER_CONTROL_2=On + Pegasus UPB.POWER_CONTROL.POWER_CONTROL_3=On + Pegasus UPB.POWER_CONTROL.POWER_CONTROL_4=On + Pegasus UPB.POWER_CURRENT.POWER_CURRENT_1=0 + Pegasus UPB.POWER_CURRENT.POWER_CURRENT_2=0.10833333333333333703 + Pegasus UPB.POWER_CURRENT.POWER_CURRENT_3=0.10833333333333333703 + Pegasus UPB.POWER_CURRENT.POWER_CURRENT_4=0.18124999999999999445 + Pegasus UPB.POWER_ON_BOOT.POWER_PORT_1=On + Pegasus UPB.POWER_ON_BOOT.POWER_PORT_2=Off + Pegasus UPB.POWER_ON_BOOT.POWER_PORT_3=Off + Pegasus UPB.POWER_ON_BOOT.POWER_PORT_4=Off + Pegasus UPB.POWER_OVER_CURRENT.DEW_A=Ok + Pegasus UPB.POWER_OVER_CURRENT.DEW_B=Ok + Pegasus UPB.POWER_OVER_CURRENT.DEW_C=Ok + Pegasus UPB.POWER_OVER_CURRENT.POWER_PORT_4=Ok + Pegasus UPB.POWER_OVER_CURRENT.=Idle + Pegasus UPB.POWER_OVER_CURRENT.=Idle + Pegasus UPB.POWER_OVER_CURRENT.=Idle + Pegasus UPB.ADJUSTABLE_VOLTAGE.ADJUSTABLE_VOLTAGE_VALUE=5 + Pegasus UPB.AUTO_DEW.DEW_A=Off + Pegasus UPB.AUTO_DEW.DEW_B=Off + Pegasus UPB.AUTO_DEW.DEW_C=Off + Pegasus UPB.AUTO_DEW_AGG.AUTO_DEW_AGG_VALUE=150 + Pegasus UPB.DEW_PWM.DEW_A=0 + Pegasus UPB.DEW_PWM.DEW_B=0 + Pegasus UPB.DEW_PWM.DEW_C=0 + Pegasus UPB.DEW_CURRENT.DEW_CURRENT_A=0 + Pegasus UPB.DEW_CURRENT.DEW_CURRENT_B=0 + Pegasus UPB.DEW_CURRENT.DEW_CURRENT_C=0 + Pegasus UPB.USB_HUB_CONTROL.INDI_ENABLED=On + Pegasus UPB.USB_HUB_CONTROL.INDI_DISABLED=Off + Pegasus UPB.USB_PORT_CONTROL.PORT_1=On + Pegasus UPB.USB_PORT_CONTROL.PORT_2=On + Pegasus UPB.USB_PORT_CONTROL.PORT_3=On + Pegasus UPB.USB_PORT_CONTROL.PORT_4=On + Pegasus UPB.USB_PORT_CONTROL.PORT_5=On + Pegasus UPB.USB_PORT_CONTROL.PORT_6=On + Pegasus UPB.FOCUS_MOTION.FOCUS_INWARD=On + Pegasus UPB.FOCUS_MOTION.FOCUS_OUTWARD=Off + Pegasus UPB.REL_FOCUS_POSITION.FOCUS_RELATIVE_POSITION=0 + Pegasus UPB.ABS_FOCUS_POSITION.FOCUS_ABSOLUTE_POSITION=16500 + Pegasus UPB.FOCUS_MAX.FOCUS_MAX_VALUE=100000 + Pegasus UPB.FOCUS_ABORT_MOTION.ABORT=Off + Pegasus UPB.FOCUS_SYNC.FOCUS_SYNC_VALUE=0 + Pegasus UPB.FOCUS_REVERSE_MOTION.INDI_ENABLED=Off + Pegasus UPB.FOCUS_REVERSE_MOTION.INDI_DISABLED=On + Pegasus UPB.FOCUS_BACKLASH_TOGGLE.INDI_ENABLED=Off + Pegasus UPB.FOCUS_BACKLASH_TOGGLE.INDI_DISABLED=On + Pegasus UPB.FOCUS_BACKLASH_STEPS.FOCUS_BACKLASH_VALUE=0 + Pegasus UPB.FOCUSER_SETTINGS.SETTING_MAX_SPEED=200 + Pegasus UPB.WEATHER_UPDATE.PERIOD=60 + Pegasus UPB.WEATHER_REFRESH.REFRESH=Off + Pegasus UPB.WEATHER_OVERRIDE.OVERRIDE=Off + Pegasus UPB.WEATHER_STATUS.WEATHER_TEMPERATURE=Ok + Pegasus UPB.WEATHER_PARAMETERS.WEATHER_TEMPERATURE=16.5 + Pegasus UPB.WEATHER_PARAMETERS.WEATHER_HUMIDITY=59 + Pegasus UPB.WEATHER_PARAMETERS.WEATHER_DEWPOINT=8.5 + Pegasus UPB.WEATHER_TEMPERATURE.MIN_OK=-15 + Pegasus UPB.WEATHER_TEMPERATURE.MAX_OK=35 + Pegasus UPB.WEATHER_TEMPERATURE.PERC_WARN=15 + Pegasus UPB.WEATHER_TEMPERATURE.ALERT_TYPE=0 + Pegasus UPB.WEATHER_HUMIDITY.MIN_OK=0 + Pegasus UPB.WEATHER_HUMIDITY.MAX_OK=100 + Pegasus UPB.WEATHER_HUMIDITY.PERC_WARN=15 + Pegasus UPB.WEATHER_HUMIDITY.ALERT_TYPE=0 + Pegasus UPB.WEATHER_DEWPOINT.MIN_OK=0 + Pegasus UPB.WEATHER_DEWPOINT.MAX_OK=100 + Pegasus UPB.WEATHER_DEWPOINT.PERC_WARN=15 + Pegasus UPB.WEATHER_DEWPOINT.ALERT_TYPE=0 + Pegasus UPB.FIRMWARE_INFO.VERSION=2.4 + Pegasus UPB.FIRMWARE_INFO.UPTIME=5.223 + """ + def __init__(self, + config=None, + connect_on_create=True): + + self.is_initialized = False + + if config is None: + config = dict( + device_name="Pegasus UPB", + device_port="/dev/serial/by-id/usb-Pegasus_Astro_UPBv2_revD_UPB25S4VWV-if00-port0", + connection_type="CONNECTION_SERIAL", + baud_rate=9600, + polling_ms=1000, + dustcap_travel_delay_s=10, + adjustable_voltage_value=5, + power_labels=dict( + POWER_LABEL_1="MAIN_TELESCOPE_DUSTCAP_CONTROL", + POWER_LABEL_2="TELESCOPE_LEVEL_POWER", #SPOX_AND_DUSTCAP_POWER + POWER_LABEL_3="FOCUSER_LEVEL_POWER", #PRIMARY_FOCUSER_POWER + POWER_LABEL_4="MOUNT_POWER"), + always_on_power_identifiers=dict( + MAIN_TELESCOPE_DUSTCAP_CONTROL=False, + TELESCOPE_LEVEL_POWER=False, #SPOX_AND_DUSTCAP_POWER + FOCUSER_LEVEL_POWER=False, #PRIMARY_FOCUSER_POWER + MOUNT_POWER=False), + usb_labels=dict( + USB_LABEL_1="PRIMARY_CAMERA", + USB_LABEL_2="ARDUINO_CONTROL_BOX", + USB_LABEL_3="GUIDE_CAMERA", + USB_LABEL_4="FIELD_CAMERA", + USB_LABEL_5="WIFI_ROUTER", + USB_LABEL_6="SPECTRO_CONTROL_BOX"), + always_on_usb_identifiers=dict( + PRIMARY_CAMERA=False, + ARDUINO_CONTROL_BOX=True, + GUIDE_CAMERA=False, + FIELD_CAMERA=False, + WIFI_ROUTER=True, + SPECTRO_CONTROL_BOX=False), + dew_labels=dict( + DEW_LABEL_1="PRIMARY_FAN", + DEW_LABEL_2="SECONDARY_DEW_HEATER", + DEW_LABEL_3="FINDER_DEW_HEATER"), + auto_dew_identifiers=dict( + PRIMARY_FAN=False, + SECONDARY_DEW_HEATER=True, + FINDER_DEW_HEATER=True), + auto_dew_aggressivity=200, # Number between 50 and 250 + default_focus=8000, + focus_range=dict( + min=7500, + max=9000), + autofocus_step=dict( + coarse=250, + fine=100), + autofocus_range=dict( + coarse=2500, + fine=2500), + indi_client=dict(indi_host="localhost", + indi_port=7625)) + + # Communication config + self.device_port = config["device_port"] + self.connection_type = config["connection_type"] + self.baud_rate = str(config["baud_rate"]) + self.polling_ms = float(config["polling_ms"]) + self.dustcap_travel_delay_s = float(config["dustcap_travel_delay_s"]) + + # labels + self.power_labels = config["power_labels"] + self.always_on_power_identifiers = config["always_on_power_identifiers"] + self.usb_labels = config["usb_labels"] + self.always_on_usb_identifiers = config["always_on_usb_identifiers"] + self.dew_labels = config["dew_labels"] + + # power parameters + self.adjustable_voltage_value = config["adjustable_voltage_value"] + + # dew parameters + self.auto_dew_identifiers = config["auto_dew_identifiers"] + self.auto_dew_aggressivity = float(config.get("auto_dew_aggressivity", 150)) + + # Focus parameters + self.default_focus = config['default_focus'] + self.focus_range = {k:float(v) for k,v in config['focus_range'].items()} + self.autofocus_step = {k:float(v) for k,v in config['autofocus_step'].items()} + self.autofocus_range = {k:float(v) for k,v in config['autofocus_range'].items()} + + + # device related intialization + IndiDevice.__init__(self, + device_name=config["device_name"], + indi_driver_name=config.get('indi_driver_name', None), + indi_client_config=config["indi_client"]) + + if connect_on_create: + self.default_connect() + + # Finished configuring + self.logger.debug('configured successfully') + + def unpark(self): + self.logger.debug("Unparking") + self.start_indi_server() + self.start_indi_driver() + self.initialize() + self.logger.debug("Successfully unparked") + + def unpark_focuser(self): + self.logger.debug("About to unpark focuser in a reset-like manner") + self.park_focuser() + self.start_indi_server() + self.start_indi_driver() + self.default_connect() + IndiFocuserMixin.unpark_focuser(self) + self.logger.debug("Focuser successfully unparked") + + def move_to(self, position): + """ Move focuser to new encoder position """ + # We need to do this weird stuff because of weird indi implementation + if position == self.get_position(): + position = position + 1 + return IndiFocuserMixin.move_to(self, position) + + def default_connect(self): + """ + Connection is made in two phases: + * connect client to server so that we can setup options, like port + * connect server to actual physical device + + Then "initialize" all outputs such that the telescope is in a steady + state, that can last a very long time (multiple days without operation) + :return: + """ + self.logger.debug("Initializing") + self.connect(connect_device=False) + self.set_device_communication_options() + self.connect_device() + + def initialize(self): + """ + Connection is made in two phases: + * connect client to server so that we can setup options, like port + * connect server to actual physical device + + Then "initialize" all outputs such that the telescope is in a steady + state, that can last a very long time (multiple days without operation) + :return: + """ + self.logger.debug("Initializing") + self.default_connect() + self.set_all_labels() + self.initialize_all_power_on_boot() + self.initialize_all_power() + self.initialize_adjustable_power_source() + self.initialize_all_dew_outputs() + self.set_auto_dew_aggressivity() + time.sleep(1) # this is a very specific case, see https://github.com/indilib/indi-3rdparty/issues/822 + self.initialize_all_usb() + self.initialize_usb_hub() + + self.is_initialized = True + + self.logger.debug("Successfully Initialized") + + def park(self): + self.logger.debug("Parking") + self.deinitialize() + self.disconnect() + self.stop_indi_server() + self.logger.debug("Successfully parked") + + def deinitialize(self): + if not self.is_initialized: + self.logger.debug("No need for deinitializing") + return + self.logger.debug("Deinitializing") + # Then switch off all electronic devices + self.close_scope_dustcap() + self.switch_off_scope_fan() + self.switch_off_dew_heater() + self.power_off_all_telescope_equipments() + self.power_off_mount() + + self.is_initialized = False + self.logger.debug("Successfully deinitialized") + + def set_device_communication_options(self): + self.set_text("DEVICE_PORT", {"PORT": self.device_port}) + self.set_switch("CONNECTION_MODE", on_switches=[self.connection_type]) + self.set_switch("DEVICE_BAUD_RATE", on_switches=[self.baud_rate]) + self.set_polling_ms(polling_ms=self.polling_ms) + + def set_polling_ms(self, polling_ms=None): + if polling_ms is not None: + self.polling_ms = polling_ms + self.set_number("POLLING_PERIOD", {'PERIOD_MS': self.polling_ms}) + + def set_auto_dew_aggressivity(self, auto_dew_aggressivity=150): #50->250 + if auto_dew_aggressivity is not None: + self.auto_dew_aggressivity = float(auto_dew_aggressivity) + else: + self.auto_dew_aggressivity = 150 + self.set_number("AUTO_DEW_AGG", {'AUTO_DEW_AGG_VALUE': self.auto_dew_aggressivity}) + + def set_all_labels(self): + self.set_text("POWER_CONTROL_LABEL", self.power_labels) + self.set_text("USB_CONTROL_LABEL", self.usb_labels) + self.set_text("DEW_CONTROL_LABEL", self.dew_labels) + + def initialize_all_power_on_boot(self): + on_switches = [f"POWER_PORT_{i}" for i in range(1, 5) if self.always_on_power_identifiers[self.power_labels[f"POWER_LABEL_{i}"]]] + off_switches = [f"POWER_PORT_{i}" for i in range(1, 5) if not self.always_on_power_identifiers[self.power_labels[f"POWER_LABEL_{i}"]]] + self.set_switch("POWER_ON_BOOT", on_switches=on_switches, off_switches=off_switches) + + def initialize_all_power(self): + on_switches = [f"POWER_CONTROL_{i}" for i in range(1, 5) if self.always_on_power_identifiers[self.power_labels[f"POWER_LABEL_{i}"]]] + off_switches = [f"POWER_CONTROL_{i}" for i in range(1, 5) if not self.always_on_power_identifiers[self.power_labels[f"POWER_LABEL_{i}"]]] + self.set_switch("POWER_CONTROL", on_switches=on_switches, off_switches=off_switches) + + def initialize_adjustable_power_source(self): + self.set_number('ADJUSTABLE_VOLTAGE', {'ADJUSTABLE_VOLTAGE_VALUE': self.adjustable_voltage_value}) + + def wait_dustcap_delay(self): + time.sleep(self.dustcap_travel_delay_s) + + def initialize_all_usb(self): + """ + On our setup, all but the PORT 5 need to be reset. + PORT 5 is connected to our GL.inet router and needs to stay up always + PORT ids goes from 1 to 6 + :return: + """ + on_switches = [f"PORT_{i}" for i in range(1, 7) if self.always_on_usb_identifiers[self.usb_labels[f"USB_LABEL_{i}"]]] + off_switches = [f"PORT_{i}" for i in range(1, 7) if not self.always_on_usb_identifiers[self.usb_labels[f"USB_LABEL_{i}"]]] + self.set_switch("USB_PORT_CONTROL", on_switches=on_switches, off_switches=off_switches) + + def deinitialize_all_usb(self): + self.initialize_all_usb() + + def initialize_usb_hub(self): + self.logger.warning("initialize_usb_hub doesn't seems to be currently supported by indi driver") + #self.set_switch("USB_HUB_CONTROL", on_switches=["INDI_ENABLED"]) + + def power_on_spectro_controller(self): + # 5V adjustable power source + self.set_number('ADJUSTABLE_VOLTAGE', {'ADJUSTABLE_VOLTAGE_VALUE': self.adjustable_voltage_value}) + + def power_on_acquisition_equipments(self): + # Power telescope level equipments + on_switches = [f"POWER_CONTROL_{i}" for i in range(1, 5) if self.power_labels[f"POWER_LABEL_{i}"] in + ['MAIN_CAMERA_POWER']] + self.set_switch("POWER_CONTROL", on_switches=on_switches) + + def switch_on_acquisition_equipments_usb(self): + # USB + on_switches = [f"PORT_{i}" for i in range(1, 7) if self.usb_labels[f"USB_LABEL_{i}"] in + ["FIELD_CAMERA", "PRIMARY_CAMERA", "GUIDE_CAMERA"]] + self.set_switch("USB_PORT_CONTROL", on_switches=on_switches) + + def power_off_acquisition_equipments(self): + # Power telescope level equipments + off_switches = [f"POWER_CONTROL_{i}" for i in range(1, 5) if self.power_labels[f"POWER_LABEL_{i}"] in + ['MAIN_CAMERA_POWER']] + self.set_switch("POWER_CONTROL", off_switches=off_switches) + + def switch_off_acquisition_equipments_usb(self): + # USB + off_switches = [f"PORT_{i}" for i in range(1, 7) if self.usb_labels[f"USB_LABEL_{i}"] in + ["FIELD_CAMERA", "PRIMARY_CAMERA", "GUIDE_CAMERA"]] + self.set_switch("USB_PORT_CONTROL", off_switches=off_switches) + + def power_on_calibration_equipments(self): + # Power telescope level equipments + on_switches = [f"POWER_CONTROL_{i}" for i in range(1, 5) if self.power_labels[f"POWER_LABEL_{i}"] in + ['SPOX_AND_DUSTCAP_POWER']] + self.set_switch("POWER_CONTROL", on_switches=on_switches) + + def switch_on_calibration_equipments_usb(self): + # USB + on_switches = [f"PORT_{i}" for i in range(1, 7) if self.usb_labels[f"USB_LABEL_{i}"] in + ["SPECTRO_CONTROL_BOX"]] + self.set_switch("USB_PORT_CONTROL", on_switches=on_switches) + + def power_off_all_telescope_equipments(self): + # Power off telescope level equipments + off_switches = [f"POWER_CONTROL_{i}" for i in range(1, 5) if self.power_labels[f"POWER_LABEL_{i}"] in + ['SPOX_AND_DUSTCAP_POWER', 'MAIN_CAMERA_POWER']] + self.set_switch("POWER_CONTROL", off_switches=off_switches) + + def switch_off_all_telescope_equipments_usb(self): + # USB + off_switches = [f"PORT_{i}" for i in range(1, 7) if self.usb_labels[f"USB_LABEL_{i}"] in + ["FIELD_CAMERA", "PRIMARY_CAMERA", "SPECTRO_CONTROL_BOX", "ARDUINO_CONTROL_BOX", "GUIDE_CAMERA"]] + self.set_switch("USB_PORT_CONTROL", off_switches=off_switches) + + def power_on_mount(self): + # Power + on_switches = [f"POWER_CONTROL_{i}" for i in range(1, 5) if self.power_labels[f"POWER_LABEL_{i}"] == 'MOUNT_POWER'] + self.set_switch("POWER_CONTROL", on_switches=on_switches) + + def power_off_mount(self): + # Power + off_switches = [f"POWER_CONTROL_{i}" for i in range(1, 5) if self.power_labels[f"POWER_LABEL_{i}"] == 'MOUNT_POWER'] + self.set_switch("POWER_CONTROL", off_switches=off_switches) + + def power_on_arduino_control_box(self): + # 5V adjustable power source + self.set_number('ADJUSTABLE_VOLTAGE', {'ADJUSTABLE_VOLTAGE_VALUE': self.adjustable_voltage_value}) + + def switch_on_arduino_control_box_usb(self): + # Now setup USB + on_switches = [f"PORT_{i}" for i in range(1, 7) if self.usb_labels[f"USB_LABEL_{i}"] == "ARDUINO_CONTROL_BOX"] + self.set_switch("USB_PORT_CONTROL", on_switches=on_switches) + + def initialize_all_dew_outputs(self): + # Set power to 0 explicitly + self.set_number("DEW_PWM", {'DEW_A': 0.0, 'DEW_B': 0.0, 'DEW_C': 0.0}) + + # Set eligbility for automatic dew to False + off_switches = [f"DEW_{l}" for i, l in enumerate("ABC")] + self.set_switch("AUTO_DEW", off_switches=off_switches) + + def set_auto_dew_eligibility_on(self): + """ + Set auto dew eligibility + :return: + """ + on_switches = [f"DEW_{l}" for i, l in enumerate("ABC") if self.auto_dew_identifiers[self.dew_labels[f"DEW_LABEL_{i+1}"]]] + self.set_switch("AUTO_DEW", on_switches=on_switches) + + def set_auto_dew_eligibility_off(self): + """ + Set auto dew eligibility + :return: + """ + off_switches = [f"DEW_{l}" for i, l in enumerate("ABC") if self.auto_dew_identifiers[self.dew_labels[f"DEW_LABEL_{i+1}"]]] + self.set_switch("AUTO_DEW", off_switches=off_switches) + + def get_power_info(self): + power_dict = self.get_number("POWER_SENSORS") + #{'SENSOR_VOLTAGE': 13.7, 'SENSOR_CURRENT': 1.0, 'SENSOR_POWER': 13.0} + power_dict.update(self.get_number("POWER_CONSUMPTION")) + #{'CONSUMPTION_AVG_AMPS': 0.74, 'CONSUMPTION_AMP_HOURS': 249.28, 'CONSUMPTION_WATT_HOURS': 3402.6} + power_dict.update(self.get_number("POWER_CURRENT")) + #{'POWER_CURRENT_1': 0.0, 'POWER_CURRENT_2': 0.04, 'POWER_CURRENT_3': 0.0, 'POWER_CURRENT_4': 0.13} + power_dict.update(self.get_number("DEW_CURRENT")) + #{'DEW_CURRENT_A': 0.0, 'DEW_CURRENT_B': 0.0, 'DEW_CURRENT_C': 0.0} + + return power_dict + + def get_weather_info(self): + """ + Relevant info vectors: + 'WEATHER_STATUS': , + 'WEATHER_PARAMETERS': , + 'WEATHER_TEMPERATURE': , + 'WEATHER_HUMIDITY': , + 'WEATHER_DEWPOINT': } + :return: + """ + weather_dict = self.get_light("WEATHER_STATUS") + #{'WEATHER_TEMPERATURE': 'Ok'} + weather_dict.update(self.get_number("WEATHER_PARAMETERS")) + #{'WEATHER_TEMPERATURE': 17.8, 'WEATHER_HUMIDITY': 45.0, 'WEATHER_DEWPOINT': 5.7} + #weather_dict.update(self.get_number("WEATHER_TEMPERATURE")) + #{'MIN_OK': -15.0, 'MAX_OK': 35.0, 'PERC_WARN': 15.0} + #weather_dict.update(self.get_number("WEATHER_HUMIDITY")) + #{'MIN_OK': 0.0, 'MAX_OK': 100.0, 'PERC_WARN': 15.0} + #weather_dict.update(self.get_number("WEATHER_DEWPOINT")) + #{'MIN_OK': 0.0, 'MAX_OK': 100.0, 'PERC_WARN': 15.0} + + return weather_dict + + def switch_on_scope_fan(self): + """ blocking call: switch on fan to cool down primary mirror + set pwm value from 0 to 100 + """ + fan_dict = {k: 85 for i, k in enumerate(["DEW_A", "DEW_B", "DEW_C"]) if self.dew_labels[f"DEW_LABEL_{i+1}"] == "PRIMARY_FAN"} + self.set_number("DEW_PWM", fan_dict) + + def switch_off_scope_fan(self): + """ blocking call: switch off fan for primary mirror + """ + fan_dict = {k: 0 for i, k in enumerate(["DEW_A", "DEW_B", "DEW_C"]) if self.dew_labels[f"DEW_LABEL_{i+1}"] == "PRIMARY_FAN"} + self.set_number("DEW_PWM", fan_dict) + + def switch_on_dew_heater(self): + """ blocking call: switch on dew heater to avoid dew on secondary mirror + """ + self.set_auto_dew_eligibility_on() + self.logger.debug("Switching on automatic dew heater") + + def switch_off_dew_heater(self): + """ blocking call: switch off dew heater on secondary mirror + """ + self.set_auto_dew_eligibility_off() + self.logger.debug("Switching off automatic dew heater") + + def power_on_mount(self): + """ blocking call: switch on main mount + """ + on_switches = [f"POWER_CONTROL_{i}" for i in range(1, 5) if (self.power_labels[f"POWER_LABEL_{i}"] == "MOUNT_POWER")] + self.set_switch("POWER_CONTROL", on_switches=on_switches) + + def power_off_mount(self): + """ blocking call: switch off main mount + """ + off_switches = [f"POWER_CONTROL_{i}" for i in range(1, 5) if (self.power_labels[f"POWER_LABEL_{i}"] == "MOUNT_POWER")] + self.set_switch("POWER_CONTROL", off_switches=off_switches) + + def open_scope_dustcap(self): + """ blocking call: open up main scope dustcap + """ + on_switches = [f"POWER_CONTROL_{i}" for i in range(1, 5) if (self.power_labels[f"POWER_LABEL_{i}"] in + ["SPOX_AND_DUSTCAP_POWER"])] + off_switches = [f"POWER_CONTROL_{i}" for i in range(1, 5) if (self.power_labels[f"POWER_LABEL_{i}"] in + ["MAIN_TELESCOPE_DUSTCAP_CONTROL"])] + self.set_switch("POWER_CONTROL", on_switches=on_switches, off_switches=off_switches) + self.wait_dustcap_delay() + + def close_scope_dustcap(self): + """ blocking call: close main scope dustcap + """ + on_switches = [f"POWER_CONTROL_{i}" for i in range(1, 5) if (self.power_labels[f"POWER_LABEL_{i}"] in + ["SPOX_AND_DUSTCAP_POWER", + "MAIN_TELESCOPE_DUSTCAP_CONTROL"])] + self.set_switch("POWER_CONTROL", on_switches=on_switches) + self.wait_dustcap_delay() + +class ArduinoServoController(IndiDevice, Base): + """ + 'CONNECTION': Custom arduino nano with firmata (ttyUSB0 at the time of the test) + /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AD0JE0ID-if00-port0 -> Shelyak arduino SPOX (ttyUSB1 at the time of the test) + /dev/serial/by-id/usb-Pegasus_Astro_UPBv2_revD_UPB25S4VWV-if00-port0 -> PEGASUS (ttyUSB2 at the time of the test) + """ + def __init__(self, config=None, connect_on_create=False): + Base.__init__(self) + + self.is_initialized = False + # pin on arduino need to be configured either as input or ouput + # it means that we need to keep track of pin status internally + self.statuses = { + "scope_fan": False, + "scope_dew": False, + "finder_dew": False, + "mount_relay": False, + } + + if config is None: + config = dict( + config_upbv2=None, + config_arduino=None, + indi_driver_connect_delay_s=10, + indi_resetable_instruments_driver_map={ + "ZWO CCD": "ZWO CCD ASI290MM Mini", + "Altair": "Altair AA183MPRO", + "Shelyak SPOX": "Shelyak SPOX", + "PlayerOne CCD": "PlayerOne CCD Ares-M PRO", + "Arduino telescope controller": "Arduino" + }, + indi_mount_driver_name="Losmandy Gemini", + indi_webserver_host="localhost", + indi_webserver_port="8624") + + # Actual device config + self.config_upbv2 = config["config_upbv2"] + self.config_arduino = config["config_arduino"] + + # Local features + self._indi_resetable_instruments_driver_map = config["indi_resetable_instruments_driver_map"] + self._indi_driver_connect_delay_s = config["indi_driver_connect_delay_s"] + self._indi_mount_driver_name = config["indi_mount_driver_name"] + self._indi_mount_device_name = config["indi_mount_device_name"] + self._indi_webserver_host = config["indi_webserver_host"] + self._indi_webserver_port = config["indi_webserver_port"] + + # device related intialization will happen at initialization + self.upbv2 = UPBV2( + config=self.config_upbv2, + connect_on_create=False) + + self.arduino_servo_controller = ArduinoServoController( + config=self.config_arduino, + connect_on_create=False) + + if connect_on_create: + self.power_on_all_equipments() + + # Finished configuring + self.logger.debug('configured successfully') + + # def reset_config(self): + # # initialize upbv2 + # self.start_upbv2_driver() + # self.upbv2.initialize() # This force to put to zero power and usb usually before an indiserver reset + + def unpark(self): + + self.logger.debug("About to unpark in a reset-like manner") + self.park() + + # Now actually start unparking + self.upbv2.unpark() + # self.logger.debug(f'1#################################### {self.upbv2.get_switch("USB_PORT_CONTROL")["PORT_4"]}') + + # Power servo controller + self.upbv2.power_on_arduino_control_box() + # self.logger.debug(f'2#################################### {self.upbv2.get_switch("USB_PORT_CONTROL")["PORT_4"]}') + + # power-on USB for the arduino + self.upbv2.switch_on_arduino_control_box_usb() + # self.logger.debug(f'3#################################### {self.upbv2.get_switch("USB_PORT_CONTROL")["PORT_4"]}') + # self.logger.debug(f'3.5#################################### {self.upbv2.get_switch("USB_PORT_CONTROL")["PORT_4"]}') + + # Power acquisition instruments: this is a very specific case, see https://github.com/indilib/indi-3rdparty/issues/822 + self.upbv2.switch_on_acquisition_equipments_usb() + # self.logger.debug(f'4#################################### {self.upbv2.get_switch("USB_PORT_CONTROL")["PORT_4"]}') + + # Power mount + self.switch_on_mount() + # self.logger.debug(f'5#################################### {self.upbv2.get_switch("USB_PORT_CONTROL")["PORT_4"]}') + + # Wait for the os serial port to be created, and stuff like that + time.sleep(self._indi_driver_connect_delay_s) + + # Power acquisition instruments: this is a very specific case, see https://github.com/indilib/indi-3rdparty/issues/822 + self.upbv2.power_on_acquisition_equipments() + # self.logger.debug(f'6#################################### {self.upbv2.get_switch("USB_PORT_CONTROL")["PORT_4"]}') + # time.sleep(self._indi_driver_connect_delay_s) + + # # start or restart drivers if needed + # self.start_all_drivers() + # Now we need to wait a bit before trying to connect driver + # but _indi_driver_connect_delay_s was already waited for at previous step + # for driver_name, device_name in self._indi_resetable_instruments_driver_map.items(): + # if not self.probe_device_driver_connection(driver_name=driver_name, device_name=device_name): + # self.logger.debug(f"Device {device_name} doesn't seems to have its driver properly started - restarting") + # self.restart_driver(driver_name) + # Now we need to wait a bit before trying to connect driver + # if not self.probe_device_driver_connection(driver_name=self._indi_mount_driver_name, + # device_name=self._indi_mount_device_name): + # self.restart_driver(self._indi_mount_driver_name) + + # Initialize dependent device + # self.logger.debug(f'7#################################### {self.upbv2.get_switch("USB_PORT_CONTROL")["PORT_4"]}') + self.arduino_servo_controller.unpark() + + self.is_initialized = True + self.logger.debug("Successfully unparked") + + def park(self): + """ + :return: + """ + self.logger.debug("Parking") + + # Power acquisition instruments: this is a very specific case, see https://github.com/indilib/indi-3rdparty/issues/822 + if self.is_initialized: + self.upbv2.power_off_acquisition_equipments() + time.sleep(1) + self.upbv2.switch_off_acquisition_equipments_usb() + + # Deinitialize arduino servo first (as it relies on upb power) + self.arduino_servo_controller.park() + + # Deinitialize upbv2 + self.upbv2.park() + + self.is_initialized = False + self.logger.debug("Successfully parked") + + def open(self): + """ blocking call: opens both main telescope and guiding scope dustcap + """ + self.logger.debug("Opening AggregatedCustomScopeController") + # if not self.is_initialized: + # self.power_on_all_equipments() + self.open_finder_dustcap() + self.open_scope_dustcap() + + def close(self): + """ blocking call: closes both main telescope and guiding scope dustcap + """ + self.logger.debug("Closing AggregatedCustomScopeController") + self.close_finder_dustcap() + self.close_scope_dustcap() + + def switch_on_acquisition_instruments(self): + """ blocking call: switch on cameras, calibration tools, finderscopes, etc... + We also need to load the corresponding indi driver + """ + # assert self.is_initialized + # if not self.is_initialized: + # self.power_on_all_equipments() + self.logger.debug("Switching on all instruments") + self.upbv2.power_on_acquisition_equipments() + + # def switch_off_instruments(self): + # """ blocking call: switch off camera + # """ + # self.logger.debug("Switching off all equipments connected to upbv2") + # + # self.upbv2.power_off_all_telescope_equipments() + # time.sleep(1) + # self.upbv2.deinitialize_all_usb() + # for driver_name, device_name in self._indi_resetable_instruments_driver_map.items(): + # self.stop_driver(driver_name) + + # def start_upbv2_driver(self): + # self.start_driver(driver_name="Pegasus UPB", check_started=True) + # + # def stop_upbv2_driver(self): + # self.stop_driver(driver_name="Pegasus UPB", check_started=True) + # + # def start_servo_controller_driver(self): + # self.start_driver(driver_name="Arduino telescope controller", check_started=True) + # + # def start_all_drivers(self): + # for driver_name, device_name in self._indi_resetable_instruments_driver_map.items(): + # self.start_driver(driver_name, check_started=True) + # self.start_driver(self._indi_mount_driver_name, check_started=True) + + # def stop_all_drivers(self): + # for driver_name, device_name in self._indi_resetable_instruments_driver_map.items(): + # self.stop_driver(driver_name) + # self.stop_driver(self._indi_mount_driver_name) + # self.stop_upbv2_driver() + + def switch_on_scope_fan(self): + """ blocking call: switch on fan to cool down primary mirror + """ + self.logger.debug("Switching on fan to cool down primary mirror") + self.upbv2.switch_on_scope_fan() + self.statuses["scope_fan"] = True + + def switch_off_scope_fan(self): + """ blocking call: switch off fan for primary mirror + """ + self.logger.debug("Switching off telescope fan on primary mirror") + self.upbv2.switch_off_scope_fan() + self.statuses["scope_fan"] = False + + def switch_on_dew_heater(self): + """ blocking call: switch on dew heater to avoid dew on secondary mirror + """ + self.logger.debug("Switching on dew heater for secondary mirror") + self.upbv2.switch_on_dew_heater() + self.statuses["scope_dew"] = True + + def switch_off_dew_heater(self): + """ blocking call: switch off dew heater on secondary mirror + """ + self.logger.debug("Switching off telescope dew heater on secondary " + "mirror") + self.upbv2.switch_off_dew_heater() + self.statuses["scope_dew"] = False + + def switch_on_mount(self): + """ blocking call: switch on main mount + """ + self.logger.debug("Switching on main mount") + self.upbv2.power_on_mount() + self.statuses["mount_relay"] = True + + def switch_off_mount(self): + """ blocking call: switch off main mount + """ + self.logger.debug("Switching off main mount") + self.upbv2.power_off_mount() + self.statuses["mount_relay"] = False + + def open_scope_dustcap(self): + """ blocking call: open up main scope dustcap + """ + self.logger.debug("Opening up main scope dustcap") + self.upbv2.open_scope_dustcap() + + def close_scope_dustcap(self): + """ blocking call: close main scope dustcap + """ + self.logger.debug("close main scope dustcap") + self.upbv2.close_scope_dustcap() + + def open_finder_dustcap(self): + """ blocking call: open up finder dustcap + """ + self.logger.debug("Opening up finder dustcap") + self.arduino_servo_controller.open_finder_dustcap() + + def close_finder_dustcap(self): + """ blocking call: close finder dustcap + """ + self.logger.debug("close finder dustcap") + self.arduino_servo_controller.close_finder_dustcap() + + def status(self): + if self.is_initialized: + status = self.statuses.copy() + status["finder_dustcap_open"] = self.arduino_servo_controller.get_switch( + 'FINDER_SERVO_DUSTCAP_SWITCH')['SERVO_SWITCH'] + switch_name = [f"POWER_CONTROL_{i}" for i in range(1, 5) if + self.upbv2.power_labels[f"POWER_LABEL_{i}"]=="MAIN_TELESCOPE_DUSTCAP_CONTROL"][0] + status["scope_dustcap_closed"] = self.upbv2.get_switch( + 'POWER_CONTROL')[switch_name] + return status + else: + return self.statuses diff --git a/Focuser/IndiBaaderSteelDrive2Focuser.py b/Focuser/IndiBaaderSteelDrive2Focuser.py new file mode 100644 index 00000000..b8238df9 --- /dev/null +++ b/Focuser/IndiBaaderSteelDrive2Focuser.py @@ -0,0 +1,208 @@ +# Basic stuff +import json +import logging +import time + +# Local +from Base.Base import Base +from helper.IndiDevice import IndiDevice +from Focuser.IndiFocuserMixin import IndiFocuserMixin + + +class IndiBaaderSteelDrive2Focuser(IndiDevice, IndiFocuserMixin): + """ + Return of indi_getprop -h 192.168.0.194 -p 7624 "Baader SteelDriveII.*.*" + Baader SteelDriveII.CONNECTION.CONNECT=On + Baader SteelDriveII.CONNECTION.DISCONNECT=Off + Baader SteelDriveII.DRIVER_INFO.DRIVER_NAME=Baader SteelDriveII + Baader SteelDriveII.DRIVER_INFO.DRIVER_EXEC=indi_steeldrive2_focus + Baader SteelDriveII.DRIVER_INFO.DRIVER_VERSION=1.0 + Baader SteelDriveII.DRIVER_INFO.DRIVER_INTERFACE=8 + Baader SteelDriveII.DEBUG.ENABLE=Off + Baader SteelDriveII.DEBUG.DISABLE=On + Baader SteelDriveII.POLLING_PERIOD.PERIOD_MS=500 + Baader SteelDriveII.SIMULATION.ENABLE=Off + Baader SteelDriveII.SIMULATION.DISABLE=On + Baader SteelDriveII.CONFIG_PROCESS.CONFIG_LOAD=Off + Baader SteelDriveII.CONFIG_PROCESS.CONFIG_SAVE=Off + Baader SteelDriveII.CONFIG_PROCESS.CONFIG_DEFAULT=Off + Baader SteelDriveII.CONFIG_PROCESS.CONFIG_PURGE=Off + Baader SteelDriveII.CONNECTION_MODE.CONNECTION_SERIAL=On + Baader SteelDriveII.CONNECTION_MODE.CONNECTION_TCP=Off + Baader SteelDriveII.DEVICE_PORT.PORT=/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AB0KCL5O-if00-port0 + Baader SteelDriveII.DEVICE_BAUD_RATE.9600=Off + Baader SteelDriveII.DEVICE_BAUD_RATE.19200=On + Baader SteelDriveII.DEVICE_BAUD_RATE.38400=Off + Baader SteelDriveII.DEVICE_BAUD_RATE.57600=Off + Baader SteelDriveII.DEVICE_BAUD_RATE.115200=Off + Baader SteelDriveII.DEVICE_BAUD_RATE.230400=Off + Baader SteelDriveII.DEVICE_AUTO_SEARCH.INDI_ENABLED=Off + Baader SteelDriveII.DEVICE_AUTO_SEARCH.INDI_DISABLED=On + Baader SteelDriveII.DEVICE_PORT_SCAN.Scan Ports=On + Baader SteelDriveII.SYSTEM_PORTS.Pegasus_Astro_UPBv2_revD_UPB25S4VWV=Off + Baader SteelDriveII.SYSTEM_PORTS.FTDI_FT232R_USB_UART_AB0KCL5O=Off + Baader SteelDriveII.FOCUS_MOTION.FOCUS_INWARD=On + Baader SteelDriveII.FOCUS_MOTION.FOCUS_OUTWARD=Off + Baader SteelDriveII.REL_FOCUS_POSITION.FOCUS_RELATIVE_POSITION=0 + Baader SteelDriveII.ABS_FOCUS_POSITION.FOCUS_ABSOLUTE_POSITION=2000 + Baader SteelDriveII.FOCUS_MAX.FOCUS_MAX_VALUE=65535 + Baader SteelDriveII.FOCUS_ABORT_MOTION.ABORT=Off + Baader SteelDriveII.FOCUS_SYNC.FOCUS_SYNC_VALUE=0 + Baader SteelDriveII.FOCUS_REVERSE_MOTION.INDI_ENABLED=Off + Baader SteelDriveII.FOCUS_REVERSE_MOTION.INDI_DISABLED=On + Baader SteelDriveII.Presets.PRESET_1=0 + Baader SteelDriveII.Presets.PRESET_2=0 + Baader SteelDriveII.Presets.PRESET_3=0 + Baader SteelDriveII.Goto.Preset 1=Off + Baader SteelDriveII.Goto.Preset 2=Off + Baader SteelDriveII.Goto.Preset 3=Off + Baader SteelDriveII.USEJOYSTICK.ENABLE=Off + Baader SteelDriveII.USEJOYSTICK.DISABLE=On + Baader SteelDriveII.SNOOP_JOYSTICK.SNOOP_JOYSTICK_DEVICE=Joystick + Baader SteelDriveII.INFO.INFO_NAME=BP SteelDrive II + Baader SteelDriveII.INFO.INFO_VERSION=1.130 (Mar 4 2021) + Baader SteelDriveII.OPERATION.OPERATION_REBOOT=Off + Baader SteelDriveII.OPERATION.OPERATION_RESET=Off + Baader SteelDriveII.OPERATION.OPERATION_ZEROING=Off + Baader SteelDriveII.TC_COMPENSATE.TC_ENABLED=Off + Baader SteelDriveII.TC_COMPENSATE.TC_DISABLED=On + Baader SteelDriveII.TC_State.TC_ACTIVE=On + Baader SteelDriveII.TC_State.TC_PAUSED=Off + Baader SteelDriveII.TC_SETTINGS.TC_FACTOR=0 + Baader SteelDriveII.TC_SETTINGS.TC_PERIOD=60000 + Baader SteelDriveII.TC_SETTINGS.TC_DELTA=0.5 + Baader SteelDriveII.TC_SENSOR.TEMP_0=-128 + Baader SteelDriveII.TC_SENSOR.TEMP_1=-128 + Baader SteelDriveII.TC_SENSOR.TEMP_AVG=-128 + Baader SteelDriveII.STEPPER_DRIVE.STEPPER_DRIVE_CURRENT_MOVE=25 + Baader SteelDriveII.STEPPER_DRIVE.STEPPER_DRIVE_CURRENT_HOLD=80 + Baader SteelDriveII.USEJOYSTICK.ENABLE=Off + Baader SteelDriveII.USEJOYSTICK.DISABLE=On + Baader SteelDriveII.SNOOP_JOYSTICK.SNOOP_JOYSTICK_DEVICE=Joystick + """ + def __init__(self, + config=None, + connect_on_create=True): + + self.is_initialized = False + + if config is None: + config = dict( + device_name="Baader SteelDriveII", + device_port="/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AB0KCL5O-if00-port0", + connection_type="CONNECTION_SERIAL", + baud_rate=19200, + polling_ms=1000, + home_position=2000, + default_focus=8450, + focus_range={ + "min": 8200, + "max": 8700}, + autofocus_step={ + "coarse": 50, + "fine": 10}, + autofocus_range={ + "coarse": 300, + "fine": 150}, + indi_client=dict(indi_host="localhost", + indi_port=7624)) + + # Communication config + self.device_port = config["device_port"] + self.connection_type = config["connection_type"] + self.baud_rate = str(config["baud_rate"]) + self.polling_ms = float(config["polling_ms"]) + + # Focus parameters + self.home_position = config["home_position"] + self.default_focus = config["default_focus"] + self.focus_range = {k:float(v) for k,v in config['focus_range'].items()} + self.autofocus_step = {k:float(v) for k,v in config['autofocus_step'].items()} + self.autofocus_range = {k:float(v) for k,v in config['autofocus_range'].items()} + + + # device related intialization + IndiDevice.__init__(self, + device_name=config["device_name"], + indi_driver_name=config.get('indi_driver_name', None), + indi_client_config=config["indi_client"]) + + # The semantic is: we want to be able to use the device, ie both driver and device are connected + if connect_on_create: + self.initialize() + + # Finished configuring + self.logger.debug('configured successfully') + + def unpark(self): + self.logger.debug("Unparking") + self.initialize() + self.reboot_device() + self.zero_home() + self.move_to(self.default_focus) + self.logger.debug("Successfully unparked") + + def unpark_focuser(self): + self.logger.debug("About to unpark focuser") + self.unpark() + self.logger.debug("Focuser successfully unparked") + + def park(self): + self.logger.debug("Parking") + if self.is_connected: + self.move_to(self.default_focus) + self.deinitialize() + self.logger.debug("Successfully parked") + + def park_focuser(self): + self.logger.debug("About to park focuser") + self.park() + self.logger.debug("Focuser successfully parked") + + def zero_home(self): + if self.get_position() == int(self.home_position): + self.move_to(int(self.home_position)+500) # This is needed for the hall sensor to actually see the difference + self.set_switch("OPERATION", on_switches=["OPERATION_ZEROING"]) + self.sync_position(position=self.home_position) + + def reboot_device(self): + self.set_switch("OPERATION", on_switches=["OPERATION_REBOOT"]) + + def factory_reset_device(self): + self.set_switch("OPERATION", on_switches=["OPERATION_RESET"]) + + def set_device_communication_options(self): + self.set_text("DEVICE_PORT", {"PORT": self.device_port}) + self.set_switch("CONNECTION_MODE", on_switches=[self.connection_type]) + self.set_switch("DEVICE_BAUD_RATE", on_switches=[self.baud_rate]) + + def initialize(self): + """ + Connection is made in two phases: + * connect client to server so that we can setup options, like port + * connect server to actual physical device + + Then "initialize" all outputs such that the telescope is in a steady + state, that can last a very long time (multiple days without operation) + :return: + """ + self.logger.debug("Initializing") + self.start_indi_server() + self.start_indi_driver() + self.connect(connect_device=False) + self.set_device_communication_options() + self.is_initialized = True + self.connect_device() + self.logger.debug("Successfully Initialized") + + def deinitialize(self): + if not self.is_initialized: + self.logger.debug("No need for deinitializing") + return + self.logger.debug("Deinitializing") + self.disconnect() + self.stop_indi_driver() + self.stop_indi_server() + # Then switch off all electronic devices + self.is_initialized = False + self.logger.debug("Successfully deinitialized") \ No newline at end of file diff --git a/Focuser/IndiFocuser.py b/Focuser/IndiFocuser.py index 9b6f614c..08cad9dd 100644 --- a/Focuser/IndiFocuser.py +++ b/Focuser/IndiFocuser.py @@ -8,8 +8,9 @@ # Indi stuff from helper.IndiDevice import IndiDevice +from Focuser.IndiFocuserMixin import IndiFocuserMixin -class IndiFocuser(IndiDevice): +class IndiFocuser(IndiDevice, IndiFocuserMixin): """ """ @@ -20,7 +21,7 @@ def __init__(self, logger=None, config=None, if config is None: config = dict( module="IndiFocuser", - focuser_name="Focuser Simulator", + device_name="Focuser Simulator", port="/dev/ttyUSB0", focus_range=dict( min=1, @@ -41,11 +42,11 @@ def __init__(self, logger=None, config=None, self.autofocus_step = config['autofocus_step'] self.autofocus_range = config['autofocus_range'] - logger.debug(f"Indi Focuser, focuser name is: {config['focuser_name']}") + logger.debug(f"Indi Focuser, focuser name is: {config['device_name']}") # device related intialization IndiDevice.__init__(self, - device_name=config['focuser_name'], + device_name=config['device_name'], indi_driver_name=config.get('indi_driver_name', None), indi_client_config=config["indi_client"]) if connect_on_create: @@ -62,12 +63,12 @@ def park(self): self.logger.debug(f"Successfully parked focuser {self.device_name}") def unpark(self): - self.logger.debug(f"Unparking focuser {self.device_name} with a reset-like behaviour") - self.park() + self.logger.debug(f"Unparking focuser {self.device_name}") self.start_indi_server() self.start_indi_driver() - self.connect(connect_device=True) + self.connect() self.initialize() + self.connect_device() self.logger.debug(f"Successfully unparked focuser {self.device_name}") def deinitialize(self): @@ -89,23 +90,6 @@ def on_emergency(self): self.logger.debug('Indi Focuser: on emergency routine started...') self.logger.debug('Indi Focuser: on emergency routine finished') - def get_position(self): - """ Current encoder position of the focuser """ - #ret = self.get_number("REL_FOCUS_POSITION")["FOCUS_RELATIVE_POSITION"] - ret = self.get_number("ABS_FOCUS_POSITION")["FOCUS_ABSOLUTE_POSITION"] - self.logger.debug(f"{self} : current position is {ret}") - return ret - - def move_to(self, position): - """ Move focuser to new encoder position """ - self.logger.debug(f"{self} moving to position {position}") - self.set_number('ABS_FOCUS_POSITION', #REL_FOCUS_POSITION - {'FOCUS_ABSOLUTE_POSITION': np.float64(position)}, #FOCUS_RELATIVE_POSITION - sync=True, timeout=self.timeout) - new_position = self.get_position() - self.logger.debug(f"{self} Now position is {new_position}") - return new_position - def __str__(self): return f"Focuser: {self.device_name}" diff --git a/Focuser/IndiFocuserMixin.py b/Focuser/IndiFocuserMixin.py new file mode 100644 index 00000000..c0d1df51 --- /dev/null +++ b/Focuser/IndiFocuserMixin.py @@ -0,0 +1,51 @@ +# Basic stuff +import io +import json +import logging + +# Numerical stuff +import numpy as np + +# Indi stuff +from helper.IndiDevice import IndiDevice + +class IndiFocuserMixin: + """ + Mixin for devices that provide focuser functionality. + Assumes the base class provides IndiDevice methods like set_number, get_number. + """ + + def park_focuser(self): + self.logger.debug(f"{self} : parking focuser") + if self.is_connected: + self.move_to(self.default_focus) + + def unpark_focuser(self): + self.logger.debug(f"{self} : unparking focuser") + if self.is_connected: + self.move_to(self.default_focus) + + def get_position(self): + """ Current encoder position of the focuser """ + #ret = self.get_number("REL_FOCUS_POSITION")["FOCUS_RELATIVE_POSITION"] + ret = self.get_number("ABS_FOCUS_POSITION")["FOCUS_ABSOLUTE_POSITION"] + self.logger.debug(f"{self} : current position is {ret}") + return ret + + def move_to(self, position): + """ Move focuser to new encoder position """ + self.logger.debug(f"{self} moving to position {position}") + self.set_number('ABS_FOCUS_POSITION', #REL_FOCUS_POSITION + {'FOCUS_ABSOLUTE_POSITION': np.float64(position)}, #FOCUS_RELATIVE_POSITION + sync=True, timeout=self.timeout) + new_position = self.get_position() + self.logger.debug(f"{self} Now position is {new_position}") + return new_position + + def sync_position(self, position): + self.logger.debug(f"{self} Syncing to position {position}") + self.set_number('FOCUS_SYNC', #REL_FOCUS_POSITION + {'FOCUS_SYNC_VALUE': np.float64(position)}, #FOCUS_RELATIVE_POSITION + sync=True, timeout=self.timeout) + new_position = self.get_position() + self.logger.debug(f"{self} Now position is {new_position}") \ No newline at end of file diff --git a/Imaging/AutoFocuser.py b/Imaging/AutoFocuser.py index e15a99f5..79d44730 100644 --- a/Imaging/AutoFocuser.py +++ b/Imaging/AutoFocuser.py @@ -8,6 +8,8 @@ from astropy.modeling import models, fitting import numpy as np import skimage.morphology +from skimage import img_as_float +from skimage import exposure # Viz import matplotlib.pyplot as plt @@ -230,9 +232,6 @@ def autofocus(self, if not thumbnail_size: if self.autofocus_roi_size: thumbnail_size = self.autofocus_roi_size - else: - raise ValueError(f"No focus thumbnail size specified, aborting" - f" autofocus of {self.camera}") if keep_files is None: if self.autofocus_keep_files: @@ -291,10 +290,12 @@ def initialize_camera(self): self.logger.debug(f"Initialize camera {self.camera} before actual autofocus") self.camera.set_frame_type('FRAME_LIGHT') self.camera.prepare_shoot() + self.camera.set_offset(self.camera.default_offset) + self.camera.set_gain(self.camera.default_gain) assert self.camera.is_connected, f"Camera {self.camera} must be connected for autofocus" assert self.camera.focuser.is_connected, f"Focuser {self.camera.focuser} must be connected for autofocus" - def get_thumbnail(self, seconds, thumbnail_size): + def get_thumbnail(self, seconds, thumbnail_size=None): fits = self.camera.get_thumbnail(seconds, thumbnail_size) try: image = fits.data @@ -366,6 +367,7 @@ def do_autofocus(self, See public `autofocus` for information about the parameters. """ + #return self.position, self.position # TODO TN OHP self.initialize_camera() focus_type = 'fine' @@ -373,8 +375,7 @@ def do_autofocus(self, focus_type = 'coarse' initial_focus = self.position - self.logger.debug(f"Beginning {focus_type} autofocus of {self.camera}" - f" - initial position: {initial_focus}") + self.logger.debug(f"Beginning {focus_type} autofocus of {self.camera} - initial position: {initial_focus}") # Set up paths for temporary focus files, and plots if requested. image_dir = self.config['directories']['images'] @@ -420,28 +421,26 @@ def do_autofocus(self, cur_focus_range = focus_range["fine"] cur_focus_step = focus_step["fine"] - self.logger.debug(f"Initial focus is {initial_focus} minus range/2 " - f"gives {initial_focus - cur_focus_range / 2}") - self.logger.debug(f"Initial focus is {initial_focus} plus range/2 gives " - f"{initial_focus + cur_focus_range / 2}") - self.logger.debug(f"Min position is {self.min_position} and " - f"Max position is {self.max_position}") + self.logger.debug(f"Initial focus is {initial_focus} minus range/2 gives {initial_focus - cur_focus_range / 2}") + self.logger.debug(f"Initial focus is {initial_focus} plus range/2 gives {initial_focus + cur_focus_range / 2}") + self.logger.debug(f"Min position is {self.min_position} and Max position is {self.max_position}") self.logger.debug(f"Focus step is {cur_focus_step}") - if not (self.min_position <= initial_focus <= self.max_position): - central_position = (self.min_position+self.max_position)/2 - self.move_to(central_position) - initial_focus = self.position - focus_positions = np.arange(max(initial_focus - cur_focus_range / 2, self.min_position), - min(initial_focus + cur_focus_range / 2, self.max_position) + 1, + central_focus = initial_focus + if not (self.min_position <= central_focus <= self.max_position): + central_focus = (self.min_position+self.max_position)/2 + self.move_to(central_focus) + central_focus = self.position + focus_positions = np.arange(max(central_focus - cur_focus_range / 2, self.min_position), + min(central_focus + cur_focus_range / 2, self.max_position) + 1, cur_focus_step, dtype=int) self.logger.debug(f"Autofocuser {self} is going to sweep over the " f"following positions for autofocusing {focus_positions}") n_positions = len(focus_positions) - thumbnails = np.zeros((n_positions, thumbnail_size, thumbnail_size), + thumbnails = np.zeros((n_positions, initial_thumbnail.shape[0], initial_thumbnail.shape[1]), dtype=initial_thumbnail.dtype) - masks = np.empty((n_positions, thumbnail_size, thumbnail_size), + masks = np.empty((n_positions, initial_thumbnail.shape[0], initial_thumbnail.shape[1]), dtype=bool) metric = np.empty(n_positions) @@ -565,7 +564,7 @@ def do_autofocus(self, # cmap=self.get_palette(), norm=colours.LogNorm()) self.plot_nice_detection_image(initial_thumbnail, ax[0]) #fig.colorbar(im1, ax=ax[0]) - ax[0].set_title('Initial focus position: {}'.format(initial_focus)) + ax[0].set_title(f"Initial focus position: {initial_focus}") ax[1].plot(focus_positions, metric, 'bo', label='{}'.format(merit_function)) if fitted: fs = np.linspace(focus_positions[fitting_indices[0]], @@ -593,8 +592,8 @@ def do_autofocus(self, # cmap=self.get_palette(), norm=colours.LogNorm()) #fig.colorbar(im3, ax=ax[2]) self.plot_nice_detection_image(final_thumbnail, ax[2]) - ax[2].set_title('Final focus position: {}'.format(final_focus)) - plot_path = os.path.join(file_path_root, '{}_focus.png'.format(focus_type)) + ax[2].set_title(f"Final focus position: {final_focus}") + plot_path = os.path.join(file_path_root, f"{focus_type}_focus.png") fig.tight_layout() latest_path = '{}/latest_focus.jpg'.format( @@ -662,8 +661,11 @@ def detect_objects(self, data): def plot_nice_detection_image(self, data, ax): objects = self.detect_objects(data) - norm = ImageNormalize(stretch=SqrtStretch()) - ax.imshow(data, cmap='Greys', origin='lower', norm=norm, interpolation='nearest') + img_eq = exposure.equalize_hist(data) + print_ready_img = img_as_float(img_eq) + #norm = ImageNormalize(stretch=SqrtStretch()) + #ax.imshow(data, cmap='Greys', origin='lower', norm=norm, interpolation='nearest') + ax.imshow(img_eq, cmap='Greys', origin='lower', interpolation='nearest') # plot an ellipse for each object for i in range(len(objects)): e = Ellipse(xy=(objects['x'][i], objects['y'][i]), diff --git a/Mount/Indi10Micron.py b/Mount/Indi10Micron.py index abb4fad8..d454eb7e 100644 --- a/Mount/Indi10Micron.py +++ b/Mount/Indi10Micron.py @@ -4,6 +4,121 @@ class Indi10Micron(IndiAbstractMount): """ + Return of indi_getprop -h 192.168.8.202 -p 7624 "LX200 10micron.*.*" + LX200 10micron.CONNECTION.CONNECT=On + LX200 10micron.CONNECTION.DISCONNECT=Off + LX200 10micron.DRIVER_INFO.DRIVER_NAME=10micron + LX200 10micron.DRIVER_INFO.DRIVER_EXEC=indi_lx200_10micron + LX200 10micron.DRIVER_INFO.DRIVER_VERSION=1.3 + LX200 10micron.DRIVER_INFO.DRIVER_INTERFACE=5 + LX200 10micron.POLLING_PERIOD.PERIOD_MS=1000 + LX200 10micron.DEBUG.ENABLE=Off + LX200 10micron.DEBUG.DISABLE=On + LX200 10micron.SIMULATION.ENABLE=Off + LX200 10micron.SIMULATION.DISABLE=On + LX200 10micron.CONFIG_PROCESS.CONFIG_LOAD=Off + LX200 10micron.CONFIG_PROCESS.CONFIG_SAVE=Off + LX200 10micron.CONFIG_PROCESS.CONFIG_DEFAULT=Off + LX200 10micron.CONFIG_PROCESS.CONFIG_PURGE=Off + LX200 10micron.CONNECTION_MODE.CONNECTION_SERIAL=Off + LX200 10micron.CONNECTION_MODE.CONNECTION_TCP=On + LX200 10micron.DEVICE_ADDRESS.ADDRESS=192.168.8.181 + LX200 10micron.DEVICE_ADDRESS.PORT=3490 + LX200 10micron.CONNECTION_TYPE.TCP=On + LX200 10micron.CONNECTION_TYPE.UDP=Off + LX200 10micron.DEVICE_LAN_SEARCH.INDI_ENABLED=Off + LX200 10micron.DEVICE_LAN_SEARCH.INDI_DISABLED=On + LX200 10micron.ACTIVE_DEVICES.ACTIVE_GPS= + LX200 10micron.ACTIVE_DEVICES.ACTIVE_DOME=Dome Simulator + LX200 10micron.DOME_POLICY.DOME_IGNORED=On + LX200 10micron.DOME_POLICY.DOME_LOCKS=Off + LX200 10micron.ON_COORD_SET.TRACK=On + LX200 10micron.ON_COORD_SET.SLEW=Off + LX200 10micron.ON_COORD_SET.SYNC=Off + LX200 10micron.ON_COORD_SET.FLIP=Off + LX200 10micron.EQUATORIAL_EOD_COORD.RA=11.052448272705078125 + LX200 10micron.EQUATORIAL_EOD_COORD.DEC=89.98590850830078125 + LX200 10micron.TELESCOPE_ABORT_MOTION.ABORT=Off + LX200 10micron.TELESCOPE_TRACK_MODE.TRACK_SIDEREAL=On + LX200 10micron.TELESCOPE_TRACK_MODE.TRACK_SOLAR=Off + LX200 10micron.TELESCOPE_TRACK_MODE.TRACK_LUNAR=Off + LX200 10micron.TELESCOPE_TRACK_MODE.TRACK_CUSTOM=Off + LX200 10micron.TELESCOPE_TRACK_STATE.TRACK_ON=On + LX200 10micron.TELESCOPE_TRACK_STATE.TRACK_OFF=Off + LX200 10micron.TELESCOPE_TRACK_RATE.TRACK_RATE_RA=15.04106717867020393 + LX200 10micron.TELESCOPE_TRACK_RATE.TRACK_RATE_DE=0 + LX200 10micron.TELESCOPE_MOTION_NS.MOTION_NORTH=Off + LX200 10micron.TELESCOPE_MOTION_NS.MOTION_SOUTH=Off + LX200 10micron.TELESCOPE_MOTION_WE.MOTION_WEST=Off + LX200 10micron.TELESCOPE_MOTION_WE.MOTION_EAST=Off + LX200 10micron.TELESCOPE_REVERSE_MOTION.REVERSE_NS=Off + LX200 10micron.TELESCOPE_REVERSE_MOTION.REVERSE_WE=Off + LX200 10micron.TELESCOPE_SLEW_RATE.1x=Off + LX200 10micron.TELESCOPE_SLEW_RATE.2x=Off + LX200 10micron.TELESCOPE_SLEW_RATE.3x=Off + LX200 10micron.TELESCOPE_SLEW_RATE.4x=On + LX200 10micron.TARGET_EOD_COORD.RA=0 + LX200 10micron.TARGET_EOD_COORD.DEC=0 + LX200 10micron.TIME_UTC.UTC=2025-08-01T01:56:10 + LX200 10micron.TIME_UTC.OFFSET=2 + LX200 10micron.GEOGRAPHIC_COORD.LAT=46.233055555555559124 + LX200 10micron.GEOGRAPHIC_COORD.LONG=6.0666666666666664298 + LX200 10micron.GEOGRAPHIC_COORD.ELEV=418.04998799999998482 + LX200 10micron.TELESCOPE_PARK.PARK=Off + LX200 10micron.TELESCOPE_PARK.UNPARK=On + LX200 10micron.TELESCOPE_PIER_SIDE.PIER_WEST=On + LX200 10micron.TELESCOPE_PIER_SIDE.PIER_EAST=Off + LX200 10micron.SAT_TLE_TEXT.TLE= + LX200 10micron.SAT_PASS_WINDOW.SAT_PASS_WINDOW_START=2025-07-31T23:27:36 + LX200 10micron.SAT_PASS_WINDOW.SAT_PASS_WINDOW_END=2025-07-31T23:27:36 + LX200 10micron.SAT_TRACKING_STAT.SAT_TRACK=Off + LX200 10micron.SAT_TRACKING_STAT.SAT_HALT=On + LX200 10micron.USEJOYSTICK.ENABLE=Off + LX200 10micron.USEJOYSTICK.DISABLE=On + LX200 10micron.SNOOP_JOYSTICK.SNOOP_JOYSTICK_DEVICE=Joystick + LX200 10micron.Tracking Frequency.trackFreq=60.200000762939453125 + LX200 10micron.Use Pulse Cmd.Off=Off + LX200 10micron.Use Pulse Cmd.On=On + LX200 10micron.TELESCOPE_TIMED_GUIDE_NS.TIMED_GUIDE_N=0 + LX200 10micron.TELESCOPE_TIMED_GUIDE_NS.TIMED_GUIDE_S=0 + LX200 10micron.TELESCOPE_TIMED_GUIDE_WE.TIMED_GUIDE_W=0 + LX200 10micron.TELESCOPE_TIMED_GUIDE_WE.TIMED_GUIDE_E=0 + LX200 10micron.PRODUCT_INFO.NAME=10micron GM2000HPS + LX200 10micron.PRODUCT_INFO.CONTROL_BOX=Q-TYPE2016 + LX200 10micron.PRODUCT_INFO.FIRMWARE_VERSION=3.1.10 + LX200 10micron.PRODUCT_INFO.FIRMWARE_DATE=2022-10-12T16:47:16 + LX200 10micron.UNATTENDED_FLIP.Disabled=On + LX200 10micron.UNATTENDED_FLIP.Enabled=Off + LX200 10micron.REFRACTION_MODEL_TEMPERATURE.TEMPERATURE=16.700000762939453125 + LX200 10micron.REFRACTION_MODEL_PRESSURE.PRESSURE=942.5999755859375 + LX200 10micron.MODEL_COUNT.COUNT=1 + LX200 10micron.ALIGNMENT_POINTS.COUNT=0 + LX200 10micron.Alignment.Idle=On + LX200 10micron.Alignment.Start=Off + LX200 10micron.Alignment.End=Off + LX200 10micron.Alignment.Del=Off + LX200 10micron.MINIMAL_NEW_ALIGNMENT_POINT_RO.MRA=11.052448272705078125 + LX200 10micron.MINIMAL_NEW_ALIGNMENT_POINT_RO.MDEC=89.98590850830078125 + LX200 10micron.MINIMAL_NEW_ALIGNMENT_POINT_RO.MSIDE=1 + LX200 10micron.MINIMAL_NEW_ALIGNMENT_POINT_RO.SIDTIME=23.077147222222222922 + LX200 10micron.MINIMAL_NEW_ALIGNMENT_POINT.PRA=0 + LX200 10micron.MINIMAL_NEW_ALIGNMENT_POINT.PDEC=0 + LX200 10micron.NEW_ALIGNMENT_POINT.MRA=0 + LX200 10micron.NEW_ALIGNMENT_POINT.MDEC=0 + LX200 10micron.NEW_ALIGNMENT_POINT.MSIDE=0 + LX200 10micron.NEW_ALIGNMENT_POINT.SIDTIME=0 + LX200 10micron.NEW_ALIGNMENT_POINT.PRA=0 + LX200 10micron.NEW_ALIGNMENT_POINT.PDEC=0 + LX200 10micron.NEW_ALIGNMENT_POINTS.COUNT=0 + LX200 10micron.NEW_MODEL_NAME.NAME=newmodel + LX200 10micron.TLE_NUMBER.NUMBER=1 + LX200 10micron.ACTIVE_DEVICES.ACTIVE_GPS= + LX200 10micron.ACTIVE_DEVICES.ACTIVE_DOME=Dome Simulator + LX200 10micron.DOME_POLICY.DOME_IGNORED=On + LX200 10micron.DOME_POLICY.DOME_LOCKS=Off + LX200 10micron.USEJOYSTICK.ENABLE=Off + LX200 10micron.USEJOYSTICK.DISABLE=On + LX200 10micron.SNOOP_JOYSTICK.SNOOP_JOYSTICK_DEVICE=Joystick """ def __init__(self, location, serv_time, diff --git a/Mount/IndiMount.py b/Mount/IndiMount.py index 9d7e55ee..17592b85 100644 --- a/Mount/IndiMount.py +++ b/Mount/IndiMount.py @@ -2,6 +2,7 @@ import io import json import logging +import time # Indi stuff from helper.IndiDevice import IndiDevice @@ -92,6 +93,7 @@ def slew_to_coord_and_track(self, coord): """ self.on_coord_set('TRACK') self.set_coord(coord) + time.sleep(10) # TODO TN: temporary solution to the fact that indi returns too early from pointing def sync_to_coord(self, coord): """ diff --git a/ObservationPlanner/Scheduler.py b/ObservationPlanner/Scheduler.py index 364ccbb5..adcf36ab 100644 --- a/ObservationPlanner/Scheduler.py +++ b/ObservationPlanner/Scheduler.py @@ -277,6 +277,7 @@ def define_target(self, target_name): except: try: #"5h12m43.2s +31d12m43s" is perfectly valid + #"22h37m03s.645 +34d25m7.96s" target = FixedTarget(name=target_name.replace(" ", ""), coord=SkyCoord( target_name, diff --git a/ObservationPlanner/SpectroScheduler.py b/ObservationPlanner/SpectroScheduler.py index 9b776c63..49d178e5 100644 --- a/ObservationPlanner/SpectroScheduler.py +++ b/ObservationPlanner/SpectroScheduler.py @@ -285,7 +285,7 @@ def get_observation(self, time=None, show_all=False, """ # Before doing anything, we want to know if we need to acquire a # reference observation - if self.current_observation is not None: + if False: #self.current_observation is not None: # TODO TN OHP # If observation is not itself a reference observation and it does not features a reference yet if ((not self.current_observation.is_reference_observation) and (self.current_observation.reference_observation_id is None)): diff --git a/Pointer/DifferentialPointer.py b/Pointer/DifferentialPointer.py index c6bb5ef5..2c2e3292 100644 --- a/Pointer/DifferentialPointer.py +++ b/Pointer/DifferentialPointer.py @@ -65,7 +65,7 @@ def points_async(self, mount, camera, observation, fits_headers, pointing_event, self.logger.debug(f"Exposing for camera: {camera.name}") try: - # Start the exposures + # Start the exposures - TODO TN URGENT YOU NEED TO IMPLEMENT TAKE_POINTING, OTHERWISE IT WILL SET TEMPERATURE TO OBSERVATION VALUE camera_event = camera.take_observation( observation=observation, headers=fits_headers, diff --git a/Service/IndiUranusMeteoSensor.py b/Service/IndiUranusMeteoSensor.py new file mode 100644 index 00000000..39c8fd91 --- /dev/null +++ b/Service/IndiUranusMeteoSensor.py @@ -0,0 +1,306 @@ +# Generic stuff +from collections import deque +import json +import logging +import threading +import time + +# Numerical tools +import numpy as np + +# Astropy +import astropy.units as u + +#Local stuff +from Base.Base import Base +from helper.IndiDevice import IndiDevice +from Service.PanMessaging import PanMessaging + + +class IndiUranusMeteoSensor(threading.Thread, IndiDevice): + """ + Here is the return of + indi_getprop -h 192.168.8.202 -p 7624 "Uranus Meteo Sensor.*.*" + Uranus Meteo Sensor.CONNECTION.CONNECT=On + Uranus Meteo Sensor.CONNECTION.DISCONNECT=Off + Uranus Meteo Sensor.DRIVER_INFO.DRIVER_NAME=Uranus Meteo Sensor + Uranus Meteo Sensor.DRIVER_INFO.DRIVER_EXEC=indi_uranus_weather + Uranus Meteo Sensor.DRIVER_INFO.DRIVER_VERSION=1.0 + Uranus Meteo Sensor.DRIVER_INFO.DRIVER_INTERFACE=192 + Uranus Meteo Sensor.DEBUG.ENABLE=Off + Uranus Meteo Sensor.DEBUG.DISABLE=On + Uranus Meteo Sensor.SIMULATION.ENABLE=Off + Uranus Meteo Sensor.SIMULATION.DISABLE=On + Uranus Meteo Sensor.CONFIG_PROCESS.CONFIG_LOAD=Off + Uranus Meteo Sensor.CONFIG_PROCESS.CONFIG_SAVE=Off + Uranus Meteo Sensor.CONFIG_PROCESS.CONFIG_DEFAULT=Off + Uranus Meteo Sensor.CONFIG_PROCESS.CONFIG_PURGE=Off + Uranus Meteo Sensor.POLLING_PERIOD.PERIOD_MS=5000 + Uranus Meteo Sensor.CONNECTION_MODE.CONNECTION_SERIAL=On + Uranus Meteo Sensor.SYSTEM_PORTS.Pegasus_Astro_UPBv2_revD_UPB25S4VWV=Off + Uranus Meteo Sensor.SYSTEM_PORTS.Pegasus_Astro_PPBADV_Gen2C_PPBA93RF0I=Off + Uranus Meteo Sensor.SYSTEM_PORTS.Arduino_Uranus_MeteoSensor_95A5389D50555233342E3120FF12122B=Off + Uranus Meteo Sensor.SYSTEM_PORTS.1a86_USB_Serial=Off + Uranus Meteo Sensor.DEVICE_PORT.PORT=/dev/serial/by-id/usb-Arduino_Uranus_MeteoSensor_95A5389D50555233342E3120FF12122B-if00 + Uranus Meteo Sensor.DEVICE_BAUD_RATE.9600=Off + Uranus Meteo Sensor.DEVICE_BAUD_RATE.19200=Off + Uranus Meteo Sensor.DEVICE_BAUD_RATE.38400=Off + Uranus Meteo Sensor.DEVICE_BAUD_RATE.57600=Off + Uranus Meteo Sensor.DEVICE_BAUD_RATE.115200=On + Uranus Meteo Sensor.DEVICE_BAUD_RATE.230400=Off + Uranus Meteo Sensor.DEVICE_AUTO_SEARCH.INDI_ENABLED=Off + Uranus Meteo Sensor.DEVICE_AUTO_SEARCH.INDI_DISABLED=On + Uranus Meteo Sensor.DEVICE_PORT_SCAN.Scan Ports=Off + Uranus Meteo Sensor.GEOGRAPHIC_COORD.LAT=43.932209999999997763 + Uranus Meteo Sensor.GEOGRAPHIC_COORD.LONG=5.7156500000000001194 + Uranus Meteo Sensor.GEOGRAPHIC_COORD.ELEV=0 + Uranus Meteo Sensor.TIME_UTC.UTC=2025-07-31T17:44:23 + Uranus Meteo Sensor.TIME_UTC.OFFSET=2.00 + Uranus Meteo Sensor.GPS_REFRESH.REFRESH=Off + Uranus Meteo Sensor.GPS_REFRESH_PERIOD.PERIOD=0 + Uranus Meteo Sensor.SYSTEM_TIME_UPDATE.UPDATE_NEVER=Off + Uranus Meteo Sensor.SYSTEM_TIME_UPDATE.UPDATE_ON_STARTUP=On + Uranus Meteo Sensor.SYSTEM_TIME_UPDATE.UPDATE_ON_REFRESH=Off + Uranus Meteo Sensor.SENSORS.AmbientTemperature=20.359999999999999432 + Uranus Meteo Sensor.SENSORS.RelativeHumidity=47 + Uranus Meteo Sensor.SENSORS.DewPoint=8.6699999999999999289 + Uranus Meteo Sensor.SENSORS.AbsolutePressure=944.26999999999998181 + Uranus Meteo Sensor.SENSORS.RelativePressure=0 + Uranus Meteo Sensor.SENSORS.BarometricAltitude=672 + Uranus Meteo Sensor.SENSORS.SkyTemperature=7.9100000000000001421 + Uranus Meteo Sensor.SENSORS.InfraredTemperature=20.550000000000000711 + Uranus Meteo Sensor.SENSORS.BatteryUsage=0 + Uranus Meteo Sensor.SENSORS.BatteryVoltage=5.0899999999999998579 + Uranus Meteo Sensor.CLOUDS.TemperatureDifference=19.730000000000000426 + Uranus Meteo Sensor.CLOUDS.CloudIndex=100 + Uranus Meteo Sensor.CLOUDS.CloudSkyTemperature=7.9100000000000001421 + Uranus Meteo Sensor.CLOUDS.CloudAmbientTemperature=20.550000000000000711 + Uranus Meteo Sensor.CLOUDS.InfraredEmissivity=1 + Uranus Meteo Sensor.SKYQUALITY.MPAS=19.820000000000000284 + Uranus Meteo Sensor.SKYQUALITY.NELM=5.3700000000000001066 + Uranus Meteo Sensor.SKYQUALITY.FullSpectrum=52 + Uranus Meteo Sensor.SKYQUALITY.VisualSpectrum=29 + Uranus Meteo Sensor.SKYQUALITY.InfraredSpectrum=23 + Uranus Meteo Sensor.SKYQUALITY_TIMER.VALUE=60 + Uranus Meteo Sensor.GPS.GPSFix=3 + Uranus Meteo Sensor.GPS.GPSTime=1753991063 + Uranus Meteo Sensor.GPS.UTCOffset=2 + Uranus Meteo Sensor.GPS.Latitude=43.932209999999997763 + Uranus Meteo Sensor.GPS.Longitude=5.7156500000000001194 + Uranus Meteo Sensor.GPS.SatelliteNumber=7 + Uranus Meteo Sensor.GPS.GPSSpeed=680 + Uranus Meteo Sensor.GPS.GPSBearing=0.54000000000000003553 + Uranus Meteo Sensor.WEATHER_UPDATE.PERIOD=60 + Uranus Meteo Sensor.WEATHER_REFRESH.REFRESH=Off + Uranus Meteo Sensor.WEATHER_OVERRIDE.OVERRIDE=Off + Uranus Meteo Sensor.WEATHER_STATUS.WEATHER_CLOUD=Alert + Uranus Meteo Sensor.WEATHER_STATUS.WEATHER_TEMPERATURE=Ok + Uranus Meteo Sensor.WEATHER_STATUS.WEATHER_HUMIDITY=Ok + Uranus Meteo Sensor.WEATHER_PARAMETERS.WEATHER_CLOUD=100 + Uranus Meteo Sensor.WEATHER_PARAMETERS.WEATHER_MPAS=19.820000000000000284 + Uranus Meteo Sensor.WEATHER_PARAMETERS.WEATHER_TEMPERATURE=20.359999999999999432 + Uranus Meteo Sensor.WEATHER_PARAMETERS.WEATHER_HUMIDITY=47 + Uranus Meteo Sensor.WEATHER_CLOUD.MIN_OK=0 + Uranus Meteo Sensor.WEATHER_CLOUD.MAX_OK=85 + Uranus Meteo Sensor.WEATHER_CLOUD.PERC_WARN=15 + Uranus Meteo Sensor.WEATHER_CLOUD.ALERT_TYPE=0 + Uranus Meteo Sensor.WEATHER_MPAS.MIN_OK=1 + Uranus Meteo Sensor.WEATHER_MPAS.MAX_OK=30 + Uranus Meteo Sensor.WEATHER_MPAS.PERC_WARN=15 + Uranus Meteo Sensor.WEATHER_MPAS.ALERT_TYPE=0 + Uranus Meteo Sensor.WEATHER_TEMPERATURE.MIN_OK=-20 + Uranus Meteo Sensor.WEATHER_TEMPERATURE.MAX_OK=50 + Uranus Meteo Sensor.WEATHER_TEMPERATURE.PERC_WARN=15 + Uranus Meteo Sensor.WEATHER_TEMPERATURE.ALERT_TYPE=0 + Uranus Meteo Sensor.WEATHER_HUMIDITY.MIN_OK=0 + Uranus Meteo Sensor.WEATHER_HUMIDITY.MAX_OK=75 + Uranus Meteo Sensor.WEATHER_HUMIDITY.PERC_WARN=15 + Uranus Meteo Sensor.WEATHER_HUMIDITY.ALERT_TYPE=0 + """ + + def __init__(self, logger=None, config=None, serv_time=None, + connect_on_create=True, loop_on_create=False): + logger = logger or logging.getLogger(__name__) + + if config is None: + config = dict( + service_name="AAG Cloud Watcher", + delay_sec=60, + indi_client=dict( + indi_host="localhost", + indi_port="7624"), + limits=dict( + MAX_WEATHER_WIND_SPEED_KPH=25, + MAX_WEATHER_WIND_GUST_KPH=30, + MAX_WEATHER_CLOUD_COVER=5) + ) + + logger.debug(f"Indi Weather service, name is: {config['service_name']}") + + # device related intialization + IndiDevice.__init__(self, + device_name=config['service_name'], + indi_driver_name=config.get('indi_driver_name', None), + indi_client_config=config["indi_client"]) + + # Init parent thread + threading.Thread.__init__(self, target=self.serve) + self._stop_event = threading.Event() + + # we broadcast data throught a message queue style mecanism + self.messaging = None + + # store result in a database + self.serv_time = serv_time + self.store_result = True + self._do_run = True + self._delay_sec = config["delay_sec"] + # we store the last 10 entries + self.weather_entries = deque([], 3) + + # Actual threshold for safety alerts + self.limits = config["limits"] + + if connect_on_create: + self.initialize() + + # Finished configuring + self.logger.debug('Indi Weather service configured successfully') + + if loop_on_create: + self.start() + + def initialize(self): + """ + Connect, and setup coordinatea + """ + self.connect() + self.set_geographic_coord() + self.set_update_period() + + def send_message(self, msg, channel='WEATHER'): + if self.messaging is None: + # TODO TN: if it breaks, checkout IndiWeather code instead + self.messaging = PanMessaging(**self.config["messaging_publisher"]) + self.messaging.send_message(channel, msg) + + def capture(self, send_message=True, store_result=True): + """ Query the weather station and eventually publish results""" + self.logger.debug("Updating weather") + + data = self._fill_in_weather_data() + data['weather_sensor_name'] = self.device_name + data['date'] = self.serv_time.get_utc() + self.weather_entries.append(data) + + if send_message: + self.send_message({'data': data}, channel='WEATHER') + + if store_result and self.store_result: + self.db.insert_current('weather', data) + + return data + + def serve(self): + """ + Continuously generates weather reports + """ + while not self.stopped(): + self.capture() + time.sleep(self._delay_sec) + + def stop(self): + """ + Stops the web server. + """ + self._stop_event.set() + + def stopped(self): + """ + Checks if server is stopped. + + :return: True if server is stopped, False otherwise + """ + return self._stop_event.is_set() + + def set_geographic_coord(self): + self.set_number('GEOGRAPHIC_COORD', + {'LAT': self.config['observatory']['latitude'], + 'LONG': self.config['observatory']['longitude'], + 'ELEV': self.config['observatory']['elevation'] }, + sync=True) + + def set_update_period(self): + self.set_number('WEATHER_UPDATE', + {'PERIOD': self._delay_sec}, + sync=True) + + def get_weather_features(self): + """ + get the whole set of values + """ + return self.get_number('WEATHER_PARAMETERS') + + def _fill_in_weather_data(self): + """ + + """ + features = self.get_weather_features() + data = {} + #data['sky_temp_C'] = np.random.randint(-10, 30) + #data['ambient_temp_C'] = np.random.randint(-10, 30) + #data['rain_sensor_temp_C'] = np.random.randint(-10, 30) + #data['rain_frequency'] = np.random.randint(-10, 30) + #data['errors'] = 'no error' + #data['wind_speed_KPH'] = np.random.randint(0, 100) + + # some electronic stuff + #data['pwm_value'] = np.random.randint(0, 50) + #data['ldr_resistance_Ohm'] = np.random.randint(2500, 5000) + + # Make Safety Decision + # self.safe_dict = self.make_safety_decision(data) + #data['safe'] = True + #data['sky_condition'] = 'Sky_condition' + #data['wind_condition'] = 'Wind_condition' + #data['gust_condition'] = 'Gust_condition' + #data['rain_condition'] = 'Rain_condition' + + # Generic indi state for this property, can be OK, IDLE, BUSY, ALERT + data["state"] = features["state"] + # name: WEATHER_FORECAST, label: Weather, format: '%4.2f' + data["WEATHER_FORECAST"] = features["WEATHER_FORECAST"] + # name: WEATHER_TEMPERATURE, label: Temperature (C), format: '%4.2f' + data["WEATHER_TEMPERATURE"] = features["WEATHER_TEMPERATURE"] + # name: WEATHER_WIND_SPEED, label: Wind (kph), format: '%4.2f' + data["WEATHER_WIND_SPEED"] = features["WEATHER_WIND_SPEED"] + # name: WEATHER_WIND_GUST, label: Gust (kph), format: '%4.2f' + data["WEATHER_WIND_GUST"] = features["WEATHER_WIND_GUST"] + # name: WEATHER_RAIN_HOUR, label: Precip (mm), format: '%4.2f' + data["WEATHER_RAIN_HOUR"] = features["WEATHER_RAIN_HOUR"] + data["safe"] = self._make_safety_decision(data) + return data + + def _make_safety_decision(self, features): + """ + based on: + name: WEATHER_FORECAST, label: Weather, format: '%4.2f' + name: WEATHER_TEMPERATURE, label: Temperature (C), format: '%4.2f' + name: WEATHER_WIND_SPEED, label: Wind (kph), format: '%4.2f' + name: WEATHER_WIND_GUST, label: Gust (kph), format: '%4.2f' + name: WEATHER_RAIN_HOUR, label: Precip (mm), format: '%4.2f' + """ + status = features["state"] == 'OK' + status = status and (np.float32(features["WEATHER_WIND_SPEED"]) < + self.limits["MAX_WEATHER_WIND_SPEED_KPH"]) + status = status and (np.float32(features["WEATHER_WIND_GUST"]) < + self.limits["MAX_WEATHER_WIND_GUST_KPH"]) + status = status and (np.float32(features["WEATHER_RAIN_HOUR"]) == 0) + return bool(status) + + def __str__(self): + return f"Weather service: {self.device_name}" + + def __repr__(self): + return self.__str__() # Get key from json + diff --git a/StateMachine/StateMachine.py b/StateMachine/StateMachine.py index b8da28a5..cad22065 100644 --- a/StateMachine/StateMachine.py +++ b/StateMachine/StateMachine.py @@ -268,7 +268,7 @@ def run(self, exit_when_done=False): time, defaults to False to loop continuously. after run don't do state anymore but still read messages """ - assert self.is_initialized, self.logger.error("not initialized") + assert self.is_initialized, "not initialized" self._keep_running = True self._do_states = True diff --git a/StateMachine/States/observing.py b/StateMachine/States/observing.py index b93755f0..36be914a 100644 --- a/StateMachine/States/observing.py +++ b/StateMachine/States/observing.py @@ -13,7 +13,8 @@ STATUS_INTERVAL = 10. * u.second GUIDER_STATUS_INTERVAL = 5. * u.second WAITING_MSG_INTERVAL = 5. * u.second -MAX_EXTRA_TIME = (100+SLEEP_SECONDS) * u.second +# 300 seconds might seems a lot, but there can be time dedicated to cooling the camera +MAX_EXTRA_TIME = (300+SLEEP_SECONDS) * u.second def on_enter(event_data): #TODO TN DEBUG diff --git a/apps/prototyping/Camera/IndiASICameraDriverTest.py b/apps/prototyping/Camera/IndiASICameraDriverTest.py index df0a7c71..19e5d845 100644 --- a/apps/prototyping/Camera/IndiASICameraDriverTest.py +++ b/apps/prototyping/Camera/IndiASICameraDriverTest.py @@ -31,7 +31,7 @@ indi_port=7624), # focuser=dict( # module="IndiFocuser", - # focuser_name="Focuser Simulator", + # device_name="Focuser Simulator", # port="/dev/ttyUSB0", # focus_range=dict( # min=30000, diff --git a/apps/prototyping/Camera/IndiASICameraTest.py b/apps/prototyping/Camera/IndiASICooledCameraTest.py similarity index 95% rename from apps/prototyping/Camera/IndiASICameraTest.py rename to apps/prototyping/Camera/IndiASICooledCameraTest.py index c6e1bfdc..907de7df 100644 --- a/apps/prototyping/Camera/IndiASICameraTest.py +++ b/apps/prototyping/Camera/IndiASICooledCameraTest.py @@ -24,8 +24,8 @@ # indi_port="7624" # )) config = dict( - camera_name='ZWO CCD ASI183MM Pro', - pointing_seconds=30, + camera_name='ZWO CCD ASI533MM Pro', + pointing_seconds=5, adjust_center_x=400, adjust_center_y=400, adjust_roi_search_size=50, @@ -34,11 +34,11 @@ autofocus_roi_size=500, autofocus_merit_function="half_flux_radius", indi_client=dict( - indi_host="192.168.144.32", + indi_host="192.168.8.202", indi_port=7624), # focuser=dict( # module="IndiFocuser", - # focuser_name="Focuser Simulator", + # device_name="Focuser Simulator", # port="/dev/ttyUSB0", # focus_range=dict( # min=30000, diff --git a/apps/prototyping/Camera/IndiASINonCooledCameraTest.py b/apps/prototyping/Camera/IndiASINonCooledCameraTest.py new file mode 100644 index 00000000..de424228 --- /dev/null +++ b/apps/prototyping/Camera/IndiASINonCooledCameraTest.py @@ -0,0 +1,110 @@ +# Basic stuff + +# Viz stuff +import matplotlib.pyplot as plt +from skimage import img_as_float +from skimage import exposure + +# Local stuff : IndiClient +from helper.IndiClient import IndiClient + +# Local stuff : Camera +from Camera.IndiASICameraNonCool import IndiASICameraNonCool +from Service.NTPTimeService import HostTimeService + +if __name__ == '__main__': + # test indi client + # config = dict( + # camera_name='Altair AA183MPRO', + # autofocus_seconds=5, + # pointing_seconds=30, + # autofocus_roi_size=500, + # indi_client=dict( + # indi_host="192.168.0.33", + # indi_port="7624" + # )) + config = dict( + camera_name='ZWO CCD ASI290MM Mini', + pointing_seconds=5, + adjust_center_x=400, + adjust_center_y=400, + adjust_roi_search_size=50, + adjust_pointing_seconds=5, + autofocus_seconds=5, + autofocus_roi_size=500, + autofocus_merit_function="half_flux_radius", + indi_client=dict( + indi_host="192.168.8.202", + indi_port=7624), + # focuser=dict( + # module="IndiFocuser", + # device_name="Focuser Simulator", + # port="/dev/ttyUSB0", + # focus_range=dict( + # min=30000, + # max=50000), + # autofocus_step=dict( + # fine=750, + # coarse=2500), + # autofocus_range=dict( + # fine=15000, + # coarse=30000), + # indi_client=dict( + # indi_host="192.168.144.32", + # indi_port="7624"), + # ) + ) + + # test indi virtual camera class + cam = IndiASICameraNonCool(config=config, + serv_time=HostTimeService(), + connect_on_create=False) + cam.connect() + + # Play with camera configuration + cam.set_roi({'X': 256, 'Y': 480, 'WIDTH': 512, 'HEIGHT': 640}) + # get_roi + print(f"Current camera ROI is: {cam.get_roi()}") + cam.set_roi({'X': 0, 'Y': 0, 'WIDTH': 1280, 'HEIGHT': 1024}) + # get_roi + print(f"Current camera ROI is: {cam.get_roi()}") + + #print('Setting cooling on') + #cam.set_cooling_on() THIS VECTOR IS EXPECTED TO BE IN BUSY STATE, NOT IDLE NOR OK, THAT's WHY THERE IS TIMEOUT + print(f"Current camera temperature is: {cam.get_temperature()}") + target_temp = -5.5 + print(f"Now, setting temperature to: {target_temp}") + cam.set_temperature(target_temp) + print(f"Current camera temperature is: {cam.get_temperature()}") + target_temp = -8 + print(f"Now, setting temperature to: {target_temp}") + cam.set_temperature(target_temp) + print(f"Current camera temperature is: {cam.get_temperature()}") + #cam.set_cooling_off() + + # set frame type (mostly for simulation purpose + cam.set_frame_type('FRAME_DARK') + cam.set_frame_type('FRAME_FLAT') + cam.set_frame_type('FRAME_BIAS') + cam.set_frame_type('FRAME_LIGHT') + + # set gain + print(f"gain is {cam.get_gain()}") + cam.set_gain(100) + print(f"gain is {cam.get_gain()}") + + # Acquire data + cam.prepare_shoot() + cam.setExpTimeSec(10) + cam.shoot_async() + cam.synchronize_with_image_reception() + fits = cam.get_received_image() + + # Show image + fig, ax = plt.subplots(1, figsize=(16, 9)) + img = fits[0].data + img_eq = exposure.equalize_hist(img) + print_ready_img = img_as_float(img_eq) + print(f"Print ready has shape {print_ready_img.shape}") + ax.imshow(print_ready_img) + plt.show() \ No newline at end of file diff --git a/apps/prototyping/Camera/IndiCameraFocusing.py b/apps/prototyping/Camera/IndiCameraFocusing.py index c9eb59f5..33592752 100644 --- a/apps/prototyping/Camera/IndiCameraFocusing.py +++ b/apps/prototyping/Camera/IndiCameraFocusing.py @@ -42,12 +42,16 @@ FOCAL_LENGTH=800, APERTURE=200), pointing_seconds=30, + adjust_center_x=400, + adjust_center_y=400, + adjust_roi_search_size=50, + adjust_pointing_seconds=5, autofocus_seconds=5, - autofocus_roi_size=650, + autofocus_roi_size=500, autofocus_merit_function="half_flux_radius", focuser=dict( module="IndiFocuser", - focuser_name="Focuser Simulator", + device_name="Focuser Simulator", port="/dev/ttyUSB0", focus_range=dict( min=25000, diff --git a/apps/prototyping/Camera/IndiPlayerOneCameraTest.py b/apps/prototyping/Camera/IndiPlayerOneCameraTest.py new file mode 100644 index 00000000..3e98b9d1 --- /dev/null +++ b/apps/prototyping/Camera/IndiPlayerOneCameraTest.py @@ -0,0 +1,110 @@ +# Basic stuff + +# Viz stuff +import matplotlib.pyplot as plt +from skimage import img_as_float +from skimage import exposure + +# Local stuff : IndiClient +from helper.IndiClient import IndiClient + +# Local stuff : Camera +from Camera.IndiPlayerOneCamera import IndiPlayerOneCamera +from Service.NTPTimeService import HostTimeService + +if __name__ == '__main__': + # test indi client + # config = dict( + # camera_name='Altair AA183MPRO', + # autofocus_seconds=5, + # pointing_seconds=30, + # autofocus_roi_size=500, + # indi_client=dict( + # indi_host="192.168.0.33", + # indi_port="7624" + # )) + config = dict( + camera_name='PlayerOne CCD Ares-M PRO', + pointing_seconds=5, + adjust_center_x=400, + adjust_center_y=400, + adjust_roi_search_size=50, + adjust_pointing_seconds=5, + autofocus_seconds=5, + autofocus_roi_size=500, + autofocus_merit_function="half_flux_radius", + indi_client=dict( + indi_host="192.168.0.194", + indi_port=7624), + # focuser=dict( + # module="IndiFocuser", + # device_name="Focuser Simulator", + # port="/dev/ttyUSB0", + # focus_range=dict( + # min=30000, + # max=50000), + # autofocus_step=dict( + # fine=750, + # coarse=2500), + # autofocus_range=dict( + # fine=15000, + # coarse=30000), + # indi_client=dict( + # indi_host="192.168.144.32", + # indi_port="7624"), + # ) + ) + + # test indi virtual camera class + cam = IndiPlayerOneCamera(config=config, + serv_time=HostTimeService(), + connect_on_create=False) + cam.connect() + + # Play with camera configuration + cam.set_roi({'X': 256, 'Y': 480, 'WIDTH': 512, 'HEIGHT': 640}) + # get_roi + print(f"Current camera ROI is: {cam.get_roi()}") + cam.set_roi({'X': 0, 'Y': 0, 'WIDTH': 1280, 'HEIGHT': 1024}) + # get_roi + print(f"Current camera ROI is: {cam.get_roi()}") + + #print('Setting cooling on') + #cam.set_cooling_on() THIS VECTOR IS EXPECTED TO BE IN BUSY STATE, NOT IDLE NOR OK, THAT's WHY THERE IS TIMEOUT + print(f"Current camera temperature is: {cam.get_temperature()}") + target_temp = -5.5 + print(f"Now, setting temperature to: {target_temp}") + cam.set_temperature(target_temp) + print(f"Current camera temperature is: {cam.get_temperature()}") + target_temp = -8 + print(f"Now, setting temperature to: {target_temp}") + cam.set_temperature(target_temp) + print(f"Current camera temperature is: {cam.get_temperature()}") + #cam.set_cooling_off() + + # set frame type (mostly for simulation purpose + cam.set_frame_type('FRAME_DARK') + cam.set_frame_type('FRAME_FLAT') + cam.set_frame_type('FRAME_BIAS') + cam.set_frame_type('FRAME_LIGHT') + + # set gain + print(f"gain is {cam.get_gain()}") + cam.set_gain(100) + print(f"gain is {cam.get_gain()}") + + # Acquire data + cam.prepare_shoot() + cam.setExpTimeSec(10) + cam.shoot_async() + cam.synchronize_with_image_reception() + fits = cam.get_received_image() + + # Show image + fig, ax = plt.subplots(1, figsize=(16, 9)) + img = fits[0].data + img_eq = exposure.equalize_hist(img) + print_ready_img = img_as_float(img_eq) + print(f"Print ready has shape {print_ready_img.shape}") + ax.imshow(print_ready_img) + plt.show() \ No newline at end of file diff --git a/apps/prototyping/Camera/IndiRealCameraFocusing.py b/apps/prototyping/Camera/IndiRealCameraFocusing.py new file mode 100644 index 00000000..73509432 --- /dev/null +++ b/apps/prototyping/Camera/IndiRealCameraFocusing.py @@ -0,0 +1,112 @@ +# Basic stuff +import logging.config + +# Miscellaneous +import matplotlib.pyplot as plt + +# Local stuff : Camera +from Camera.IndiASICameraNonCool import IndiASICameraNonCool +from Service.NTPTimeService import HostTimeService + +# For this t +if __name__ == '__main__': + + # load the logging configuration + logging.config.fileConfig('logging.ini') + + config = dict( + camera_name='ZWO CCD ASI290MM Mini', + # SCOPE_INFO=dict( + # FOCAL_LENGTH=800, + # APERTURE=200), + default_exp_time_sec=5, + default_gain=300, + default_offset=10, + autofocus_seconds=4, + autofocus_roi_size=None, + autofocus_merit_function="half_flux_radius", #"vollath_F4" + focuser=dict( + module="AggregatedCustomScopeControllerUPBv2", + device_name="Pegasus UPB", + device_port="/dev/serial/by-id/usb-Pegasus_Astro_UPBv2_revD_UPB25S4VWV-if00-port0", + connection_type="CONNECTION_SERIAL", + baud_rate="9600", + polling_ms="1000", + dustcap_travel_delay_s="10", + adjustable_voltage_value=5, + power_labels=dict( + POWER_LABEL_1="MAIN_TELESCOPE_DUSTCAP_CONTROL", + POWER_LABEL_2="SPOX_AND_DUSTCAP_POWER", + POWER_LABEL_3="MAIN_CAMERA_POWER", + POWER_LABEL_4="MOUNT_POWER"), + always_on_power_identifiers=dict( + MAIN_TELESCOPE_DUSTCAP_CONTROL="True", + SPOX_AND_DUSTCAP_POWER="False", + MAIN_CAMERA_POWER="False", + MOUNT_POWER="False"), + usb_labels=dict( + USB_LABEL_1="FIELD_CAMERA", + USB_LABEL_2="PRIMARY_CAMERA", + USB_LABEL_3="SPECTRO_CONTROL_BOX", + USB_LABEL_4="ARDUINO_CONTROL_BOX", + USB_LABEL_5="WIFI_ROUTER", + USB_LABEL_6="GUIDE_CAMERA"), + always_on_usb_identifiers=dict( + FIELD_CAMERA="False", + PRIMARY_CAMERA="False", + SPECTRO_CONTROL_BOX="False", + ARDUINO_CONTROL_BOX="False", + WIFI_ROUTER="True,", + GUIDE_CAMERA="False"), + dew_labels=dict( + DEW_LABEL_1="PRIMARY_FAN", + DEW_LABEL_2="SECONDARY_DEW_HEATER", + DEW_LABEL_3="FINDER_DEW_HEATER"), + auto_dew_identifiers=dict( + PRIMARY_FAN="False", + SECONDARY_DEW_HEATER="True", + FINDER_DEW_HEATER="True"), + auto_dew_aggressivity=150, # Number between 50 and 250 + default_focus=8000, + focus_range=dict( + min=7500, + max=9000), + autofocus_step=dict( + coarse=250, + fine=100), + autofocus_range=dict( + coarse=2500, + fine=2500), + indi_client=dict( + indi_host="192.168.8.202", + indi_port="7624") + ), + indi_client=dict( + indi_host="192.168.8.202", + indi_port="7624") + ) + + # test indi virtual camera class + cam = IndiASICameraNonCool(serv_time=HostTimeService(), config=config, connect_on_create=True) + cam.prepare_shoot() + + def get_thumb(cam): + thumbnail_size = 500 + cam.prepare_shoot() + fits = cam.get_thumbnail(exp_time_sec=5, thumbnail_size=thumbnail_size) + try: + image = fits.data + except: + image = fits[0].data + plt.imshow(image) + plt.show() + + # Now focus + assert(cam.focuser.is_connected) + autofocus_status = [False] + #autofocus_event = cam.autofocus_async(coarse=True, autofocus_status=autofocus_status) + autofocus_event = cam.autofocus_async(coarse=False, autofocus_status=autofocus_status) + autofocus_event.wait() + cam.focuser.park_focuser() + assert autofocus_status[0], "Focusing failed" + print("Done") diff --git a/apps/prototyping/Camera/IndiRealCameraFocusingBaader.py b/apps/prototyping/Camera/IndiRealCameraFocusingBaader.py new file mode 100644 index 00000000..28390a4d --- /dev/null +++ b/apps/prototyping/Camera/IndiRealCameraFocusingBaader.py @@ -0,0 +1,78 @@ +# Basic stuff +import logging.config + +# Miscellaneous +import matplotlib.pyplot as plt + +# Local stuff : Camera +from Camera.IndiASICameraNonCool import IndiASICameraNonCool +from Service.NTPTimeService import HostTimeService + +# For this t +if __name__ == '__main__': + + # load the logging configuration + logging.config.fileConfig('logging.ini') + + config = dict( + camera_name='ZWO CCD ASI290MM Mini', + # SCOPE_INFO=dict( + # FOCAL_LENGTH=800, + # APERTURE=200), + default_exp_time_sec=5, + default_gain=150, + default_offset=10, + autofocus_seconds=2, + autofocus_roi_size=None, + autofocus_merit_function="half_flux_radius", #"vollath_F4" + focuser=dict( + module="IndiBaaderSteelDrive2Focuser", + device_name="Baader SteelDriveII", + device_port="/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AB0KCL5O-if00-port0", + connection_type="CONNECTION_SERIAL", + baud_rate="19200", + polling_ms="1000", + home_position="2000", + default_focus="8450", + focus_range=dict( + min=8200, + max=8700), + autofocus_step=dict( + coarse=50, + fine=10), + autofocus_range=dict( + coarse=500, + fine=150), + indi_client=dict( + indi_host="192.168.0.194", + indi_port="7624") + ), + indi_client=dict( + indi_host="192.168.0.194", + indi_port="7624") + ) + + # test indi virtual camera class + cam = IndiASICameraNonCool(serv_time=HostTimeService(), config=config, connect_on_create=True) + cam.prepare_shoot() + + def get_thumb(cam): + thumbnail_size = 500 + cam.prepare_shoot() + fits = cam.get_thumbnail(exp_time_sec=5, thumbnail_size=thumbnail_size) + try: + image = fits.data + except: + image = fits[0].data + plt.imshow(image) + plt.show() + + # Now focus + assert(cam.focuser.is_connected) + autofocus_status = [False] + autofocus_event = cam.autofocus_async(coarse=True, autofocus_status=autofocus_status) + #autofocus_event = cam.autofocus_async(coarse=False, autofocus_status=autofocus_status) + autofocus_event.wait() + cam.focuser.park_focuser() + assert autofocus_status[0], "Focusing failed" + print("Done") diff --git a/apps/prototyping/Camera/IndiSimulatorCameraTest.py b/apps/prototyping/Camera/IndiSimulatorCameraTest.py index b53a0e4b..797dedda 100644 --- a/apps/prototyping/Camera/IndiSimulatorCameraTest.py +++ b/apps/prototyping/Camera/IndiSimulatorCameraTest.py @@ -48,7 +48,7 @@ indi_port="7624"), focuser=dict( module="IndiFocuser", - focuser_name="Focuser Simulator", + device_name="Focuser Simulator", port="/dev/ttyUSB0", focus_range=dict( min=30000, diff --git a/apps/prototyping/Camera/IndiSimultaneousSimulatorGuidingCameraTest.py b/apps/prototyping/Camera/IndiSimultaneousSimulatorGuidingCameraTest.py index 8e36cd8c..893d0781 100644 --- a/apps/prototyping/Camera/IndiSimultaneousSimulatorGuidingCameraTest.py +++ b/apps/prototyping/Camera/IndiSimultaneousSimulatorGuidingCameraTest.py @@ -47,7 +47,7 @@ indi_port="7624"), focuser=dict( module="IndiFocuser", - focuser_name="Focuser Simulator", + device_name="Focuser Simulator", port="/dev/ttyUSB0", focus_range=dict( min=30000, @@ -103,7 +103,7 @@ indi_port="7624"), focuser=dict( module="IndiFocuser", - focuser_name="Focuser Simulator", + device_name="Focuser Simulator", port="/dev/ttyUSB0", focus_range=dict( min=30000, diff --git a/apps/prototyping/Mount/IndiMount10Micron.py b/apps/prototyping/Mount/IndiMount10Micron.py new file mode 100644 index 00000000..3ced7119 --- /dev/null +++ b/apps/prototyping/Mount/IndiMount10Micron.py @@ -0,0 +1,64 @@ +# Basic stuff +import logging +import random +import sys + +# Local stuff +from Mount.Indi10Micron import Indi10Micron +from Service.HostTimeService import HostTimeService + +#Astropy stuff +from astropy import units as u +from astropy.coordinates import EarthLocation +from astropy.coordinates import SkyCoord + +logging.getLogger().setLevel(logging.DEBUG) + +if __name__ == '__main__': + + # Build the Mount + mount_config = dict( + module="Indi10Micron", + mount_name="LX200 10micron", + equatorial_eod="J2000", # JNOW + indi_client=dict( + indi_host="192.168.8.202", + indi_port=7624)) + + mount = Indi10Micron(config=mount_config, + location=EarthLocation(lat=43.00 * u.deg, + lon=5.5 * u.deg, + height=550 * u.m), + serv_time=HostTimeService()) + mount.connect(connect_device=True) + # Get Pier side, not supported by simulator + ps = mount.get_pier_side() + + # Unpark if you want something useful to actually happen + mount.unpark() + print(f"Status of the mount for parking is {mount.is_parked}") + + # Do a slew and track + #JNow: 15h 20m 40s 71° 46' 13" + #J2000: 15h 20m 43s 71° 50' 02" + #AzAlt: 332° 12' 25" 61° 48' 28" + #ra = 15.9 + #dec = 9 + #c = SkyCoord(ra=ra*u.hourangle, dec=dec*u.degree, frame='icrs') + c = SkyCoord.from_name("Vega") + print("BEFORE SLEWING --------------------------") + mount.set_slew_rate('3x') + mount.slew_to_coord_and_track(c) + print("After SLEWING --------------------------") + + # Check coordinates + c_true = mount.get_current_coordinates() + print(f"Coordinates are now: ra:{c_true.ra.to(u.hourangle)}, dec:{c_true.dec.to(u.degree)}") + print(f"Should be: ra:{c.ra.to(u.hourangle)}, dec:{c.dec.to(u.degree)}") + + # Set tracking + mount.set_track_mode(track_mode='TRACK_SIDEREAL') + + # Park before standby + mount.park() + diff --git a/apps/prototyping/Observatory/AggregatedCustomScopeController.py b/apps/prototyping/Observatory/AggregatedCustomScopeController.py index f592cdfa..553162d9 100644 --- a/apps/prototyping/Observatory/AggregatedCustomScopeController.py +++ b/apps/prototyping/Observatory/AggregatedCustomScopeController.py @@ -1,19 +1,13 @@ # Basic stuff import logging -import time # Local stuff : IndiClient #from helper.IndiClient import IndiClient # Local stuff : Mount -from Observatory.AggregatedCustomScopeController import UPBV2 -from Observatory.AggregatedCustomScopeController import ArduinoServoController -from Observatory.AggregatedCustomScopeController import AggregatedCustomScopeController - +from Focuser.AggregatedCustomScopeControllerUPBv2 import UPBV2 #Astropy stuff -from astropy import units as u -from astropy.coordinates import SkyCoord logging.getLogger().setLevel(logging.DEBUG) if __name__ == '__main__': @@ -26,7 +20,7 @@ baud_rate="9600", polling_ms="1000", dustcap_travel_delay_s="10", - adjustable_voltage_value="5", + adjustable_voltage_value=5, power_labels= dict( POWER_LABEL_1="MAIN_TELESCOPE_DUSTCAP_CONTROL", POWER_LABEL_2="SPOX_AND_DUSTCAP_POWER", @@ -59,7 +53,7 @@ PRIMARY_FAN="False", SECONDARY_DEW_HEATER="True", FINDER_DEW_HEATER="True"), - auto_dew_aggressivity="150 # Number between 50 and 250", + auto_dew_aggressivity=150 # Number between 50 and 250", indi_client= dict( indi_host="localhost", indi_port="7625") diff --git a/apps/prototyping/Observatory/AggregatedCustomScopeControllerFocusing.py b/apps/prototyping/Observatory/AggregatedCustomScopeControllerFocusing.py new file mode 100644 index 00000000..d26bebf4 --- /dev/null +++ b/apps/prototyping/Observatory/AggregatedCustomScopeControllerFocusing.py @@ -0,0 +1,137 @@ +# Basic stuff +import logging + +# Local stuff : IndiClient +#from helper.IndiClient import IndiClient + +# Local stuff : Mount +from Focuser.AggregatedCustomScopeControllerUPBv2 import UPBV2 + +#Astropy stuff +logging.getLogger().setLevel(logging.DEBUG) + +if __name__ == '__main__': + + # TEST UPBV2 first + config_upbv2= dict( + device_name="Pegasus UPB", + device_port="/dev/serial/by-id/usb-Pegasus_Astro_UPBv2_revD_UPB25S4VWV-if00-port0", + connection_type="CONNECTION_SERIAL", + baud_rate="9600", + polling_ms="1000", + dustcap_travel_delay_s="10", + adjustable_voltage_value=5, + power_labels= dict( + POWER_LABEL_1="MAIN_TELESCOPE_DUSTCAP_CONTROL", + POWER_LABEL_2="SPOX_DUSTCAP_FINDERCAM", + POWER_LABEL_3="MAIN_CAMERA_POWER", + POWER_LABEL_4="PPBA_POWER"), + always_on_power_identifiers= dict( + MAIN_TELESCOPE_DUSTCAP_CONTROL="True", + SPOX_DUSTCAP_FINDERCAM="False", + MAIN_CAMERA_POWER="False", + PPBA_POWER="False"), + usb_labels= dict( + USB_LABEL_1="FIELD_CAMERA", + USB_LABEL_2="PRIMARY_CAMERA", + USB_LABEL_3="SPECTRO_CONTROL_BOX", + USB_LABEL_4="ARDUINO_CONTROL_BOX", + USB_LABEL_5="WIFI_ROUTER", + USB_LABEL_6="GUIDE_CAMERA"), + always_on_usb_identifiers= dict( + FIELD_CAMERA="False", + PRIMARY_CAMERA="False", + SPECTRO_CONTROL_BOX="False", + ARDUINO_CONTROL_BOX="False", + WIFI_ROUTER="True,", + GUIDE_CAMERA="False"), + dew_labels= dict( + DEW_LABEL_1="PRIMARY_FAN", + DEW_LABEL_2="SECONDARY_DEW_HEATER", + DEW_LABEL_3="FINDER_DEW_HEATER"), + auto_dew_identifiers= dict( + PRIMARY_FAN="False", + SECONDARY_DEW_HEATER="True", + FINDER_DEW_HEATER="True"), + auto_dew_aggressivity=150, # Number between 50 and 250 + focus_range=dict( + min=25000, + max=50000), + autofocus_step=dict( + coarse=2500, + fine=1000), + autofocus_range=dict( + coarse=25000, + fine=10000), + indi_client= dict( + indi_host="192.168.8.202", + indi_port="7625") + ) + + # Now test UPBV2 + upbv2 = UPBV2( + config=config_upbv2, + connect_on_create=True) + print(upbv2.get_power_info()) + print(upbv2.get_weather_info()) + + getval = lambda: getval() + upbv2.power_on_arduino_control_box() + print(f'2#################################### {getval()}') + upbv2.switch_on_arduino_control_box_usb() + print(f'3#################################### {getval()}') + print(f'3.5#################################### {getval()}') + # Power acquisition instruments: this is a very specific case, see https://github.com/indilib/indi-3rdparty/issues/822 + print(f'4#################################### {getval()}') + # Power mount + print(f'5#################################### {getval()}') + + # # Now test Arduino controller + # upbv2.open_scope_dustcap() + # + # # config for simple arduino + # config_arduino = dict( + # device_name="Arduino", + # device_port="/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AB0L9VL1-if00-port0", + # connection_type="CONNECTION_SERIAL", + # baud_rate=57600, + # polling_ms=1000, + # indi_client=dict(indi_host="localhost", + # indi_port=7625)) + # # One need to enable usb power for arduino controller beforehand + # upbv2.power_on_arduino_control_box() + # indi_driver_connect_delay_s = 10 + # time.sleep(indi_driver_connect_delay_s) + # arduino = ArduinoServoController( + # config=config_arduino, + # connect_on_create=True) + # + # arduino.open_finder_dustcap() + # arduino.close_finder_dustcap() + # + # config_aggregated = dict( + # config_upbv2=config_upbv2, + # config_arduino=config_arduino, + # indi_driver_connect_delay_s=10, + # indi_resetable_instruments_driver_name_list=dict( + # driver_1="ZWO CCD", + # driver_2="Altair", + # driver_3="Shelyak SPOX", + # driver_4="PlayerOne CCD", + # driver_5="Arduino telescope controller" + # ), + # indi_mount_driver_name="Losmandy Gemini", + # indi_webserver_host="localhost", + # indi_webserver_port="8624", ) + # + # aggregated = AggregatedCustomScopeController( + # config=config_aggregated, + # connect_on_create=True) + # aggregated.switch_on_instruments() + # aggregated.open() + # aggregated.close() + # aggregated.switch_off_instruments() + # aggregated.deinitialize() + # aggregated.initialize() + # aggregated.open() + # aggregated.close() \ No newline at end of file diff --git a/apps/prototyping/Spectro/test_IndiSpectroController.py b/apps/prototyping/Spectro/test_IndiSpectroController.py index 42dd7f2b..6c7ca7d0 100644 --- a/apps/prototyping/Spectro/test_IndiSpectroController.py +++ b/apps/prototyping/Spectro/test_IndiSpectroController.py @@ -4,18 +4,19 @@ config = dict( module="IndiSpectroController", - device_name="Shelyak Spox", + device_name="Shelyak SPOX", port="/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AD0JE0ID-if00-port0", indi_client=dict( - indi_host="localhost", - indi_port="7625" + indi_host="192.168.0.194", + indi_port="7624" )) +time.sleep(5) sc = IndiSpectroController(config=config) sc.close_optical_path_for_dark() time.sleep(5) sc.switch_on_flat_light() time.sleep(5) -sc.switch_off_spectro_light() +sc.switch_on_spectro_light() time.sleep(5) sc.open_optical_path() time.sleep(5) \ No newline at end of file diff --git a/calibration/SpectralCalibration.py b/calibration/SpectralCalibration.py index 5b3e7040..af66f705 100644 --- a/calibration/SpectralCalibration.py +++ b/calibration/SpectralCalibration.py @@ -39,6 +39,8 @@ def __init__(self, self.flat_offset = config["flat"]["offset"] self.flat_temperature = config["flat"]["temperature"] self.dark_nb = config["dark"]["dark_nb"] + self.offset_exp_sec = config["offset"]["sec"] * u.second + self.offset_calib_nb = config["offset"]["nb"] # If controller is specified in the config, load try: @@ -63,7 +65,9 @@ def calibrate(self, observed_list): event_flat = self.take_flat(observed_list) event_spectral = self.take_spectral_calib(observed_list, event=event_flat) event_dark = self.take_dark(observed_list, event=event_spectral) - return event_dark + event_offset = self.take_offset(observed_list, event=event_dark) + event_park = self.async_park(event=event_offset) + return event_park def take_flat(self, observed_list, event=None): if event: @@ -132,4 +136,66 @@ def take_dark(self, observed_list, event=None): observations=observed_list.values()) event.wait() self.controller.open_optical_path() - return event \ No newline at end of file + return event + + def take_offset(self, observed_list, event=None): + """ + Temperature is the "most expensive" parameter to change, hence we will use this as our primary key + :param observed_list: + :return: + """ + if event: + event.wait() + offset_config_dict = dict() + for seq_time, observation in observed_list.items(): + temp_deg = observation.configuration['temperature'] + conf = (observation.configuration['gain'], + observation.configuration['offset']) + if temp_deg in offset_config_dict: + offset_config_dict[temp_deg].add(conf) + else: + offset_config_dict[temp_deg] = set((conf,)) + + self.controller.close_optical_path_for_dark() + for temp_deg, gains_offsets in offset_config_dict.items(): + if temp_deg: + self.camera.set_temperature(temp_deg) + for (gain, offset) in gains_offsets: + for i in range(self.offset_calib_nb): + event = self.camera.take_calibration( + temperature=temp_deg, + gain=gain, + offset=offset, + exp_time=self.offset_exp_sec, + headers={}, + calibration_name="offset", + observations=observed_list.values()) + event.wait() + self.controller.open_optical_path() + return event + + def async_park(self, event=None, timeout_s=300): + """ + Temperature is the "most expensive" parameter to change, hence we will use this as our primary key + :param event: + :param timeout_s: + :return: + """ + park_event = Event() + def subroutine(): + if event: + event.wait() + self.camera.park() + self.controller.open_optical_path() + park_event.set() + + try: + # There's one local event for each subroutine that might never be set ontime by the thread + # and another event + thread = Thread(target=subroutine) + thread.start() + except Exception as e: + self.logger.error(f"There has been an error while trying to park with routine {subroutine}:{e}") + park_event.set() + + return park_event diff --git a/conf_files/config.yaml b/conf_files/config.yaml index 6f53f9c3..750c8a4b 100644 --- a/conf_files/config.yaml +++ b/conf_files/config.yaml @@ -21,6 +21,61 @@ observatory: gmt_offset: 60 # Minutes # webcam: # rtsp_url: rtsp://user:password@192.168.0.16 + scope_controller_bak: + module: AggregatedCustomScopeControllerUPBv2 + device_name: Pegasus UPB + device_port: /dev/serial/by-id/usb-Pegasus_Astro_UPBv2_revD_UPB25S4VWV-if00-port0 + connection_type: CONNECTION_SERIAL + baud_rate: 9600 + polling_ms: 1000 + dustcap_travel_delay_s: 10 + adjustable_voltage_value: 5 + power_labels: + POWER_LABEL_1: MAIN_TELESCOPE_DUSTCAP_CONTROL + POWER_LABEL_2: SPOX_DUSTCAP_FINDERCAM_POWER + POWER_LABEL_3: SCI_CAMERA_FOCUS_POWER + POWER_LABEL_4: SECONDARY_SETUP_POWER + always_on_power_identifiers: + MAIN_TELESCOPE_DUSTCAP_CONTROL: True # to hold dustocover closed + SPOX_DUSTCAP_FIELDCAM_POWER: True + SCI_CAMERA_FOCUS_POWER: True + SECONDARY_SETUP_POWER: True + usb_labels: + USB_LABEL_1: FIELD_CAMERA + USB_LABEL_2: PRIMARY_CAMERA + USB_LABEL_3: SPECTRO_CONTROL_BOX + USB_LABEL_4: ARDUINO_CONTROL_BOX + USB_LABEL_5: WIFI_ROUTER + USB_LABEL_6: GUIDE_CAMERA + always_on_usb_identifiers: + FIELD_CAMERA: True + PRIMARY_CAMERA: True + SPECTRO_CONTROL_BOX: True + ARDUINO_CONTROL_BOX: True + WIFI_ROUTER: True + GUIDE_CAMERA: True + dew_labels: + DEW_LABEL_1: PRIMARY_FAN + DEW_LABEL_2: SECONDARY_DEW_HEATER + DEW_LABEL_3: FINDER_DEW_HEATER + auto_dew_identifiers: + PRIMARY_FAN: False + SECONDARY_DEW_HEATER: True + FINDER_DEW_HEATER: True + auto_dew_aggressivity: 150 # Number between 50 and 250 + default_focus: 8000 + focus_range: + min: 7500 + max: 9000 + autofocus_step: + coarse: 200 + fine: 50 + autofocus_range: + coarse: 2500 + fine: 1500 + indi_client: + indi_host: 192.168.0.194 + indi_port: 7624 scope_controller: module: DummyScopeController #IndiArduiScopeController port: /dev/ttyACM0 @@ -37,7 +92,7 @@ observatory: dome_movement_timeout_s: 300 dome_name: Dome Simulator indi_client : - indi_host : localhost + indi_host : 192.168.0.194 indi_port : 7624 use_unique_client: True directories: @@ -75,177 +130,127 @@ calibration: temperature: -10 dark: dark_nb: 2 + offset: + sec: 0.00001 + nb: 15 controller: - module: DummySpectroController - device_name: dummy - #module: IndiSpectroController - #device_name: spox +# module: DummySpectroController +# device_name: dummy + module: IndiSpectroController + device_name: Shelyak SPOX + port: /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AD0JE0ID-if00-port0 + indi_client: + indi_host: 192.168.0.194 + indi_port: 7624 mount: - #module: IndiG11 - #mount_name: Losmandy Gemini - module: IndiAbstractMountSimulator - mount_name : Telescope Simulator + module: Indi10Micron + mount_name: LX200 10micron + #module: IndiAbstractMountSimulator + #mount_name : Telescope Simulator equatorial_eod: J2000 # JNOW - is_simulator: true + is_simulator: false simulator_settings: switches: MOUNT_TYPE: ['EQ_GEM'] SIM_PIER_SIDE: ['PS_ON'] indi_client : - indi_host : localhost + indi_host : 192.168.0.194 indi_port : 7624 use_unique_client: True cameras: - - module: IndiAbstractCameraSimulatorNonCoolNonOffset - camera_name : Guide Simulator + module: IndiASICameraNonCool + camera_name : ZWO CCD ASI290MM Mini do_acquisition: false - SIMULATOR_SETTINGS: # 290MM - SIM_XRES: 1000 #1936 - SIM_YRES: 1000 #1096 - SIM_XSIZE: 9 #2.9 - SIM_YSIZE: 9 #2.9 - SIM_MAXVAL: 65000 - SIM_SATURATION: 1 - SIM_LIMITINGMAG: 17 - SIM_NOISE: 0 - SIM_SKYGLOW: 19.5 - SIM_OAGOFFSET: 0 - SIM_POLAR: 0 - SIM_POLARDRIFT: 0 - SIM_ROTATION: 0 - SIM_KING_GAMMA: 0 - SIM_KING_THETA: 0 - # SIM_PEPERIOD: 0 - # SIM_PEMAX: 0 - SIM_TIME_FACTOR: 1 SCOPE_INFO: # 200/800 newtonian FOCAL_LENGTH: 800 APERTURE: 200 subsample_astrometry: 1 + sampling_arcsec: 0.89 do_guiding: true - do_pointing: true + do_pointing: false pointing_seconds: 5 default_exp_time_sec: 5 - default_gain: 50 - default_offset: 30 + default_gain: 300 + default_offset: 10 do_adjust_pointing: true - adjust_center_x: 580 - adjust_center_y: 400 + adjust_center_x: 952 + adjust_center_y: 660 adjust_roi_search_size: 50 adjust_pointing_seconds: 5 do_autofocus: true # true - autofocus_seconds: 4 - autofocus_roi_size : 750 + autofocus_seconds: 5 + autofocus_roi_size : autofocus_merit_function: half_flux_radius #vollath_F4 focuser: - module: IndiFocuser - focuser_name: Focuser Simulator - port: /dev/ttyUSB0 + module: IndiBaaderSteelDrive2Focuser + device_name: Baader SteelDriveII + device_port: /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AB0KCL5O-if00-port0 + connection_type: CONNECTION_SERIAL + baud_rate: 19200 + polling_ms: 1000 + home_position: 2000 + default_focus: 7150 focus_range: - min: 32500 - max: 40000 + min: 6750 + max: 76000 autofocus_step: - fine: 750 - coarse: 2500 + coarse: 50 + fine: 10 autofocus_range: - fine: 10000 - coarse: 30000 + coarse: 500 + fine: 150 indi_client: - indi_host: localhost + indi_host: 192.168.0.194 indi_port: 7624 - use_unique_client: True - indi_client : - indi_host : localhost - indi_port : 7624 - use_unique_client: True #This doesn't seems to work + indi_client: + indi_host: 192.168.0.194 + indi_port: 7624 - - module: IndiAbstractCameraSimulator - camera_name : CCD Simulator + module: IndiPlayerOneCamera + camera_name : PlayerOne CCD Ares-M PRO do_acquisition: true - working_temperature: -12 - SIMULATOR_SETTINGS: # 180mm - SIM_XRES: 1500 - SIM_YRES: 1500 - SIM_XSIZE: 4.8 - SIM_YSIZE: 4.8 - SIM_MAXVAL: 65000 - SIM_SATURATION: 1 - SIM_LIMITINGMAG: 17 - SIM_NOISE: 0 - SIM_SKYGLOW: 19.5 - SIM_OAGOFFSET: 0 - SIM_POLAR: 0 - SIM_POLARDRIFT: 0 - SIM_ROTATION: 0 - # SIM_KING_GAMMA: 0 - # SIM_KING_THETA: 0 - SIM_PEPERIOD: 0 - SIM_PEMAX: 0 - SIM_TIME_FACTOR: 1 + working_temperature: -10 SCOPE_INFO: # 200/800 newtonian FOCAL_LENGTH: 800 APERTURE: 200 subsample_astrometry: 2 + sampling_arcsec: 0.97 do_guiding: false do_pointing: false pointing_seconds: 5 default_exp_time_sec: 5 - default_gain: 50 - default_offset: 30 - do_adjust_pointing: false - adjust_center_x: 400 - adjust_center_y: 400 - adjust_roi_search_size: 50 - adjust_pointing_seconds: 5 - do_autofocus: false - autofocus_seconds: 5 - autofocus_roi_size : 750 - autofocus_merit_function : half_flux_radius #vollath_F4 - focuser: - module: IndiFocuser - focuser_name: Focuser Simulator - port: /dev/ttyUSB0 - focus_range: - min: 32500 - max: 40000 - autofocus_step: - fine: 750 - coarse: 2500 - autofocus_range: - fine: 10000 - coarse: 30000 - indi_client: - indi_host: localhost - indi_port: 7624 - use_unique_client: True - do_filter_wheel: false - filter_wheel: - module: IndiFilterWheel - filterwheel_name: Filter Simulator - filter_list: - Luminance: 1 - Red: 2 - Green: 3 - Blue: 4 - H_Alpha: 5 - OIII: 6 - SII: 7 - LPR: 8 - indi_client: - indi_host: localhost - indi_port: 7624 - use_unique_client: True + default_gain: 125 + default_offset: 5 indi_client : - indi_host : localhost + indi_host: 192.168.0.194 indi_port : 7624 use_unique_client: True + - module: IndiASICamera + camera_name: ZWO CCD ASI533MM Pro + do_acquisition: false + working_temperature: -10 + SCOPE_INFO: # 200/800 newtonian + FOCAL_LENGTH: 800 + APERTURE: 200 + subsample_astrometry: 2 + sampling_arcsec: 2.9 + do_guiding: false + do_pointing: true + pointing_seconds: 4 + default_exp_time_sec: 5 + default_gain: 105 + default_offset: 10 + do_autofocus: false + indi_client: + indi_host: 192.168.0.194 + indi_port: 7624 pointer: module: DifferentialPointer #DifferentialPointer #IterativeSync gen_hips: False timeout_seconds: 450 max_iterations: 5 - max_pointing_error_seconds: 2 + max_pointing_error_seconds: 10 offset_pointer: module: InvisibleObjectOffsetPointer #StarOffsetPointer #NoOffsetPointer #InvisibleObjectOffsetPointer timeout_seconds: 300 @@ -257,10 +262,10 @@ offset_pointer: max_pointing_error_seconds: 2 guider: module : GuiderPHD2 - host : 127.0.0.1 + host : localhost port : 4400 do_calibration : False - profile_name : Simulator + profile_name : spectro_br_real exposure_time_sec : 2 settle : pixels : 3 @@ -285,7 +290,7 @@ weather_service : MAX_WEATHER_WIND_GUST_KPH : 30 MAX_WEATHER_CLOUD_COVER : 5 indi_client : - indi_host : localhost + indi_host : 192.168.0.194 indi_port : 7624 use_unique_client: True #vizualization_service: diff --git a/conf_files/config_backyard.yaml b/conf_files/config_backyard.yaml index 73936c3e..d56d5719 100644 --- a/conf_files/config_backyard.yaml +++ b/conf_files/config_backyard.yaml @@ -205,7 +205,7 @@ cameras: autofocus_merit_function : half_flux_radius #vollath_F4 #focuser: # module: IndiFocuser - # focuser_name: Focuser Simulator + # device_name: Focuser Simulator # port: /dev/ttyUSB0 # focus_range: # min: 32500 diff --git a/conf_files/indi_driver_conf/Pegasus UPB_config.xml b/conf_files/indi_driver_conf/Pegasus UPB_config.xml index de35a86b..b3630aa6 100644 --- a/conf_files/indi_driver_conf/Pegasus UPB_config.xml +++ b/conf_files/indi_driver_conf/Pegasus UPB_config.xml @@ -181,13 +181,13 @@ MAIN_TELESCOPE_DUSTCAP_CONTROL - SPOX_N_DUSTCAP + SPOX_DUSTCAP_FINDERCAM MAIN_CAMERA_POWER - MOUNT_POWER + PPBA_POWER diff --git a/conf_files/spectral_targets.yaml b/conf_files/spectral_targets.yaml index 925048ae..9192a73e 100644 --- a/conf_files/spectral_targets.yaml +++ b/conf_files/spectral_targets.yaml @@ -3,81 +3,74 @@ constraints : maxairmass : 17 #for testing, normally use 2 minmoonseparationdeg : 2 # normally put 45 targets : - "Altair": + "10 lacerta": priority : 0 - count : 1 - temperature : 10 - gain: 150 - offset: 30 - exp_time_sec : 20 - "Capella": - priority: 0 - count: 1 - temperature: 15 - gain: 150 - offset: 30 - exp_time_sec: 20 - "10 Lacertae": - priority: 0 - count: 1 - temperature: 15 - gain: 150 - offset: 30 - exp_time_sec: 20 - "Gamma Cassiopeiae" : + count : 10 + temperature : -10 + gain: 125 + offset: 10 + exp_time_sec : 30 +# "22h37m3.645s +34d25m7.96s": +# priority: 0 +# count: 20 +# temperature: -10 +# gain: 125 +# offset: 10 +# exp_time_sec: 300 + "alpha CrB": priority : 0 - count : 2 - temperature : 10 - gain: 150 - offset: 30 - exp_time_sec : 5 - "T CrB" : + count : 10 + temperature : -10 + gain: 125 + offset: 10 + exp_time_sec : 30 + "mu Her": priority : 0 - count : 1 - temperature : 10 - gain: 150 - offset: 30 - exp_time_sec : 20 - "V375 Lac" : + count : 10 + temperature : -10 + gain: 125 + offset: 10 + exp_time_sec : 30 +# "30 Her": +# priority : 0 +# count : 9 +# temperature : -10 +# gain: 125 +# offset: 10 +# exp_time_sec : 300 +# "Beta Lyr": +# priority : 0 +# count : 10 +# temperature : -10 +# gain: 125 +# offset: 10 +# exp_time_sec : 30 +# "P Cyg": +# priority : 0 +# count : 9 +# temperature : -10 +# gain: 125 +# offset: 10 +# exp_time_sec : 300 + "V Cyg": priority : 0 - count : 2 - temperature : 10 - gain: 150 - offset: 30 - exp_time_sec : 5 - "GK Per": - priority: 0 - count: 2 - temperature: 15 - gain: 150 - offset: 30 - exp_time_sec: 5 - "Alkaid": - priority: 0 - count: 2 - temperature: 15 - gain: 150 - offset: 30 - exp_time_sec: 5 - "Mizar": - priority: 0 - count: 2 - temperature: 15 - gain: 150 - offset: 30 - exp_time_sec: 5 - "Alioth": - priority: 0 - count: 2 - temperature: 15 - gain: 150 - offset: 30 - exp_time_sec: 5 + count : 9 + temperature : -10 + gain: 125 + offset: 10 + exp_time_sec : 300 + "M57": + priority : 0 + count : 9 + temperature : -10 + gain: 125 + offset: 10 + exp_time_sec : 300 reference_observation: - count : 10 - temperature : 10 - gain : 150 - offset: 30 - exp_time_sec : 20 + count : 5 + temperature : -10 + gain: 125 + offset: 10 + exp_time_sec : 30 diff --git a/helper/IndiDevice.py b/helper/IndiDevice.py index e67e590e..b515f3b2 100644 --- a/helper/IndiDevice.py +++ b/helper/IndiDevice.py @@ -116,6 +116,8 @@ def __init__(self, device_name, indi_client_config, indi_driver_name=None, debug @property def is_connected(self): + if self.device is None: + return False return self.device.isConnected() # indi_client.isServerConnected() # indi_client.disconnectServer() @@ -690,7 +692,16 @@ def start_indi_driver(self): self.indi_client.indi_webmanager_client.start_driver( driver_name=self.indi_driver_name, check_started=True) -# + + def stop_indi_driver(self): + self.indi_client.indi_webmanager_client.stop_driver( + driver_name=self.indi_driver_name) + + def restart_indi_driver(self): + self.indi_client.indi_webmanager_client.restart_driver( + driver_name=self.indi_driver_name) + + # # def get_switch(self, name): # return self.get_vector_dict(name) # @@ -743,7 +754,7 @@ def wait_for_incoming_blob_vector(self, blob_vector_name=None, timeout=None): f"size={blob.size}, queue size: {self.blob_listener.queue.qsize()} (isEmpty: {self.blob_listener.queue.empty()})") self.blob_queue.append(blob) except queue.Empty: - raise BLOBError(f"Timeout while waiting for BLOB on {self.device.name}") + raise BLOBError(f"Timeout while waiting for BLOB on {self.device_name}") def get_last_incoming_blob_vector(self): blob = self.blob_queue.pop() # deque Append + pop = LIFO diff --git a/infrastructure/run_install_script.sh b/infrastructure/run_install_script.sh index 8b985d0c..9bee87b1 100644 --- a/infrastructure/run_install_script.sh +++ b/infrastructure/run_install_script.sh @@ -121,6 +121,8 @@ su - ${username} -c "BASH_ENV=$userhome/.nibashrc bash -c 'pip3 install -r ${req #echo 'SUBSYSTEM=="vchiq",GROUP="video",MODE="0660"' > /etc/udev/rules.d/10-vchiq-permissions.rules #chmod a+x ${parent_path}/telegraf/scripts/*.sh #chmod o+x /bin/vcgencmd +# Conflicts with some devices for some input keyboard we do not use +rm /usr/lib/udev/rules.d/85-brltty.rules # Enable serial access usermod -a -G dialout $username diff --git a/requirements.txt b/requirements.txt index 59432b2c..8c76051d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,12 +17,13 @@ pyaml==20.4.0 pydantic==2.2.1 pydantic-core==2.6.1 #pygraphviz==1.6 +#pyindi-client==2.1.9 PyLaTeX==1.4.2 pymongo==3.11.3 paho-mqtt==1.6.1 #pyserial==3.4 pytz==2021.3 -pyzmq==25.1.0 +pyzmq==27.0.0 requests==2.31.0 rinohtype==0.3.1 sep==1.2.1 @@ -32,4 +33,4 @@ Sphinx==1.8.1 sqlmodel==0.0.22 transitions==0.7.1 tzwhere==3.0.3 -watchdog==4.0.1 \ No newline at end of file +watchdog==4.0.1 diff --git a/tests/Camera/test_IndiCameraFocusing.py b/tests/Camera/test_IndiCameraFocusing.py index 7191aee6..96c5c944 100644 --- a/tests/Camera/test_IndiCameraFocusing.py +++ b/tests/Camera/test_IndiCameraFocusing.py @@ -58,7 +58,7 @@ def test_indiSimulatorCamera(): indi_port="7624"), focuser=dict( module="IndiFocuser", - focuser_name="Focuser Simulator", + device_name="Focuser Simulator", port="/dev/ttyUSB0", focus_range=dict( min=30000, diff --git a/tests/Camera/test_IndiCameraFocusingMixed.py b/tests/Camera/test_IndiCameraFocusingMixed.py index 49058ac3..1ddfeb46 100644 --- a/tests/Camera/test_IndiCameraFocusingMixed.py +++ b/tests/Camera/test_IndiCameraFocusingMixed.py @@ -67,7 +67,7 @@ def test_indiSimulatorCamera(): indi_port="7624"), focuser=dict( module="IndiFocuser", - focuser_name="Focuser Simulator", + device_name="Focuser Simulator", port="/dev/ttyUSB0", focus_range=dict( min=30000, diff --git a/tests/Camera/test_IndiSimulatorCamera.py b/tests/Camera/test_IndiSimulatorCamera.py index 98e5929b..020b75cf 100644 --- a/tests/Camera/test_IndiSimulatorCamera.py +++ b/tests/Camera/test_IndiSimulatorCamera.py @@ -58,7 +58,7 @@ def test_indiSimulatorCamera(): indi_port="7624"), focuser=dict( module="IndiFocuser", - focuser_name="Focuser Simulator", + device_name="Focuser Simulator", port="/dev/ttyUSB0", focus_range=dict( min=30000, diff --git a/tests/integration_debug/test_debug.py b/tests/integration_debug/test_debug.py index 7080a3b7..4f7ac091 100644 --- a/tests/integration_debug/test_debug.py +++ b/tests/integration_debug/test_debug.py @@ -48,7 +48,7 @@ def test_annoying_camera_bug(): indi_port="7624"), focuser=dict( module="IndiFocuser", - focuser_name="Focuser Simulator", + device_name="Focuser Simulator", port="/dev/ttyUSB0", focus_range=dict( min=30000,