def autoguide(self, timeout=30): """ Perform autoguiding Args: timeout (int, optional): Timeout in seconds to wait for the guide image, defaults to 30 seconds Returns: bool: Indicates if guiding was successfully started Raises: error.PanError: Error raised if guide image does not appear """ success = False if self.is_connected: self.logger.debug("Starting autoguider") # Remove existing image try: os.remove(self.image_path) except FileNotFoundError: pass self.logger.debug("Getting autoguiding image") self.take_exposure() count = 0 while not os.path.exists(self.image_path): self.logger.debug("Waiting for guide image") time.sleep(1) count += 1 if count == timeout: raise error.PanError("Problem getting autoguide image") try: x, y = self.find_guide_star() except Exception as e: raise error.PanError( "Can't find guide star in image, guiding not turned on") self.logger.debug( "Setting guide star at CCD coordinates: {} {}".format(x, y)) self.set_guide_position(x=x, y=y) self.logger.debug("Starting autoguide") success = self.start_guiding() return success
def process_cr2(cr2_fname, fits_headers={}, solve=True, make_pretty=False, verbose=False, **kwargs): assert os.path.exists(cr2_fname), warnings.warn("File must exist: {}".format(cr2_fname)) processed_info = {} try: if verbose: print("Processing image") if make_pretty: # If we have the object name, pass it to pretty image if 'title' in fits_headers: kwargs['title'] = "{}".format(fits_headers.get('title')) pretty_image = make_pretty_image(cr2_fname, **kwargs) processed_info['pretty_image'] = pretty_image if solve: try: solve_info = get_solve_field(cr2_fname, fits_headers=fits_headers, **kwargs) if verbose: print("Solve info: {}".format(solve_info)) processed_info.update(solve_info) except error.PanError as e: warnings.warn("Timeout while solving: {}".format(e)) except Exception as e: raise error.PanError("Can't solve field: {}".format(e)) except Exception as e: warnings.warn("Problem in processing: {}".format(e)) return processed_info
def command(self, cmd): """ Run gphoto2 command """ # Test to see if there is a running command already if self._proc and self._proc.poll(): raise error.InvalidCommand("Command already running") else: # Build the command. run_cmd = [self._gphoto2, '--port', self.port] run_cmd.extend(listify(cmd)) self.logger.debug("gphoto2 command: {}".format(run_cmd)) try: self._proc = subprocess.Popen(run_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, shell=False) except OSError as e: raise error.InvalidCommand( "Can't send command to gphoto2. {} \t {}".format( e, run_cmd)) except ValueError as e: raise error.InvalidCommand( "Bad parameters to gphoto2. {} \t {}".format(e, run_cmd)) except Exception as e: raise error.PanError(e)
def _make_pretty_from_cr2(fname, timeout=15, **kwargs): verbose = kwargs.get('verbose', False) title = '{} {}'.format(kwargs.get('title', ''), current_time().isot) solve_field = "{}/scripts/cr2_to_jpg.sh".format(os.getenv('POCS')) cmd = [solve_field, fname, title] if kwargs.get('primary', False): cmd.append('link') if verbose: print(cmd) try: proc = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) if verbose: print(proc) except OSError as e: raise error.InvalidCommand("Can't send command to gphoto2." " {} \t {}".format(e, cmd)) except ValueError as e: raise error.InvalidCommand("Bad parameters to gphoto2." " {} \t {}".format(e, cmd)) except Exception as e: raise error.PanError("Timeout on plate solving: {}".format(e)) return fname.replace('cr2', 'jpg')
def _setup_location(self): """ Sets up the site and location details for the observatory Note: These items are read from the 'site' config directive and include: * name * latitude * longitude * timezone * presseure * elevation * horizon """ self.logger.debug('Setting up site details of observatory') try: config_site = self.config.get('location') name = config_site.get('name', 'Nameless Location') latitude = config_site.get('latitude') longitude = config_site.get('longitude') timezone = config_site.get('timezone') utc_offset = config_site.get('utc_offset') pressure = config_site.get('pressure', 0.680) * u.bar elevation = config_site.get('elevation', 0 * u.meter) horizon = config_site.get('horizon', 30 * u.degree) twilight_horizon = config_site.get('twilight_horizon', -18 * u.degree) self.location = { 'name': name, 'latitude': latitude, 'longitude': longitude, 'elevation': elevation, 'timezone': timezone, 'utc_offset': utc_offset, 'pressure': pressure, 'horizon': horizon, 'twilight_horizon': twilight_horizon, } self.logger.debug("Location: {}".format(self.location)) # Create an EarthLocation for the mount self.earth_location = EarthLocation(lat=latitude, lon=longitude, height=elevation) self.observer = Observer(location=self.earth_location, name=name, timezone=timezone) except Exception: raise error.PanError(msg='Bad site information')
def __init__(self, *arg, **kwargs): super().__init__(*arg, **kwargs) self._gphoto2 = shutil.which('gphoto2') assert self._gphoto2 is not None, error.PanError("Can't find gphoto2") self.logger.debug('GPhoto2 camera {} created on {}'.format( self.name, self.port)) # Setup a holder for the process self._proc = None
def make_pretty_image(fname, timeout=15, **kwargs): # pragma: no cover """ Make a pretty image This calls out to an external script which will try to extract the JPG directly from the CR2 file, otherwise will do an actual conversion Notes: See `$POCS/scripts/cr2_to_jpg.sh` Arguments: fname {str} -- Name of CR2 file **kwargs {dict} -- Additional arguments to be passed to external script Keyword Arguments: timeout {number} -- Process timeout (default: {15}) Returns: str -- Filename of image that was created """ assert os.path.exists(fname),\ warn("File doesn't exist, can't make pretty: {}".format(fname)) verbose = kwargs.get('verbose', False) title = '{} {}'.format(kwargs.get('title', ''), current_time().isot) solve_field = "{}/scripts/cr2_to_jpg.sh".format(os.getenv('POCS')) cmd = [solve_field, fname, title] if kwargs.get('primary', False): cmd.append('link') if verbose: print(cmd) try: proc = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) if verbose: print(proc) except OSError as e: raise error.InvalidCommand("Can't send command to gphoto2." " {} \t {}".format(e, cmd)) except ValueError as e: raise error.InvalidCommand("Bad parameters to gphoto2." " {} \t {}".format(e, cmd)) except Exception as e: raise error.PanError("Timeout on plate solving: {}".format(e)) return fname.replace('cr2', 'jpg')
def _create_mount(self, mount_info=None): """Creates a mount object. Details for the creation of the mount object are held in the configuration file or can be passed to the method. This method ensures that the proper mount type is loaded. Args: mount_info (dict): Configuration items for the mount. Returns: pocs.mount: Returns a sub-class of the mount type """ if mount_info is None: mount_info = self.config.get('mount') model = mount_info.get('model') port = mount_info.get('port') if 'mount' in self.config.get('simulator', []): model = 'simulator' driver = 'simulator' mount_info['simulator'] = True else: model = mount_info.get('brand') driver = mount_info.get('driver') # TODO(jamessynge): We should move the driver specific validation into the driver # module (e.g. module.create_mount_from_config). This means we have to adjust the # definition of this method to return a validated but not fully initialized mount # driver. if model != 'bisque': port = mount_info.get('port') if port is None or len(glob(port)) == 0: msg = "Mount port ({}) not available. Use --simulator=mount for simulator. Exiting.".format( port) raise error.PanError(msg=msg, exit=True) self.logger.debug('Creating mount: {}'.format(model)) module = load_module('pocs.mount.{}'.format(driver)) # Make the mount include site information self.mount = module.Mount(location=self.earth_location) self.logger.debug('Mount created')
def solve_field(fname, timeout=15, solve_opts=[], **kwargs): """ Plate solves an image. Args: fname(str, required): Filename to solve in either .cr2 or .fits extension. timeout(int, optional): Timeout for the solve-field command, defaults to 60 seconds. solve_opts(list, optional): List of options for solve-field. verbose(bool, optional): Show output, defaults to False. """ verbose = kwargs.get('verbose', False) if verbose: print("Entering solve_field") if fname.endswith('cr2'): if verbose: print("Converting cr2 to FITS") fname = cr2_to_fits(fname, **kwargs) if verbose: print("Solved filename: ", fname) solve_field_script = "{}/scripts/solve_field.sh".format(os.getenv('POCS'), '/var/panoptes/POCS') if not os.path.exists(solve_field_script): raise error.InvalidSystemCommand("Can't find solve-field: {}".format(solve_field_script)) # Add the options for solving the field if solve_opts: options = solve_opts else: options = [ '--guess-scale', '--cpulimit', str(timeout), '--no-verify', '--no-plots', '--crpix-center', '--downsample', '4', ] if kwargs.get('clobber', True): options.append('--overwrite') if kwargs.get('skip_solved', True): options.append('--skip-solved') if 'ra' in kwargs: options.append('--ra') options.append(str(kwargs.get('ra'))) if 'dec' in kwargs: options.append('--dec') options.append(str(kwargs.get('dec'))) if 'radius' in kwargs: options.append('--radius') options.append(str(kwargs.get('radius'))) if os.getenv('PANTEMP'): options.append('--temp-dir') options.append(os.getenv('PANTEMP')) cmd = [solve_field_script, ' '.join(options), fname] if verbose: print("Cmd: ", cmd) try: proc = subprocess.Popen(cmd, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) except OSError as e: raise error.InvalidCommand("Can't send command to solve_field.sh. {} \t {}".format(e, cmd)) except ValueError as e: raise error.InvalidCommand("Bad parameters to solve_field.sh. {} \t {}".format(e, cmd)) except Exception as e: raise error.PanError("Timeout on plate solving: {}".format(e)) return proc
def _create_cameras(self, **kwargs): """Creates a camera object(s) Loads the cameras via the configuration. Creates a camera for each camera item listed in the config. Ensures the appropriate camera module is loaded. Note: We are currently only operating with one camera and the `take_pic.sh` script automatically discovers the ports. Note: This does not actually make a usb connection to the camera. To do so, call the 'camear.connect()' explicitly. Args: **kwargs (dict): Can pass a camera_config object that overrides the info in the configuration file. Can also pass `auto_detect`(bool) to try and automatically discover the ports. Returns: list: A list of created camera objects. Raises: error.CameraNotFound: Description error.PanError: Description """ if kwargs.get('camera_info') is None: camera_info = self.config.get('cameras') self.logger.debug("Camera config: \n {}".format(camera_info)) a_simulator = 'camera' in self.config.get('simulator', []) if a_simulator: self.logger.debug("Using simulator for camera") ports = list() # Lookup the connected ports if not using a simulator auto_detect = kwargs.get('auto_detect', camera_info.get('auto_detect', False)) if not a_simulator and auto_detect: self.logger.debug("Auto-detecting ports for cameras") try: ports = list_connected_cameras() except Exception as e: self.logger.warning(e) if len(ports) == 0: raise error.PanError( msg= "No cameras detected. Use --simulator=camera for simulator." ) else: self.logger.debug("Detected Ports: {}".format(ports)) for cam_num, camera_config in enumerate(camera_info.get('devices', [])): cam_name = 'Cam{:02d}'.format(cam_num) if not a_simulator: camera_model = camera_config.get('model') # Assign an auto-detected port. If none are left, skip if auto_detect: try: camera_port = ports.pop() except IndexError: self.logger.warning( "No ports left for {}, skipping.".format(cam_name)) continue else: try: camera_port = camera_config['port'] except KeyError: raise error.CameraNotFound( msg="No port specified and auto_detect=False") camera_focuser = camera_config.get('focuser', None) camera_readout = camera_config.get('readout_time', 6.0) else: # Set up a simulated camera with fully configured simulated # focuser camera_model = 'simulator' camera_port = '/dev/camera/simulator' camera_focuser = { 'model': 'simulator', 'focus_port': '/dev/ttyFAKE', 'initial_position': 20000, 'autofocus_range': (40, 80), 'autofocus_step': (10, 20), 'autofocus_seconds': 0.1, 'autofocus_size': 500 } camera_readout = 0.5 camera_set_point = camera_config.get('set_point', None) camera_filter = camera_config.get('filter_type', None) self.logger.debug('Creating camera: {}'.format(camera_model)) try: module = load_module('pocs.camera.{}'.format(camera_model)) self.logger.debug('Camera module: {}'.format(module)) except ImportError: raise error.CameraNotFound(msg=camera_model) else: # Create the camera object cam = module.Camera(name=cam_name, model=camera_model, port=camera_port, set_point=camera_set_point, filter_type=camera_filter, focuser=camera_focuser, readout_time=camera_readout) is_primary = '' if camera_info.get('primary', '') == cam.uid: self.primary_camera = cam is_primary = ' [Primary]' self.logger.debug("Camera created: {} {} {}".format( cam.name, cam.uid, is_primary)) self.cameras[cam_name] = cam # If no camera was specified as primary use the first if self.primary_camera is None: self.primary_camera = self.cameras['Cam00'] if len(self.cameras) == 0: raise error.CameraNotFound(msg="No cameras available. Exiting.", exit=True) self.logger.debug("Cameras created")