diff --git a/pocs/images.py b/pocs/images.py index c7ce87d33..0c09e63f8 100644 --- a/pocs/images.py +++ b/pocs/images.py @@ -129,10 +129,10 @@ def pointing_error(self): d_ra = self.pointing.ra - self.header_pointing.ra self._pointing_error = OffsetError( - d_ra.to( - u.arcsec), d_dec.to( - u.arcsec), mag.to( - u.arcsec)) + d_ra.to(u.arcsec), + d_dec.to(u.arcsec), + mag.to(u.arcsec) + ) return self._pointing_error diff --git a/pocs/mount/mount.py b/pocs/mount/mount.py index e8cc37ec9..51c25ca10 100644 --- a/pocs/mount/mount.py +++ b/pocs/mount/mount.py @@ -72,8 +72,8 @@ def __init__(self, location, commands=None, *args, **kwargs self._state = 'Parked' self.sidereal_rate = ((360 * u.degree).to(u.arcsec) / (86164 * u.second)) - self.ra_guide_rate = 0.5 # Sidereal - self.dec_guide_rate = 0.5 # Sidereal + self.ra_guide_rate = 0.9 # Sidereal + self.dec_guide_rate = 0.9 # Sidereal self._tracking_rate = 1.0 # Sidereal self._tracking = 'Sidereal' self._movement_speed = '' @@ -311,6 +311,121 @@ def distance_from_target(self): return separation + def get_tracking_correction(self, offset_info, pointing_ha): + """Determine the needed tracking corrections from current position. + + This method will determine the direction and number of milliseconds to + correct the mount for each axis in order to correct for any tracking + drift. The Declination axis correction ('north' or 'south') depends on + the movement of the camera box with respect to the pier, which can be + determined from the Hour Angle (HA) of the pointing image in the sequence. + + Note: + Correction values below 50ms will be skipped and values above 99999ms + will be clipped. + + Args: + offset_info (`OffsetError`): A named tuple describing the offset + error. See `pocs.images.OffsetError`. + pointing_ha (float): The Hour Angle (HA) of the mount at the + beginning of the observation sequence in degrees. This affects + the direction of the Dec adjustment. + + Returns: + dict: Offset corrections for each axis as needed :: + + dict: { + # axis: (arcsec, millisecond, direction) + 'ra': (float, float, str), + 'dec': (float, float, str), + } + """ + pier_side = 'east' + if pointing_ha <= 12: + pier_side = 'west' + + self.logger.debug("Mount pier side: {}".format(pier_side)) + + axis_corrections = { + 'dec': None, + 'ra': None, + } + + for axis in axis_corrections.keys(): + # find the number of ms and direction for Dec axis + offset = getattr(offset_info, 'delta_{}'.format(axis)) + offset_ms = self.get_ms_offset(offset, axis=axis) + + if axis == 'dec': + # Determine which direction to move based on direction mount + # is moving (i.e. what side it started on). + if pier_side == 'east': + if offset_ms >= 0: + delta_direction = 'north' + else: + delta_direction = 'south' + else: + if offset_ms >= 0: + delta_direction = 'south' + else: + delta_direction = 'north' + else: + if offset_ms >= 0: + delta_direction = 'west' + else: + delta_direction = 'east' + + offset_ms = abs(offset_ms.value) + + # Skip short corrections + if offset_ms <= 50: + continue + + # Ensure we don't try to move for too long + max_time = 99999 + + # Correct long offset + if offset_ms > max_time: + offset_ms = max_time + + axis_corrections[axis] = (offset, offset_ms, delta_direction) + + return axis_corrections + + def correct_tracking(self, correction_info, axis_timeout=30.): + """ Make tracking adjustment corrections. + + Args: + correction_info (dict[tuple]): Correction info to be applied, see + `get_tracking_correction`. + axis_timeout (float, optional): Timeout for adjustment in each axis, + default 30 seconds. + + Raises: + `error.Timeout`: Timeout error. + """ + for axis, corrections in correction_info.items(): + offset = corrections[0] + offset_ms = corrections[1] + delta_direction = corrections[2] + + self.logger.info("Adjusting {}: {} {:0.2f} ms {:0.2f}".format( + axis, delta_direction, offset_ms, offset)) + + self.mount.query( + 'move_ms_{}'.format(delta_direction), + '{:05.0f}'.format(offset_ms) + ) + + # Adjust tracking for `axis_timeout` seconds then fail if not done. + start_tracking_time = current_time() + while self.mount.is_tracking is False: + if (current_time() - start_tracking_time).sec > axis_timeout: + raise error.Timeout("Tracking adjustment timeout: {}".format(axis)) + + self.logger.debug("Waiting for {} tracking adjustment".format(axis)) + time.sleep(0.5) + ################################################################################################## # Movement methods diff --git a/pocs/observatory.py b/pocs/observatory.py index 383e9b1a7..65f07dd45 100644 --- a/pocs/observatory.py +++ b/pocs/observatory.py @@ -1,5 +1,4 @@ import os -import time from collections import OrderedDict from datetime import datetime @@ -342,67 +341,24 @@ def update_tracking(self): if self.current_offset_info is not None: self.logger.debug("Updating the tracking") - # find the number of ms and direction for Dec axis - dec_offset = self.current_offset_info.delta_dec - dec_ms = self.mount.get_ms_offset(dec_offset, axis='dec') - if dec_offset >= 0: - dec_direction = 'north' - else: - dec_direction = 'south' + # Get the pier side of pointing image + pointing_ha = self.current_observation.pointing_image.header_ha - # find the number of ms and direction for RA axis - ra_offset = self.current_offset_info.delta_ra - ra_ms = self.mount.get_ms_offset(ra_offset, axis='ra') - if ra_offset >= 0: - ra_direction = 'west' - else: - ra_direction = 'east' - - dec_ms = abs(dec_ms.value) * 1.5 # TODO(wtgee): Figure out why 1.5 - ra_ms = abs(ra_ms.value) * 1. - - # Ensure we don't try to move for too long - max_time = 99999 - - # Correct the Dec axis (if offset is large enough) - if dec_ms > max_time: - dec_ms = max_time - - if dec_ms >= 50: - self.logger.info("Adjusting Dec: {} {:0.2f} ms {:0.2f}".format( - dec_direction, dec_ms, dec_offset)) - if dec_ms >= 1. and dec_ms <= max_time: - self.mount.query('move_ms_{}'.format( - dec_direction), '{:05.0f}'.format(dec_ms)) - - # Adjust tracking for up to 30 seconds then fail if not done. - start_tracking_time = current_time() - while self.mount.is_tracking is False: - if (current_time() - start_tracking_time).sec > 30: - raise Exception("Trying to adjust Dec tracking for more than 30 seconds") - - self.logger.debug("Waiting for Dec tracking adjustment") - time.sleep(0.1) - - # Correct the RA axis (if offset is large enough) - if ra_ms > max_time: - ra_ms = max_time - - if ra_ms >= 50: - self.logger.info("Adjusting RA: {} {:0.2f} ms {:0.2f}".format( - ra_direction, ra_ms, ra_offset)) - if ra_ms >= 1. and ra_ms <= max_time: - self.mount.query('move_ms_{}'.format( - ra_direction), '{:05.0f}'.format(ra_ms)) - - # Adjust tracking for up to 30 seconds then fail if not done. - start_tracking_time = current_time() - while self.mount.is_tracking is False: - if (current_time() - start_tracking_time).sec > 30: - raise Exception("Trying to adjust RA tracking for more than 30 seconds") - - self.logger.debug("Waiting for RA tracking adjustment") - time.sleep(0.1) + try: + pointing_ha = pointing_ha.value + except AttributeError: + pass + + self.logger.debug("Pointing HA: {}".format(pointing_ha)) + correction_info = self.mount.get_tracking_correction( + self.current_offset_info, + pointing_ha + ) + + try: + self.mount.correct_tracking(correction_info) + except error.Timeout: + self.logger.warning("Timeout while correcting tracking") def get_standard_headers(self, observation=None): """Get a set of standard headers diff --git a/pocs/tests/bisque/test_mount.py b/pocs/tests/bisque/test_mount.py index 8c514ac3a..0a076646b 100644 --- a/pocs/tests/bisque/test_mount.py +++ b/pocs/tests/bisque/test_mount.py @@ -115,13 +115,6 @@ def test_update_location(mount, config): assert mount.location == location2 -# def test_target_coords_below_horizon(mount, target_down): -# mount.initialize() - -# assert mount.set_target_coordinates(target_down) is False -# assert mount.get_target_coordinates() is None - - def test_target_coords(mount, target): mount.initialize(unpark=True) diff --git a/pocs/tests/test_ioptron.py b/pocs/tests/test_ioptron.py index 8c345d414..020f708ca 100644 --- a/pocs/tests/test_ioptron.py +++ b/pocs/tests/test_ioptron.py @@ -1,8 +1,33 @@ +import os import pytest from astropy.coordinates import EarthLocation +from astropy import units as u +from pocs.images import OffsetError from pocs.mount.ioptron import Mount +from pocs.utils.config import load_config + + +@pytest.fixture +def location(): + config = load_config(ignore_local=True) + loc = config['location'] + return EarthLocation(lon=loc['longitude'], lat=loc['latitude'], height=loc['elevation']) + + +@pytest.fixture(scope="function") +def mount(config, location): + try: + del os.environ['POCSTIME'] + except KeyError: + pass + + config['mount'] = { + 'brand': 'bisque', + 'template_dir': 'resources/bisque', + } + return Mount(location=location, config=config) @pytest.mark.with_mount @@ -64,3 +89,41 @@ def test_unpark_park(self): assert self.mount.is_parked is False self.mount.home_and_park() assert self.mount.is_parked is True + + +def test_get_tracking_correction(mount): + + offsets = [ + # HA, ΔRA, ΔDec, Magnitude + (2, -13.0881456, 1.4009, 12.154), + (2, -13.0881456, -1.4009, 12.154), + (2, 13.0881456, 1.4009, 12.154), + (14, -13.0881456, 1.4009, 12.154), + (14, 13.0881456, 1.4009, 12.154), + ] + + corrections = [ + (103.49, 'south', 966.84, 'east'), + (103.49, 'north', 966.84, 'east'), + (103.49, 'south', 966.84, 'west'), + (103.49, 'north', 966.84, 'east'), + (103.49, 'north', 966.84, 'west'), + ] + + for offset, correction in zip(offsets, corrections): + pointing_ha = offset[0] + offset_info = OffsetError( + offset[1] * u.arcsec, + offset[2] * u.arcsec, + offset[3] * u.arcsec + ) + correction_info = mount.get_tracking_correction(offset_info, pointing_ha) + + dec_info = correction_info['dec'] + ra_info = correction_info['ra'] + + assert dec_info[1] == pytest.approx(correction[0], rel=1e-2) + assert dec_info[2] == correction[1] + + assert ra_info[1] == pytest.approx(correction[2], rel=1e-2) + assert ra_info[2] == correction[3]