From 22009b31e2ec94d915b685d22212526237c879af Mon Sep 17 00:00:00 2001 From: gnthibault Date: Tue, 8 Aug 2023 11:53:48 +0200 Subject: [PATCH] WIP --- Camera/IndiASICamera.py | 10 +- Camera/IndiCamera.py | 7 +- Manager/Manager.py | 7 +- Mount/IndiG11.py | 6 + .../AggregatedCustomScopeController.py | 107 +++++++++++++----- Spectro/IndiSpectroController.py | 51 ++++++--- .../Camera/IndiASICameraDriverTest.py | 99 ++++++++++++++++ .../Spectro/test_IndiSpectroController.py | 14 ++- conf_files/config_backyard.yaml | 18 +-- conf_files/spectral_targets.yaml | 8 +- helper/IndiClient.py | 11 +- 11 files changed, 265 insertions(+), 73 deletions(-) create mode 100644 apps/prototyping/Camera/IndiASICameraDriverTest.py diff --git a/Camera/IndiASICamera.py b/Camera/IndiASICamera.py index 8fa4fa5..bcb32d3 100644 --- a/Camera/IndiASICamera.py +++ b/Camera/IndiASICamera.py @@ -20,12 +20,12 @@ def prepare_shoot(self): # Always use maximum dynamic dyn = self.get_dynamic() max_dyn = self.get_maximum_dynamic() - if dyn != max_dyn: + if dyn < max_dyn: self.logger.warning(f"Camera {self.name} using format {self.get_current_format()} with dynamic {dyn} although it " - f"is capable of {max_dyn}. Trying to set maximum bit depth") - self.set_switch("CCD_VIDEO_FORMAT", ["ASI_IMG_RAW16"]) - self.logger.info(f"Now camera {self.name} has format {self.get_current_format()} allowing for dynamic " - f"{self.get_dynamic()}") + f"is capable of {max_dyn}.") #Trying to set maximum bit depth") + #self.set_switch("CCD_VIDEO_FORMAT", ["ASI_IMG_RAW16"]) + #self.logger.info(f"Now camera {self.name} has format {self.get_current_format()} allowing for dynamic " + # f"{self.get_dynamic()}") def get_current_format(self): return [key for key, val in self.get_switch('CCD_VIDEO_FORMAT').items() if val == "On"] diff --git a/Camera/IndiCamera.py b/Camera/IndiCamera.py index 10c9b2a..c89e8d7 100644 --- a/Camera/IndiCamera.py +++ b/Camera/IndiCamera.py @@ -40,7 +40,7 @@ def __init__(self, logger=None, config=None, connect_on_create=True): camera_name='CCD Simulator', pointing_seconds=30, adjust_center_x=400, - adjust_center_y= 400, + adjust_center_y=400, adjust_roi_search_size=50, adjust_pointing_seconds=5, autofocus_seconds=5, @@ -310,10 +310,9 @@ def get_frame_type(self): def set_frame_type(self, frame_type): """ - FRAME_LIGHT Take a light fram mmmmmmmmmmmmmmmm bbb bnbn nb n b nb nb kllklke exposure + FRAME_LIGHT Take a light frame exposure FRAME_BIAS Take a bias frame exposure - m, . ,m. ,mm m,,,,,,,,,,,, m,mmmmmmmmmmmmmmmmmmmmm mmm mm m mmmm mmmmmmmmmmFRAME_DARK Take a dark frame - exposure + FRAME_DARK Take a dark frame exposure FRAME_FLAT Take a flat field frame exposure """ self.set_switch('CCD_FRAME_TYPE', [frame_type], sync=True, timeout=self.defaultTimeout) diff --git a/Manager/Manager.py b/Manager/Manager.py index 685140c..148b3f4 100644 --- a/Manager/Manager.py +++ b/Manager/Manager.py @@ -530,12 +530,12 @@ def close_observatory(self): def unpark(self): try: - # unpark the mount - self.mount.unpark() - # unpark the observatory self.observatory.unpark() + # unpark the mount + self.mount.unpark() + # Launch guider server if self.guider is not None: self.guider.launch_server() @@ -711,6 +711,7 @@ def _setup_cameras(self, **kwargs): def setup_cameras(): try: for cam_config in self.config["cameras"]: + self.logger.debug(f"Setting up camera with config {cam_config}") cam_name = cam_config['module'] cam_module = load_module('Camera.'+cam_name) cam = getattr(cam_module, cam_name)( diff --git a/Mount/IndiG11.py b/Mount/IndiG11.py index e65b045..36abb40 100644 --- a/Mount/IndiG11.py +++ b/Mount/IndiG11.py @@ -225,6 +225,12 @@ def __init__(self, location, serv_time, config): config=config, connect_on_create=False) + def unpark(self): + if not self.is_connected(): + self.initialize() + IndiAbstractMount.unpark(self) + + def initialize(self): self.connect() self.set_startup_mode(mode='WARM_RESTART') self.set_park_settings(mode='HOME') diff --git a/Observatory/AggregatedCustomScopeController.py b/Observatory/AggregatedCustomScopeController.py index 22f16d3..5f1e685 100644 --- a/Observatory/AggregatedCustomScopeController.py +++ b/Observatory/AggregatedCustomScopeController.py @@ -1,4 +1,5 @@ # Basic stuff +import json import logging import requests import time @@ -501,6 +502,14 @@ def __init__(self, config=None, connect_on_create=True): 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( @@ -542,21 +551,12 @@ def __init__(self, config=None, connect_on_create=True): if connect_on_create: self.initialize() - # 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, - } - # Finished configuring self.logger.debug('configured successfully') def initialize(self): # Restart all driver related to the aggregated devices - self.restart_all_drivers() + self.start_all_drivers() # initialize upbv2 self.upbv2.initialize() @@ -566,6 +566,9 @@ def initialize(self): time.sleep(self._indi_driver_connect_delay_s) self.arduino_servo_controller.initialize() + self.switch_on_instruments() + self.switch_on_mount() + self._is_initialized = True def deinitialize(self): @@ -575,6 +578,9 @@ def deinitialize(self): """ self.logger.debug("Deinitializing AggregatedCustomScopeController") + self.switch_off_mount() + self.switch_off_instruments() + # Deinitialize arduino servo first (as it relies on upb power) self.arduino_servo_controller.deinitialize() @@ -601,8 +607,15 @@ def close(self): self.logger.debug("Closing AggregatedCustomScopeController") self.close_finder_dustcap() self.close_scope_dustcap() + + def is_driver_started(self, driver_name): + return driver_name in self.get_running_driver_list() - def restart_driver(self, driver_name): + def get_running_driver_list(self): + running_driver_list = self.get_running_driver() + return [driver["name"] for driver in running_driver_list] + + def get_running_driver(self): """ See documentation for the API here: https://github.com/knro/indiwebmanager :param driver_name: @@ -611,30 +624,64 @@ def restart_driver(self, driver_name): try: base_url = f"http://{self._indi_webserver_host}:"\ f"{self._indi_webserver_port}" - req = f"{base_url}/api/drivers/restart/"\ - f"{urllib.parse.quote(driver_name)}" - response = requests.post(req) - self.logger.debug(f"restart_driver {driver_name} - url {req} - response: {response.text}") + req = f"{base_url}/api/server/drivers" + response = requests.get(req) + self.logger.debug(f"get_running_driver_list - url {req} - code {response.status_code} - response:{response.text}") assert response.status_code == 200 + running_driver_list = json.loads(response.text) + except json.JSONDecodeError as e: + msg = f"Cannot properly parse list of running indi driver from {response.text} : {e}" + self.logger.error(msg) + raise RuntimeError(msg) except Exception as e: - self.logger.warning(f"Cannot restart indi driver : {e}") + msg = f"Cannot get list of running indi driver : {e}" + self.logger.error(msg) + raise RuntimeError(msg) + else: + return running_driver_list - def start_driver(self, driver_name): + def restart_driver(self, driver_name): """ See documentation for the API here: https://github.com/knro/indiwebmanager :param driver_name: :return: """ + if self.is_driver_started(driver_name): + try: + base_url = f"http://{self._indi_webserver_host}:"\ + f"{self._indi_webserver_port}" + req = f"{base_url}/api/drivers/restart/"\ + f"{urllib.parse.quote(driver_name)}" + response = requests.post(req) + self.logger.debug(f"restart_driver {driver_name} - url {req} - code {response.status_code} - response:{response.text}") + assert response.status_code == 200 + except Exception as e: + msg = f"Cannot restart indi driver : {e}" + self.logger.error(msg) + raise RuntimeError(msg) + else: + self.start_driver(driver_name, check_started=False) + + def start_driver(self, driver_name, check_started=True): + """ + See documentation for the API here: https://github.com/knro/indiwebmanager + :param driver_name: + :return: + """ + if check_started and self.is_driver_started(driver_name): + return try: base_url = f"http://{self._indi_webserver_host}:"\ f"{self._indi_webserver_port}" req = f"{base_url}/api/drivers/start/"\ f"{urllib.parse.quote(driver_name)}" response = requests.post(req) - self.logger.debug(f"start_driver {driver_name} - url {req} - response: {response.text}") + self.logger.debug(f"start_driver {driver_name} - url {req} - code {response.status_code} - response:{response.text}") assert response.status_code == 200 except Exception as e: - self.logger.warning(f"Cannot start indi driver : {e}") + msg = f"Cannot start indi driver : {e}" + self.logger.error(msg) + raise RuntimeError(msg) def stop_driver(self, driver_name): """ @@ -642,6 +689,10 @@ def stop_driver(self, driver_name): :param driver_name: :return: """ + # No need to stop a driver that is not started + if not self.is_driver_started(driver_name): + self.logger.debug(f"No need to stop driver {driver_name} because it doesn't seems to be started") + return try: #if driver_name not in ["ZWO CCD"]: #"Shelyak SPOX", "Arduino telescope controller", "ASI EAF", "Altair", "ZWO CCD" # return @@ -650,16 +701,14 @@ def stop_driver(self, driver_name): req = f"{base_url}/api/drivers/stop/"\ f"{urllib.parse.quote(driver_name)}" #self.logger.setLevel("DEBUG") - #self.logger.warning(f"stop_driver {driver_name} DISABLED for now as it was randomly breaking indiserver") response = requests.post(req) - self.logger.debug(f"stop_driver {driver_name} - url {req} - response: {response.text}") - assert response.status_code in [200, 500] - if response.status_code == 500: - self.logger.debug(f"stop_driver {driver_name} - url {req} - response: {response.text}, " - f"might be expected in case the driver was not started or already stopped") + self.logger.debug(f"stop_driver {driver_name} - url {req} - code {response.status_code} - response: {response.text}") + assert response.status_code == 200 except Exception as e: - self.logger.warning(f"Cannot stop indi driver : {e}") + msg = f"Cannot stop indi driver : {e}" + self.logger.error(msg) + raise RuntimeError(msg) def switch_on_instruments(self): """ blocking call: switch on cameras, calibration tools, finderscopes, etc... @@ -682,10 +731,10 @@ def switch_off_instruments(self): for driver_name in self._indi_resetable_instruments_driver_name_list.values(): self.stop_driver(driver_name) - def restart_all_drivers(self): + def start_all_drivers(self): for driver_name in self._indi_resetable_instruments_driver_name_list.values(): - self.restart_driver(driver_name) - self.restart_driver(self._indi_mount_driver_name) + 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 in self._indi_resetable_instruments_driver_name_list.values(): diff --git a/Spectro/IndiSpectroController.py b/Spectro/IndiSpectroController.py index 2c723a7..a293cde 100644 --- a/Spectro/IndiSpectroController.py +++ b/Spectro/IndiSpectroController.py @@ -12,7 +12,22 @@ class IndiSpectroController(IndiDevice): """ - + Return of indi_getprop -p 7625 "Shelyak Spox.*.*" + Shelyak Spox.CONNECTION.CONNECT=On + Shelyak Spox.CONNECTION.DISCONNECT=Off + Shelyak Spox.DRIVER_INFO.DRIVER_NAME=Shelyak Spox + Shelyak Spox.DRIVER_INFO.DRIVER_EXEC=indi_shelyakspox_spectrograph + Shelyak Spox.DRIVER_INFO.DRIVER_VERSION=1.0 + Shelyak Spox.DRIVER_INFO.DRIVER_INTERFACE=0 + Shelyak Spox.CONFIG_PROCESS.CONFIG_LOAD=Off + Shelyak Spox.CONFIG_PROCESS.CONFIG_SAVE=Off + Shelyak Spox.CONFIG_PROCESS.CONFIG_DEFAULT=Off + Shelyak Spox.CONFIG_PROCESS.CONFIG_PURGE=Off + Shelyak Spox.DEVICE_PORT.PORT=/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AD0JE0ID-if00-port0 + Shelyak Spox.CALIBRATION.DARK=Off + Shelyak Spox.CALIBRATION.FLAT=Off + Shelyak Spox.CALIBRATION.CALIBRATION=Off + Shelyak Spox.CALIBRATION.SKY=Off """ def __init__(self, config=None, @@ -21,11 +36,11 @@ def __init__(self, if config is None: config = dict( module="IndiSpectroController", - device_name="spox", - port="/dev/ttyUSB0", + 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="7624" + indi_port="7625" )) self.port = config['port'] @@ -41,42 +56,48 @@ def __init__(self, self.logger.debug('Indi spectro controller configured successfully') def initialize(self): - self._setup_indi_client() - self.connect_client() + self.connect(connect_device=False) #TODO TN Urgent check if this is ok self.set_port() self.connect_device() + self.initialize_calibration() def set_port(self): self.set_text("DEVICE_PORT", {"PORT": self.port}) + def initialize_calibration(self): + self.open_optical_path() + def on_emergency(self): self.logger.debug("Indi spectro controller: on emergency routine started...") - self.switch_off_spectro_light() - self.switch_off_flat_light() + self.open_optical_path() self.logger.debug("Indi spectro controller: on emergency routine finished") def switch_on_spectro_light(self): - self.logger("Switching-on spectro light") + self.set_switch("CALIBRATION", ["CALIBRATION"]) + self.logger.debug("Switching-on spectro light") def switch_off_spectro_light(self): - self.logger("Switching-off spectro light") + self.logger.debug("Switching-off spectro light") self.open_optical_path() def switch_on_flat_light(self): - self.logger("Switching-on flat light") + self.set_switch("CALIBRATION", ["FLAT"]) + self.logger.debug("Switching-on flat light") def switch_off_flat_light(self): - self.logger("Switching-off flat light") + self.logger.debug("Switching-off flat light") self.open_optical_path() def close_optical_path_for_dark(self): - self.logger("Close optical path for dark") + self.set_switch("CALIBRATION", ["DARK"]) + self.logger.debug("Close optical path for dark") def open_optical_path(self): - self.logger("Open optical path") + self.set_switch("CALIBRATION", ["SKY"]) + self.logger.debug("Open optical path") def __str__(self): - return f"Spectro controller: {self.device_name}" + return f"Spectro controller: {self.device_name} with current calibration {self.get_switch('CALIBRATION')}" def __repr__(self): return self.__str__() diff --git a/apps/prototyping/Camera/IndiASICameraDriverTest.py b/apps/prototyping/Camera/IndiASICameraDriverTest.py new file mode 100644 index 0000000..edcb2a7 --- /dev/null +++ b/apps/prototyping/Camera/IndiASICameraDriverTest.py @@ -0,0 +1,99 @@ +# 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.IndiASICamera import IndiASICamera +from Service.NTPTimeService import HostTimeService + +if __name__ == '__main__': + config = dict( + camera_name='ZWO CCD ASI120MC', + 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=500, + autofocus_merit_function="half_flux_radius", + indi_client=dict( + indi_host="localhost", + indi_port=7624), + # focuser=dict( + # module="IndiFocuser", + # focuser_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"), + # ) + ) + + # Try to probe camera driver status + from helper.IndiDevice import IndiDevice + probe = IndiDevice( + device_name=config["camera_name"], + indi_client_config=config["indi_client"]) + pass + + + + + + + # test indi virtual camera class + cam = IndiASICamera(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': 960}) + # get_roi + print(f"Current camera ROI is: {cam.get_roi()}") + + # 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(0.01) + 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/Spectro/test_IndiSpectroController.py b/apps/prototyping/Spectro/test_IndiSpectroController.py index 1a03d23..42dd7f2 100644 --- a/apps/prototyping/Spectro/test_IndiSpectroController.py +++ b/apps/prototyping/Spectro/test_IndiSpectroController.py @@ -1,11 +1,21 @@ +import time + from Spectro.IndiSpectroController import IndiSpectroController config = dict( module="IndiSpectroController", - device_name="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" )) -sc = IndiSpectroController(config=config) \ No newline at end of file +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() +time.sleep(5) +sc.open_optical_path() +time.sleep(5) \ No newline at end of file diff --git a/conf_files/config_backyard.yaml b/conf_files/config_backyard.yaml index 0030a51..98b5850 100644 --- a/conf_files/config_backyard.yaml +++ b/conf_files/config_backyard.yaml @@ -22,6 +22,7 @@ observatory: # webcam: # rtsp_url: rtsp://user:password@192.168.0.16 scope_controller: + module: AggregatedCustomScopeController config_arduino: device_name: Arduino device_port: /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AB0L9VL1-if00-port0 @@ -30,7 +31,7 @@ observatory: polling_ms: 1000 indi_client: indi_host: localhost - indi_port: 7624 + indi_port: 7625 config_upbv2: device_name: Pegasus UPB device_port: /dev/serial/by-id/usb-Pegasus_Astro_UPBv2_revD_UPB25S4VWV-if00-port0 @@ -106,15 +107,15 @@ db: ntp: ntpserver: time.google.com scheduler: - module: DefaultScheduler - # module: SpectroScheduler - target_file: targets.yaml - #target_file: spectral_targets.yaml + # module: DefaultScheduler + module: SpectroScheduler + # target_file: targets.yaml + target_file: spectral_targets.yaml spectroscope_controller: module: IndiSpectroController calibration: - module: ImagingCalibration - #module: SpectralCalibration + #module: ImagingCalibration + module: SpectralCalibration spectral_calib: nb: 11 sec: 30 @@ -129,12 +130,11 @@ calibration: dark_nb: 1 controller: module: IndiSpectroController - device_name: spox + device_name: Shelyak Spox port: /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AD0JE0ID-if00-port0 indi_client : indi_host : localhost indi_port : 7625 - mount: module: IndiG11 mount_name: Losmandy Gemini diff --git a/conf_files/spectral_targets.yaml b/conf_files/spectral_targets.yaml index 9664e69..167490a 100644 --- a/conf_files/spectral_targets.yaml +++ b/conf_files/spectral_targets.yaml @@ -3,12 +3,18 @@ constraints : maxairmass : 17 #for testing, normally use 2 minmoonseparationdeg : 2 # normally put 45 targets : - "T CrB" : + "Vega" : priority : 0 count : 1 temperature : gain : 100 exp_time_sec : 20 +# "T CrB" : +# priority : 0 +# count : 1 +# temperature : +# gain : 100 +# exp_time_sec : 20 # "GK Per" : # priority : 0 # count : 2 diff --git a/helper/IndiClient.py b/helper/IndiClient.py index c0654c9..a0b227d 100644 --- a/helper/IndiClient.py +++ b/helper/IndiClient.py @@ -145,18 +145,19 @@ def connect_to_server(self, sync=True, timeout=30): if (not self.client_connecting) and (not self.running): self.client_connecting = True asyncio.run_coroutine_threadsafe(self.connect(timeout=timeout), self.ioloop) - if sync: future = asyncio.run_coroutine_threadsafe(self.wait_running(), self.ioloop) try: assert (future.result(timeout) is True) except concurrent.futures.TimeoutError: - logger.error("Setting up running state took too long...") + msg = "Setting up running state took too long..." + logger.error(msg) future.cancel() - raise RuntimeError + raise RuntimeError(msg) except Exception as exc: - logger.error(f"Error while trying to connect client: {exc!r}") - raise RuntimeError + msg = f"Error while trying to connect client: {exc!r}" + logger.error(msg) + raise RuntimeError(msg) def trigger_get_properties(self): self.xml_to_indiserver("")