From 5e9e405d606c8aff18ebd342f88e6ae60756e3d2 Mon Sep 17 00:00:00 2001 From: gnthibault Date: Fri, 11 Aug 2023 15:07:46 +0200 Subject: [PATCH] Fixes and features --- Manager/Manager.py | 38 +++- Mount/IndiG11.py | 2 +- ObservationPlanner/ObservationPlanner.py | 45 ++-- .../AggregatedCustomScopeController.py | 35 +++- Observatory/DummyScopeController.py | 3 + Observatory/IndiDomeController.py | 4 + Observatory/Observatory.py | 7 + Pointer/DifferentialPointer.py | 12 +- Pointer/InvisibleObjectOffsetPointer.py | 1 + Pointer/IterativeSync.py | 11 +- Pointer/StarOffsetPointer.py | 1 + Service/IndiWeather.py | 1 + Service/IndiWebManagerClient.py | 2 +- apps/launch_remote_observatory.py | 3 +- .../AggregatedCustomScopeController.py | 193 ++++++++++-------- conf_files/config_backyard.yaml | 49 ++--- conf_files/spectral_targets.yaml | 30 ++- helper/IndiClient.py | 4 +- helper/IndiDevice.py | 2 +- helper/client.py | 3 +- helper/device.py | 2 +- 21 files changed, 276 insertions(+), 172 deletions(-) diff --git a/Manager/Manager.py b/Manager/Manager.py index 143edb02..6b039e97 100644 --- a/Manager/Manager.py +++ b/Manager/Manager.py @@ -44,7 +44,13 @@ def __init__(self, *args, **kwargs): """ Base.__init__(self) - self.is_initialized = False + self.independant_services = None + self.is_initialized = False + self.serv_time = None + self.serv_weather = None + self.vizualization_service = None + self.webmanager_client = None + ########################################################################## # Properties ########################################################################## @@ -95,6 +101,9 @@ def initialize(self): self.logger.info('\tSetting up main image directory') self._setup_image_directory() + # Reset indi webmanager client + self._setup_indi_web_manager_client() + # setup various services self.logger.info('\tSetting up web services') self._setup_services() @@ -128,9 +137,9 @@ def initialize(self): self._setup_scheduler() # Setup vizualization service - self.logger.info('\tSetting up vizualization service') - self._setup_vizualization_service() - self.logger.info('\t Observatory initialized') + # self.logger.info('\tSetting up vizualization service') + # self._setup_vizualization_service() + # self.logger.info('\t Observatory initialized') self.is_initialized = True # self.logger.debug("Initializing mount") # self.mount.initialize() @@ -531,9 +540,23 @@ def close_observatory(self): def unpark(self): try: + # Reset indi server + #TODO TN This needs to be setup properly with splitted webmanager clients + if self.serv_weather is not None: + self.logger.debug("Waiting for weather server to stop") + self.serv_weather.stop() + if self.vizualization_service is not None: + self.logger.debug("Waiting for vizualization server to stop") + self.vizualization_service.stop() + + for _, indi_client in IndiClient._instances.items(): + #indi_client.stop() # This is working properly in debug but not in normal runtime + del indi_client + IndiClient._instances = {} + + self.observatory.reset_config() self.webmanager_client.reset_server() - self._setup_weather_service() # unpark the observatory self.observatory.power_on_all_equipments() @@ -551,6 +574,9 @@ def unpark(self): self.guider.launch_server() self.guider.connect_server() # self.guider.connect_profile() + + self._setup_weather_service() + self._setup_vizualization_service() return True except Exception as e: self.logger.error(f"Problem unparking: {e}") @@ -591,7 +617,7 @@ def _setup_services(self): setup various services that are supposed to provide infos/data """ try: - self._setup_indi_web_manager_client() + # TODO TN, I need to properly split observatory related webmanager and services related self._setup_time_service() self._setup_weather_service() self._setup_messaging() diff --git a/Mount/IndiG11.py b/Mount/IndiG11.py index 6d107d31..bcabe4e3 100644 --- a/Mount/IndiG11.py +++ b/Mount/IndiG11.py @@ -312,4 +312,4 @@ def set_park_settings(self, mode='HOME'): def set_coord(self, coord): IndiMount.set_coord(self, coord) # Wait for the mount/tube to damper vibrations - time.sleep(2) \ No newline at end of file + time.sleep(5) \ No newline at end of file diff --git a/ObservationPlanner/ObservationPlanner.py b/ObservationPlanner/ObservationPlanner.py index 7fe05b7c..8e9349cc 100644 --- a/ObservationPlanner/ObservationPlanner.py +++ b/ObservationPlanner/ObservationPlanner.py @@ -11,7 +11,7 @@ # Astropy stuff from astropy.coordinates import get_moon from astropy.coordinates import get_sun -from astropy import units as AU +from astropy import units as u from astropy.coordinates import AltAz from astropy.coordinates import EarthLocation from astropy.coordinates import SkyCoord @@ -23,8 +23,9 @@ from astroplan import ObservingBlock from astroplan.constraints import AtNightConstraint from astroplan.constraints import AirmassConstraint -from astroplan.constraints import TimeConstraint +from astroplan.constraints import AltitudeConstraint from astroplan.constraints import MoonSeparationConstraint +from astroplan.constraints import TimeConstraint from astroplan.plots import plot_sky from astroplan.scheduling import PriorityScheduler from astroplan.scheduling import SequentialScheduler @@ -121,14 +122,16 @@ def gen_constraints(self): # return [AirmassConstraint(max=3, boolean_constraint=True), AtNightConstraint.twilight_astronomical(), - MoonSeparationConstraint(min=45*AU.deg), + MoonSeparationConstraint(min=45*u.deg), LocalHorizonConstraint(horizon=self.obs.get_horizon(), - boolean_constraint=True)] + boolean_constraint=True), + AltitudeConstraint(max=self.obs.max_altitude_degree*u.deg, + boolean_constraint=True)] def gen_obs_blocks(self, constraint): #TODO TN readout time, get that info from camera - camera_time = 1*AU.second + camera_time = 1*u.second # Create ObservingBlocks for each filter and target with our time # constraint, and durations determined by the exposures needed @@ -140,7 +143,7 @@ def gen_obs_blocks(self, constraint): SkyCoord.from_name(target_name, frame="icrs"), equinox=Time('J2000'))) - #target = FixedTarget(SkyCoord(239*AU.deg, 49*AU.deg)) + #target = FixedTarget(SkyCoord(239*u.deg, 49*u.deg)) for filter_name, acq_config in config.items(): # We split big observing blocks into smaller blocks for better # granularity @@ -148,7 +151,7 @@ def gen_obs_blocks(self, constraint): while count > 0: l_count = max(1, min(count, self.MaximumSlotDurationSec//exp_time_sec)) - exp_time = exp_time_sec*AU.second + exp_time = exp_time_sec*u.second #TODO TN retrieve priority from the file priority = 0 if (filter_name=='Luminance') else 1 @@ -166,9 +169,9 @@ def gen_scheduler(self, constraint): # Initialize a transitioner object with the slew rate and/or the # duration of other transitions (e.g. filter changes) # TODO TN do that from mount or filterwheel info - slew_rate = 1.5*AU.deg/AU.second + slew_rate = 1.5*u.deg/u.second transitions = Transitioner(slew_rate, - {'filter':{'default': 10*AU.second}}) + {'filter':{'default': 10*u.second}}) # Initialize the priority scheduler with constraints and transitioner return PriorityScheduler( @@ -180,9 +183,9 @@ def gen_schedule(self, obs_blocks, scheduler, start_time=None, duration_hour=None): """ start_time can either be a precise datetime or a datetime.date """ if duration_hour is None: - duration_hour = self.tmh * 2 * AU.hour + duration_hour = self.tmh * 2 * u.hour else: - duration_hour = duration_hour * AU.hour + duration_hour = duration_hour * u.hour if start_time is None or (isinstance(start_time, datetime.date) and not isinstance(start_time, datetime.datetime)): @@ -208,9 +211,9 @@ def showObservationPlan(self, start_time=None, duration_hour=None, show_airmass=True, afig=None, pfig=None): """ start_time can either be a precise datetime or a datetime.date """ if duration_hour is None: - duration_hour = self.tmh * 2 * AU.hour + duration_hour = self.tmh * 2 * u.hour else: - duration_hour = duration_hour * AU.hour + duration_hour = duration_hour * u.hour if start_time is None or (isinstance(start_time, datetime.date) and not isinstance(start_time, datetime.datetime)): @@ -219,13 +222,13 @@ def showObservationPlan(self, start_time=None, duration_hour=None, else: target_date = start_time midnight = self.ntpServ.get_next_local_midnight_in_utc(target_date) - d_h = float(duration_hour/AU.hour) + d_h = float(duration_hour/u.hour) start_time = midnight - datetime.timedelta(hours=d_h/2) else: target_date = start_time.date() #Time margin, in hour - tmh_range = int(np.ceil(float(duration_hour/AU.hour))) + tmh_range = int(np.ceil(float(duration_hour/u.hour))) #tmh_range = np.arange(tmh_range, dtype=float) - tmh_range//2 date_font_size = 8 resolution = 400 @@ -239,7 +242,7 @@ def showObservationPlan(self, start_time=None, duration_hour=None, #Astropy ranges (everything in UTC) start_time = Time(start_time) relative_time_frame = np.linspace(0, tmh_range, - resolution) * AU.hour + resolution) * u.hour absolute_time_frame = start_time + relative_time_frame altaz_frame = AltAz(obstime=absolute_time_frame, location=self.obs.getAstropyEarthLocation()) @@ -273,18 +276,18 @@ def showObservationPlan(self, start_time=None, duration_hour=None, color='silver', label=moon_label) #grey sky when sun lower than -0 deg - grey_sun_range = sun_altazs.alt < -0*AU.deg + grey_sun_range = sun_altazs.alt < -0*u.deg self.alt_ax.fill_between(matplotlib.dates.date2num( absolute_time_frame.to_datetime()), 0, 90, grey_sun_range, color='0.5', zorder=0) #Dark sky when sun is below -18 deg - dark_sun_range = sun_altazs.alt < -18*AU.deg + dark_sun_range = sun_altazs.alt < -18*u.deg self.alt_ax.fill_between(matplotlib.dates.date2num( absolute_time_frame.to_datetime()), 0, 90, dark_sun_range, color='0', zorder=0) #grey sky when sun is low (<18deg) but moon has risen - grey_moon_range = np.logical_and(moon_altazs.alt > -5*AU.deg, - sun_altazs.alt < -18*AU.deg) + grey_moon_range = np.logical_and(moon_altazs.alt > -5*u.deg, + sun_altazs.alt < -18*u.deg) grey_moon_intensity = 0.0025*moon_ill_perc #hence 0.25 for 100% self.alt_ax.fill_between(matplotlib.dates.date2num( absolute_time_frame.to_datetime()), 0, 90, @@ -360,7 +363,7 @@ def showObservationPlan(self, start_time=None, duration_hour=None, for (target_name, imaging_program), color in zip( self.targetList.items(), colors): target_coord = SkyCoord(SkyCoord.from_name(target_name, frame="icrs"), equinox=Time('J2000')) - #target_coord = SkyCoord(239*AU.deg, 49*AU.deg) + #target_coord = SkyCoord(239*u.deg, 49*u.deg) # Compute altazs for target target_altazs = target_coord.transform_to(altaz_frame) diff --git a/Observatory/AggregatedCustomScopeController.py b/Observatory/AggregatedCustomScopeController.py index 9192349f..eb36e5bc 100644 --- a/Observatory/AggregatedCustomScopeController.py +++ b/Observatory/AggregatedCustomScopeController.py @@ -162,6 +162,7 @@ def initialize(self): 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() @@ -300,6 +301,7 @@ 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) @@ -585,30 +587,43 @@ def __init__(self, config=None, connect_on_create=False): # Finished configuring self.logger.debug('configured successfully') - def power_on_all_equipments(self): + def reset_config(self): # initialize upbv2 self.start_upbv2_driver() - self.upbv2.initialize() + self.upbv2.initialize() # This force to put to zero power and usb usually before an indiserver reset + def power_on_all_equipments(self): + self.upbv2.connect(connect_device=True) + 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"]}') + time.sleep(self._indi_driver_connect_delay_s) + self.upbv2.switch_on_arduino_control_box_usb() + self.logger.debug(f'3#################################### {self.upbv2.get_switch("USB_PORT_CONTROL")["PORT_4"]}') + time.sleep(self._indi_driver_connect_delay_s) + 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.switch_on_acquisition_equipments_usb() + self.upbv2.switch_on_acquisition_equipments_usb() + self.logger.debug(f'4#################################### {self.upbv2.get_switch("USB_PORT_CONTROL")["PORT_4"]}') + time.sleep(self._indi_driver_connect_delay_s) # 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.power_on_acquisition_equipments() - + 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, @@ -616,9 +631,11 @@ def power_on_all_equipments(self): 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.initialize() self.is_initialized = True + exit() def power_off_all_equipments(self): """ @@ -628,10 +645,10 @@ def power_off_all_equipments(self): self.logger.debug("Deinitializing AggregatedCustomScopeController") self.switch_off_mount() - self.power_off_acquisition_equipments() + self.upbv2.power_off_acquisition_equipments() # Power acquisition instruments: this is a very specific case, see https://github.com/indilib/indi-3rdparty/issues/822 time.sleep(1) - self.switch_off_acquisition_equipments_usb() + self.upbv2.switch_off_acquisition_equipments_usb() # Deinitialize arduino servo first (as it relies on upb power) self.arduino_servo_controller.deinitialize() @@ -691,7 +708,7 @@ def get_running_driver(self): f"{self._indi_webserver_port}" req = f"{base_url}/api/server/drivers" response = requests.get(req) - self.logger.debug(f"get_running_driver - url {req} - code {response.status_code} - response:{response.text}") + # self.logger.debug(f"get_running_driver - 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: diff --git a/Observatory/DummyScopeController.py b/Observatory/DummyScopeController.py index 19347857..f980a144 100644 --- a/Observatory/DummyScopeController.py +++ b/Observatory/DummyScopeController.py @@ -50,6 +50,9 @@ def power_on_all_equipments(self): def power_off_all_equipments(self): self.logger.debug("Shutting down all equipements") + def reset_config(self): + pass + def deinitialize(self): self.logger.debug("Deinitializing DummyScopeController") diff --git a/Observatory/IndiDomeController.py b/Observatory/IndiDomeController.py index a4273e1a..be069fa5 100644 --- a/Observatory/IndiDomeController.py +++ b/Observatory/IndiDomeController.py @@ -1,5 +1,6 @@ # Basic stuff import logging +import time # Indi stuff from helper.IndiDevice import IndiDevice @@ -27,6 +28,9 @@ def __init__(self, config=None, connect_on_create=True): # Finished configuring self.logger.debug('Indi dome configured successfully') + def power_on(self): + self.connect(connect_device=True) + def initialize(self): self._is_initialized = True self.logger.debug("Initializing dome, not doing much actually") diff --git a/Observatory/Observatory.py b/Observatory/Observatory.py index 86f48332..18f1f175 100644 --- a/Observatory/Observatory.py +++ b/Observatory/Observatory.py @@ -53,6 +53,7 @@ def __init__(self, serv_weather=None, config=None): longitude=config['longitude']) self.altitude_meter = config['elevation'] self.horizon = config['horizon'] + self.max_altitude_degree = config.get("max_altitude_degree", 90) self.investigator = config['investigator'] # Now find the timezone from the gps coordinate @@ -156,8 +157,14 @@ def unpark(self): self.logger.debug('Observatory: unparking') self.open_everything() + def reset_config(self): + if self.has_scope: + self.scope_controller.reset_config() + def power_on_all_equipments(self): self.logger.debug('Observatory: powering all scope equipments') + if self.has_dome: + self.dome_controller.power_on() if self.has_scope: self.scope_controller.power_on_all_equipments() diff --git a/Pointer/DifferentialPointer.py b/Pointer/DifferentialPointer.py index 710d8689..2f92fc1f 100644 --- a/Pointer/DifferentialPointer.py +++ b/Pointer/DifferentialPointer.py @@ -86,10 +86,14 @@ def points_async(self, mount, camera, observation, fits_headers, pointing_event, observation.last_pointing) pointing_image = Image(pointing_path) - pointing_image.solve_field(verbose=True, - gen_hips=self.gen_hips, - sampling_arcsec=camera.sampling_arcsec, - downsample=camera.subsample_astrometry) + solve_params = dict( + verbose=True, + gen_hips=self.gen_hips, + sampling_arcsec=camera.sampling_arcsec, + downsample=camera.subsample_astrometry) + if img_num > 0: + solve_params["use_header_position"] = True + pointing_image.solve_field(**solve_params) observation.pointing_image = pointing_image self.logger.debug(f"Pointing file: {pointing_image}") pointing_error = pointing_image.pointing_error() diff --git a/Pointer/InvisibleObjectOffsetPointer.py b/Pointer/InvisibleObjectOffsetPointer.py index 5cef7d54..d9b2cfb8 100644 --- a/Pointer/InvisibleObjectOffsetPointer.py +++ b/Pointer/InvisibleObjectOffsetPointer.py @@ -92,6 +92,7 @@ def offset_points_async(self, mount, camera, guiding_camera, guider, observation gen_hips=False, remove_extras=False, skip_solved=False, + use_header_position=True, sampling_arcsec=camera.sampling_arcsec) # update mount with the actual position diff --git a/Pointer/IterativeSync.py b/Pointer/IterativeSync.py index 350e4e58..06e87729 100644 --- a/Pointer/IterativeSync.py +++ b/Pointer/IterativeSync.py @@ -87,9 +87,14 @@ def points_async(self, mount, camera, observation, fits_headers, pointing_event, observation.last_pointing) pointing_image = Image(pointing_path) - pointing_image.solve_field(verbose=True, - gen_hips=self.gen_hips, - sampling_arcsec=camera.sampling_arcsec) + solve_params = dict( + verbose=True, + gen_hips=self.gen_hips, + sampling_arcsec=camera.sampling_arcsec, + downsample=camera.subsample_astrometry) + if img_num > 0: + solve_params["use_header_position"] = True + pointing_image.solve_field(**solve_params) observation.pointing_image = pointing_image self.logger.debug(f"Pointing file: {pointing_image}") pointing_error = pointing_image.pointing_error() diff --git a/Pointer/StarOffsetPointer.py b/Pointer/StarOffsetPointer.py index c0149ded..af750b92 100644 --- a/Pointer/StarOffsetPointer.py +++ b/Pointer/StarOffsetPointer.py @@ -99,6 +99,7 @@ def offset_points_async(self, mount, camera, guiding_camera, guider, observation gen_hips=False, remove_extras=False, skip_solved=False, + use_header_position=True, sampling_arcsec=camera.sampling_arcsec) # update mount with the actual position diff --git a/Service/IndiWeather.py b/Service/IndiWeather.py index 2dae088b..0dd0cc43 100644 --- a/Service/IndiWeather.py +++ b/Service/IndiWeather.py @@ -131,6 +131,7 @@ def stop(self): Stops the web server. """ self._stop_event.set() + self.join() def stopped(self): """ diff --git a/Service/IndiWebManagerClient.py b/Service/IndiWebManagerClient.py index ebd3c752..f9f260bd 100644 --- a/Service/IndiWebManagerClient.py +++ b/Service/IndiWebManagerClient.py @@ -66,7 +66,7 @@ def stop_server(self): base_url = f"http://{self.host}:{self.port}" req = f"{base_url}/api/server/stop" response = requests.post(req) - self.logger.debug(f"start_server - url {req} - code {response.status_code} - response:{response.text}") + self.logger.debug(f"stop_server - url {req} - code {response.status_code} - response:{response.text}") assert response.status_code == 200 except Exception as e: msg = f"Cannot start server: {e}" diff --git a/apps/launch_remote_observatory.py b/apps/launch_remote_observatory.py index 67054a7c..84ea9806 100644 --- a/apps/launch_remote_observatory.py +++ b/apps/launch_remote_observatory.py @@ -373,7 +373,8 @@ def is_weather_safe(self, stale=180): else: if age > stale: self.logger.warning("Weather record looks stale, marking unsafe.") - is_safe = False + if not self.simulation_mode: + is_safe = False self._is_safe = is_safe diff --git a/apps/prototyping/Observatory/AggregatedCustomScopeController.py b/apps/prototyping/Observatory/AggregatedCustomScopeController.py index c34adf1e..f592cdfa 100644 --- a/apps/prototyping/Observatory/AggregatedCustomScopeController.py +++ b/apps/prototyping/Observatory/AggregatedCustomScopeController.py @@ -19,49 +19,51 @@ 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_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=True, - 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 - indi_client=dict(indi_host="localhost", - indi_port=7625)) + 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_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", + indi_client= dict( + indi_host="localhost", + indi_port="7625") + ) # Now test UPBV2 upbv2 = UPBV2( @@ -70,52 +72,63 @@ print(upbv2.get_power_info()) print(upbv2.get_weather_info()) - # 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 + getval = lambda: getval() 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() + 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()}') - 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 + # # 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/conf_files/config_backyard.yaml b/conf_files/config_backyard.yaml index c37deccd..61404eba 100644 --- a/conf_files/config_backyard.yaml +++ b/conf_files/config_backyard.yaml @@ -2,18 +2,19 @@ name: Remote observatory observatory: module: Observatory investigator: gnthibault - latitude: 45.67 # Degrees - longitude: 5.67 # Degrees + latitude: 43.934807501800606 # Degrees + longitude: 5.710652100000001 # Degrees elevation: 650.0 # Meters timezone: Europe/Paris #This is not mandatory, just speeds up startup # UTC+1 CET Central European Time, # UTC +2 CEST Central European Summer Time utc_offset: 1 # Hours horizon: # Degrees - 0 : 0 - 90 : 0 - 180 : 0 - 270 : 0 + 0 : 20 + 90 : 20 + 180 : 20 + 270 : 20 + max_altitude_degree: 60 twilight_horizon: -18 # Degrees timezone: Europe/Paris # Paris is in the Central European Time Zone ( CET ) is 1 hours ahead of @@ -160,11 +161,11 @@ cameras: APERTURE: 200 sampling_arcsec: 0.88 do_guiding: true - do_pointing: false + do_pointing: true pointing_seconds: 40 do_adjust_pointing: true - adjust_center_x: 894 - adjust_center_y: 653 + adjust_center_x: 877 + adjust_center_y: 637 adjust_roi_search_size: 50 adjust_pointing_seconds: 40 do_autofocus: false @@ -191,7 +192,7 @@ cameras: adjust_center_y: 400 adjust_roi_search_size: 50 adjust_pointing_seconds: 5 - do_autofocus: true + do_autofocus: false autofocus_seconds: 5 autofocus_roi_size : 750 autofocus_merit_function : half_flux_radius #vollath_F4 @@ -232,14 +233,14 @@ cameras: indi_port : 7625 - module: IndiAltairCameraNonCool camera_name: Altair AA183MPRO - do_acquisition: true + do_acquisition: false SCOPE_INFO: # 50/242 evoguide FOCAL_LENGTH: 242 APERTURE: 50 sampling_arcsec: 1.88 subsample_astrometry: 4 do_guiding: false - do_pointing: true + do_pointing: false pointing_seconds: 10 do_adjust_pointing: false adjust_center_x: 400 @@ -259,13 +260,13 @@ pointer: gen_hips: False timeout_seconds: 900 max_iterations: 10 - max_pointing_error_seconds: 5 + max_pointing_error_seconds: 10 offset_pointer: module: StarOffsetPointer #StarOffsetPointer #NoOffsetPointer timeout_seconds: 300 max_identification_error_seconds: 1 - sync_mount_upon_solve: False - use_guider_adjust: False + sync_mount_upon_solve: True + use_guider_adjust: True on_star_identification_failure: trust_astrometry # get_brightest or trust_astrometry guider: module : GuiderPHD2 @@ -273,11 +274,11 @@ guider: port : 4400 do_calibration : False profile_name : backyard - exposure_time_sec : 4 + exposure_time_sec : 2 settle : - pixels : 1.5 + pixels : 3 time : 10 - timeout : 60 + timeout : 300 dither : pixels : 0.0 ra_only : False @@ -287,11 +288,11 @@ weather_service : module : IndiWeather service_name : Weather Simulator key_path : /var/RemoteObservatory/keys.json - delay_sec : 60 + delay_sec : 10 observatory: - latitude: 43.56 # Degrees - longitude: 5.43 # Degrees - elevation: 150.0 # Meters + latitude: 43.934807501800606 # Degrees + longitude: 5.710652100000001 # Degrees + elevation: 650.0 # Meters limits : MAX_WEATHER_WIND_SPEED_KPH : 25 MAX_WEATHER_WIND_GUST_KPH : 30 @@ -305,8 +306,8 @@ vizualization_service: delay_moving_objects_s: 0.05 show_stars: False gps_coord: - latitude: 45.67 - longitude: 5.67 + latitude: 43.934807501800606 # Degrees + longitude: 5.710652100000001 # Degrees time_service : module : HostTimeService messaging_publisher: diff --git a/conf_files/spectral_targets.yaml b/conf_files/spectral_targets.yaml index e1e64879..009cebb6 100644 --- a/conf_files/spectral_targets.yaml +++ b/conf_files/spectral_targets.yaml @@ -3,13 +3,20 @@ constraints : maxairmass : 17 #for testing, normally use 2 minmoonseparationdeg : 2 # normally put 45 targets : - "Vega" : #HD214680 #HD222404 - priority : 0 - count : 1 - temperature : 15 - gain: 150 - offset: 30 - exp_time_sec : 20 +# "Altair" : #HD214680 #HD222404 +# priority : 0 +# count : 1 +# temperature : 15 +# gain: 150 +# offset: 30 +# exp_time_sec : 20 +# "10 Lacertae": #HD214680 #HD222404 +# priority: 0 +# count: 1 +# temperature: 15 +# gain: 150 +# offset: 30 +# exp_time_sec: 20 # "T CrB" : # priority : 0 # count : 1 @@ -17,13 +24,20 @@ targets : # gain: 150 # offset: 30 # exp_time_sec : 20 -# "GK Per" : +# "V375 Lac" : # priority : 0 # count : 2 # temperature : 15 # gain: 150 # offset: 30 # exp_time_sec : 5 + "GK Per": + priority: 0 + count: 2 + temperature: 15 + gain: 150 + offset: 30 + exp_time_sec: 5 reference_observation: count : 10 temperature : 15 diff --git a/helper/IndiClient.py b/helper/IndiClient.py index d800a761..3ec3b2d9 100644 --- a/helper/IndiClient.py +++ b/helper/IndiClient.py @@ -92,6 +92,8 @@ def stop(self): self.running = False # Now wait until the main connection loop (also running in ioloop) is over #self.communication_over_event.wait() + + # TODO TN THIS IS QUITE EXPERIMENTAL (runinng stop in its own loop...) remaining_tasks = asyncio.all_tasks(loop=self.ioloop) while remaining_tasks: future = asyncio.run_coroutine_threadsafe(asyncio.wait(remaining_tasks, return_when=asyncio.ALL_COMPLETED), self.ioloop) @@ -99,6 +101,7 @@ def stop(self): remaining_tasks = asyncio.all_tasks(loop=self.ioloop) # We need to force stop the ioloop that has been started with run_forever self.ioloop.call_soon_threadsafe(self.ioloop.stop) + #self.ioloop.run_until_complete(self.ioloop.shutdown_asyncgens()) # self.ioloop.close() # The thread whose only work was to run the ioloop forever should properly terminate @@ -132,7 +135,6 @@ def sync_with_predicate(self, predicate_checker, timeout=30): assert (future.result(timeout) is True) except concurrent.futures.TimeoutError: msg = f"Waiting for predicate {predicate_checker} took too long..." - logger.error(msg) future.cancel() raise IndiClientPredicateTimeoutError(msg) except Exception as exc: diff --git a/helper/IndiDevice.py b/helper/IndiDevice.py index f4fe727c..c528d5a5 100644 --- a/helper/IndiDevice.py +++ b/helper/IndiDevice.py @@ -94,7 +94,7 @@ def register_device_to_client(self): # lambda x: self.indi_client.device_subscriptions.append(x), # self.parse_xml_str) future = asyncio.run_coroutine_threadsafe(self.registering_runner(self.parse_xml_str), self.indi_client.ioloop) - _ = future.result() # This is just sync + _ = future.result() # This is just sync def unregister_device_to_client(self): self.logger.debug(f"IndiDevice: asking indi_client to stop listen for device {self.device_name}") diff --git a/helper/client.py b/helper/client.py index c5cfe98a..ba02d8ab 100644 --- a/helper/client.py +++ b/helper/client.py @@ -66,13 +66,14 @@ async def connect(self, timeout): # Send first "Introductory message" in non blocking fashion await self.to_indiQ.put("") # Now run main two asynchronous task: consume send queue, and receive - self.running = True # self.task = asyncio.gather( # self.write_to_indiserver(timeout=timeout), # self.read_from_indiserver()) # await self.task task_write = asyncio.create_task(self.write_to_indiserver(timeout=MAX_NETWORK_EXCHANCE_TIMEOUT_S)) task_read = asyncio.create_task(self.read_from_indiserver(timeout=MAX_NETWORK_EXCHANCE_TIMEOUT_S)) + self.running = True + self.client_connecting = False await asyncio.wait([task_read, task_write], return_when=asyncio.ALL_COMPLETED) logger.debug("INDI client tasks finished or indiserver crashed ?") # Stopped from the outside on purpose diff --git a/helper/device.py b/helper/device.py index 6e2e3730..4d905cca 100644 --- a/helper/device.py +++ b/helper/device.py @@ -1566,7 +1566,7 @@ def _default_blob_handler(self, blob_vector, indi): because this call is blocking inside the main ioloop, an it is the only single place where blobvector are manipulated """ - blob_vector.tell() + # blob_vector.tell() # Too verbose blob = blob_vector.get_first_element() if blob.get_plain_format() == ".fits": #await self.blob_queue.put(io.BytesIO(blob.get_data()))