def desired_dome_azimuth(self,
                             dome_target_azimuth,
                             telescope_target,
                             next_telescope_target=None):
        """Return a new target dome azimuth if dome movement is needed
        to avoid vignetting, else None.

        Parameters
        ----------
        dome_target_azimuth : `float` or `None`
            Dome target azimuth (deg), or `None` if unknown.
        telescope_target : `ElevationAzimuth`
            Telescope target elevation and azimuth.
        next_telescope_target : `ElevationAzimuth` or `None`, optional
            Next telescope_target target elevation and azimuth, if known,
            else `None`. Ignored.

        Returns
        -------
        dome_target_azimuth : `float` or `None`
            New desired dome azimuth (deg), or `None` if no change.
        """
        if dome_target_azimuth is None:
            return telescope_target.azimuth.position

        # scaled_delta_azimuth is the difference multiplied by cos(target alt).
        scaled_delta_azimuth = utils.angle_diff(
            telescope_target.azimuth.position,
            dome_target_azimuth).deg * math.cos(
                telescope_target.elevation.position * RAD_PER_DEG)
        if abs(scaled_delta_azimuth) < self.max_delta_azimuth:
            return None
        return telescope_target.azimuth.position
Beispiel #2
0
    async def dome_telemetry(self) -> None:

        while self.run_telemetry_loop:
            if (self.controllers.mtdome.evt_summaryState.data.summaryState ==
                    salobj.State.ENABLED):

                dome_az_set = self.controllers.mtdome.tel_azimuth.data.positionCommanded
                dome_el_set = (self.controllers.mtdome.tel_lightWindScreen.
                               data.positionCommanded)

                dome_az_pos = self.controllers.mtdome.tel_azimuth.data.positionActual
                dome_el_pos = (self.controllers.mtdome.tel_lightWindScreen.
                               data.positionActual)

                error_az = np.random.normal(0.0, 1e-7)
                error_el = np.random.normal(0.0, 1e-7)

                diff_az = angle_diff(dome_az_set, dome_az_pos)
                diff_el = angle_diff(dome_el_set, dome_el_pos)

                # This next bit is to simulate the MTDomeTrajectory behavior.
                if (self.controllers.mtdometrajectory.evt_summaryState.data.
                        summaryState == salobj.State.ENABLED):
                    dome_az_set = getattr(
                        self.controllers.mtmount.tel_azimuth.data,
                        self.mtmount_demand_position_name,
                    )
                    dome_el_set = getattr(
                        self.controllers.mtmount.tel_elevation.data,
                        self.mtmount_demand_position_name,
                    )

                self.controllers.mtdome.tel_azimuth.set(
                    positionCommanded=dome_az_set,
                    positionActual=dome_az_pos + error_az + diff_az.deg / 1.1,
                )
                self.controllers.mtdome.tel_lightWindScreen.set(
                    positionCommanded=dome_el_set,
                    positionActual=dome_el_pos + error_el + diff_el.deg / 1.1,
                )

                await self.controllers.mtdome.tel_azimuth.write()

                await self.controllers.mtdome.tel_lightWindScreen.write()

            await asyncio.sleep(HEARTBEAT_INTERVAL)
Beispiel #3
0
    def _get_distance(self) -> float:
        """Determines the smallest distance [rad] between the initial and
        target positions assuming motion around a circle.

        Returns
        -------
        distance: `float`
            The smallest distance between the initial and target positions.
        """
        distance = utils.angle_diff(
            math.degrees(self._end_position), math.degrees(self._start_position)
        ).rad
        return distance
Beispiel #4
0
    async def wait_dome_move(self, azimuth):
        """Wait for an ATDome azimuth move to finish.

        Parameters
        ----------
        azimuth : `float`
            Target azimuth for telescope and dome (deg)
        """
        while True:
            curr_pos = await self.dome_remote.tel_position.next(
                flush=True, timeout=STD_TIMEOUT)
            if utils.angle_diff(curr_pos.azimuthPosition, azimuth).deg < 0.1:
                break
Beispiel #5
0
 async def move_azimuth_loop(self):
     """Move the dome to the specified azimuth."""
     try:
         max_az_corr = abs(self.az_vel * self.telemetry_interval)
         while True:
             if (self.summary_state == salobj.State.ENABLED
                     and self.cmd_az != self.curr_az):
                 az_err = utils.angle_diff(self.cmd_az, self.curr_az).deg
                 abs_az_corr = min(abs(az_err), max_az_corr)
                 az_corr = abs_az_corr if az_err >= 0 else -abs_az_corr
                 self.curr_az += az_corr
             await self.tel_position.set_write(azimuthPosition=self.curr_az)
             await asyncio.sleep(self.telemetry_interval)
     except asyncio.CancelledError:
         raise
     except Exception:
         self.log.exception("move_azimuth_loop failed")
         raise
    def desired_dome_elevation(
        self, dome_target_elevation, telescope_target, next_telescope_target=None
    ):
        """Return a new target dome elevation if dome movement is needed
        to avoid vignetting, else None.

        Parameters
        ----------
        dome_target_elevation : `lsst.ts.simactuators.path.PathSegment` \
                or `None`
            Dome target elevation, or `None` if unknown.
        telescope_target : `ElevationAzimuth`
            Telescope target elevation and azimuth.
        next_telescope_target : `ElevationAzimuth` or `None`, optional
            Next telescope_target target elevation and azimuth, if known,
            else `None`. Ignored.

        Returns
        -------
        dome_target_elevation : `lsst.ts.simactuators.path.PathSegment` \
                or `None`
            New desired dome elevation, or `None` if no change.
        """
        if dome_target_elevation is None:
            return telescope_target.elevation

        dome_elevation_shifted = dome_target_elevation.at(
            telescope_target.elevation.tai
        )
        eldiff = utils.angle_diff(
            dome_elevation_shifted.position, telescope_target.elevation.position
        ).deg
        if abs(eldiff) < self.max_delta_elevation:
            return None
        return simactuators.path.PathSegment(
            position=telescope_target.elevation.position,
            velocity=0,
            tai=telescope_target.elevation.tai,
        )
    def desired_dome_azimuth(
        self, dome_target_azimuth, telescope_target, next_telescope_target=None
    ):
        """Return a new target dome azimuth if dome movement is needed
        to avoid vignetting, else None.

        Parameters
        ----------
        dome_target_azimuth : `lsst.ts.simactuators.path.PathSegment` \
                or `None`
            Dome target azimuth.
        telescope_target : `ElevationAzimuth`
            Telescope target elevation and azimuth.
        next_telescope_target : `ElevationAzimuth` or `None`, optional
            Next telescope_target target elevation and azimuth, if known,
            else `None`. Ignored.

        Returns
        -------
        dome_target_azimuth : `lsst.ts.simactuators.path.PathSegment` or `None`
            New desired dome azimuth, or `None` if no change.
        """
        if dome_target_azimuth is None:
            return telescope_target.azimuth

        # scaled_delta_azimuth is the difference multiplied by cos(target alt).
        dome_azimuth_shifted = dome_target_azimuth.at(telescope_target.azimuth.tai)
        scaled_delta_azimuth = utils.angle_diff(
            telescope_target.azimuth.position, dome_azimuth_shifted.position
        ).deg * math.cos(telescope_target.elevation.position * RAD_PER_DEG)
        if abs(scaled_delta_azimuth) < self.max_delta_azimuth:
            return None
        return simactuators.path.PathSegment(
            position=telescope_target.azimuth.position,
            velocity=0,
            tai=telescope_target.azimuth.tai,
        )
Beispiel #8
0
    async def monitor_position(self,
                               check: typing.Optional[typing.Any] = None
                               ) -> None:
        """Monitor MTCS axis position.

        Monitor/log a selected set of axis from the main telescope. This is
        useful during slew activities to make sure everything is going as
        expected.

        Parameters
        ----------
        check : `types.SimpleNamespace` or `None`
            Override `self.check` for defining which resources are used.
        """

        assert self._dome_az_in_position is not None
        assert self._dome_el_in_position is not None
        # Creates a copy of check so it can be freely modified to control what
        # needs to be verified at each stage of the process.
        _check = copy.copy(self.check) if check is None else copy.copy(check)

        self.log.debug("Monitor position started.")

        # xml 7.1/8.0 backward compatibility
        mtmount_actual_position_name = "actualPosition"

        if _check.mtmount:
            self.log.debug("Waiting for Target event from mtmount.")
            try:
                target = await self.rem.mtmount.evt_target.next(
                    flush=True, timeout=self.long_timeout)
                self.log.debug(f"Mount target: {target}")
            except asyncio.TimeoutError:
                raise RuntimeError(
                    "Not receiving target events from the NewMTMount. "
                    "Check component for errors.")
            if not hasattr(self.rem.mtmount.tel_azimuth.DataType(),
                           mtmount_actual_position_name):
                self.log.debug("Running in xml 7.1 compatibility mode.")
                mtmount_actual_position_name = "angleActual"

        while True:

            status = ""

            if _check.mtmount:
                target, tel_az, tel_el = await asyncio.gather(
                    self.rem.mtmount.evt_target.next(
                        flush=True, timeout=self.long_timeout),
                    self.rem.mtmount.tel_azimuth.next(
                        flush=True, timeout=self.fast_timeout),
                    self.rem.mtmount.tel_elevation.next(
                        flush=True, timeout=self.fast_timeout),
                )
                tel_az_actual_position = getattr(tel_az,
                                                 mtmount_actual_position_name)
                tel_el_actual_position = getattr(tel_el,
                                                 mtmount_actual_position_name)
                distance_az = angle_diff(target.azimuth,
                                         tel_az_actual_position)
                distance_el = angle_diff(target.elevation,
                                         tel_el_actual_position)
                status += (
                    f"[Tel]: Az = {tel_az_actual_position:+08.3f}[{distance_az.deg:+6.1f}]; "
                    f"El = {tel_el_actual_position:+08.3f}[{distance_el.deg:+6.1f}] "
                )

            if _check.mtrotator:
                rotation_data = await self.rem.mtrotator.tel_rotation.next(
                    flush=True, timeout=self.fast_timeout)
                distance_rot = angle_diff(rotation_data.demandPosition,
                                          rotation_data.actualPosition)
                status += f"[Rot]: {rotation_data.demandPosition:+08.3f}[{distance_rot.deg:+6.1f}] "

            if _check.mtdome:
                dome_az = await self.rem.mtdome.tel_azimuth.next(
                    flush=True, timeout=self.fast_timeout)
                dome_el = await self.rem.mtdome.tel_lightWindScreen.next(
                    flush=True, timeout=self.fast_timeout)
                dome_az_diff = angle_diff(dome_az.positionActual,
                                          dome_az.positionCommanded)
                dome_el_diff = angle_diff(dome_el.positionActual,
                                          dome_el.positionCommanded)
                if np.abs(dome_az_diff) < self.dome_slew_tolerance:
                    self._dome_az_in_position.set()

                if np.abs(dome_el_diff) < self.dome_slew_tolerance:
                    self._dome_el_in_position.set()

                status += (f"[Dome] Az = {dome_az.positionActual:+08.3f}; "
                           f"El = {dome_el.positionActual:+08.3f} ")

            if len(status) > 0:
                self.log.debug(status)

            await asyncio.sleep(self.fast_timeout)
Beispiel #9
0
    async def rotator_telemetry(self) -> None:

        await self.controllers.mtrotator.tel_electrical.set_write()
        await self.controllers.mtrotator.evt_connected.set_write(connected=True
                                                                 )
        await self.controllers.mtrotator.evt_configuration.set_write(
            positionAngleUpperLimit=90.0,
            velocityLimit=3.0,
            accelerationLimit=1.0,
            positionErrorThreshold=0.0,
            positionAngleLowerLimit=-90.0,
            followingErrorThreshold=0.1,
            trackingSuccessPositionThreshold=0.01,
            trackingLostTimeout=5.0,
        )
        await self.controllers.mtrotator.evt_controllerState.set_write(
            controllerState=0,
            offlineSubstate=1,
            enabledSubstate=0,
            applicationStatus=1024,
        )

        await self.controllers.mtrotator.evt_interlock.set_write(engaged=False)

        while self.run_telemetry_loop:
            if (self.controllers.mtrotator.evt_summaryState.data.summaryState
                    == salobj.State.ENABLED):

                # Safely initilize topic data.
                if not self.controllers.mtrotator.tel_rotation.has_data:
                    self.controllers.mtrotator.tel_rotation.set(
                        demandPosition=0.0, actualPosition=0.0)

                error = np.random.normal(0.0, 1e-7)
                demand = self.controllers.mtrotator.tel_rotation.data.demandPosition
                position = self.controllers.mtrotator.tel_rotation.data.actualPosition
                dif = angle_diff(demand, position)

                await self.controllers.mtrotator.tel_rotation.set_write(
                    actualPosition=position + error + dif.deg / 1.1)

                await self.controllers.mtrotator.tel_motors.set_write(
                    calibrated=np.zeros(2) + self.controllers.mtrotator.
                    tel_rotation.data.actualPosition,
                    raw=27.4e6 * (np.zeros(2) + self.controllers.mtrotator.
                                  tel_rotation.data.actualPosition),
                )

                await self.controllers.mtrotator.tel_ccwFollowingError.set_write(
                    timestamp=current_tai())

                if self.acting:
                    await self.controllers.mtrotator.evt_target.set_write(
                        position=position,
                        velocity=0.0,
                        tai=current_tai(),
                    )
                    await self.controllers.mtrotator.evt_tracking.set_write(
                        tracking=True)
                    await self.controllers.mtrotator.evt_inPosition.set_write(
                        inPosition=np.abs(dif) < ROT_IN_POSITION_DELTA)
                else:
                    await self.controllers.mtrotator.evt_tracking.set_write(
                        tracking=False)

            await asyncio.sleep(HEARTBEAT_INTERVAL)
Beispiel #10
0
    async def mount_telemetry(self) -> None:

        await self.controllers.mtmount.evt_elevationMotionState.set_write(
            state=MTMount.AxisMotionState.STOPPED)
        await self.controllers.mtmount.evt_azimuthMotionState.set_write(
            state=MTMount.AxisMotionState.STOPPED)
        await self.controllers.mtmount.evt_cameraCableWrapMotionState.set_write(
            state=MTMount.AxisMotionState.STOPPED)
        await self.controllers.mtmount.evt_cameraCableWrapFollowing.set_write(
            enabled=True)
        await self.controllers.mtmount.evt_connected.set_write(command=True,
                                                               replies=True)

        while self.run_telemetry_loop:
            if (self.controllers.mtmount.evt_summaryState.data.summaryState ==
                    salobj.State.ENABLED):

                # Safely initilize data
                if not self.controllers.mtmount.tel_azimuth.has_data:
                    self.controllers.mtmount.tel_azimuth.set(
                        **{
                            self.mtmount_demand_position_name: 0.0,
                            self.mtmount_actual_position_name: 0.0,
                        })

                if not self.controllers.mtmount.tel_elevation.has_data:
                    self.controllers.mtmount.tel_elevation.set(
                        **{
                            self.mtmount_demand_position_name: 0.0,
                            self.mtmount_actual_position_name: 0.0,
                        })

                if not self.controllers.mtmount.tel_elevationDrives.has_data:
                    self.controllers.mtmount.tel_elevationDrives.set()

                if not self.controllers.mtmount.tel_azimuthDrives.has_data:
                    self.controllers.mtmount.tel_azimuthDrives.set()

                if not self.controllers.mtmount.evt_target.has_data:
                    self.controllers.mtmount.evt_target.set()

                az_induced_error = np.random.normal(0.0, 1e-7)
                el_induced_error = np.random.normal(0.0, 1e-7)

                az_set = getattr(
                    self.controllers.mtmount.tel_azimuth.data,
                    self.mtmount_demand_position_name,
                )
                el_set = getattr(
                    self.controllers.mtmount.tel_elevation.data,
                    self.mtmount_demand_position_name,
                )

                az_actual = getattr(
                    self.controllers.mtmount.tel_azimuth.data,
                    self.mtmount_actual_position_name,
                )
                el_actual = getattr(
                    self.controllers.mtmount.tel_elevation.data,
                    self.mtmount_actual_position_name,
                )

                az_dif = angle_diff(az_set, az_actual)
                el_dif = angle_diff(el_set, el_actual)
                in_position_elevation = np.abs(el_dif) < 1e-1 * u.deg
                in_position_azimuth = np.abs(az_dif) < 1e-1 * u.deg

                if self.acting:
                    await self.controllers.mtmount.evt_elevationMotionState.set_write(
                        state=MTMount.AxisMotionState.TRACKING)
                    await self.controllers.mtmount.evt_azimuthMotionState.set_write(
                        state=MTMount.AxisMotionState.TRACKING)
                    await self.controllers.mtmount.evt_cameraCableWrapMotionState.set_write(
                        state=MTMount.AxisMotionState.TRACKING)
                    await self.controllers.mtmount.evt_elevationInPosition.set_write(
                        inPosition=in_position_elevation)
                    await self.controllers.mtmount.evt_azimuthInPosition.set_write(
                        inPosition=in_position_azimuth)
                    await self.controllers.mtmount.evt_cameraCableWrapInPosition.set_write(
                        inPosition=True)
                elif (self.controllers.mtmount.evt_elevationMotionState.data.
                      state == MTMount.AxisMotionState.TRACKING):
                    await self.controllers.mtmount.evt_elevationMotionState.set_write(
                        state=MTMount.AxisMotionState.STOPPING)
                    await self.controllers.mtmount.evt_azimuthMotionState.set_write(
                        state=MTMount.AxisMotionState.STOPPING)
                    await self.controllers.mtmount.evt_cameraCableWrapMotionState.set_write(
                        state=MTMount.AxisMotionState.STOPPING)
                else:
                    await self.controllers.mtmount.evt_elevationMotionState.set_write(
                        state=MTMount.AxisMotionState.STOPPED)
                    await self.controllers.mtmount.evt_azimuthMotionState.set_write(
                        state=MTMount.AxisMotionState.STOPPED)
                    await self.controllers.mtmount.evt_cameraCableWrapMotionState.set_write(
                        state=MTMount.AxisMotionState.STOPPED)
                # The following computation of angleActual is to emulate a
                # trajectory. At every loop it adds three factors:
                #   1 - the currect position
                #   2 - a random error factor
                #   3 - difference between the current and target positions
                #       multiplied by a STEP_FACTOR, where
                #       0 < STEP_FACTOR <= 1.0
                # If STEP_FACTOR == 1 it means the target position will be
                # achieved in one loop. By reducing STEP_FACTOR we can emulate
                # a slew that takes a certain number of iterations to be
                # completed.
                await self.controllers.mtmount.tel_azimuth.set_write(
                    **{
                        self.mtmount_actual_position_name:
                        az_actual + az_induced_error + az_dif.deg * STEP_FACTOR
                    })
                await self.controllers.mtmount.tel_elevation.set_write(
                    **{
                        self.mtmount_actual_position_name:
                        el_actual + el_induced_error + el_dif.deg * STEP_FACTOR
                    })

                await self.controllers.mtmount.tel_azimuthDrives.set_write(
                    current=np.random.normal(
                        loc=0.5,
                        scale=0.01,
                        size=len(self.controllers.mtmount.tel_azimuthDrives.
                                 data.current),
                    ),
                    timestamp=current_tai(),
                )

                await self.controllers.mtmount.tel_elevationDrives.set_write(
                    current=np.random.normal(
                        loc=0.5,
                        scale=0.01,
                        size=len(self.controllers.mtmount.tel_elevationDrives.
                                 data.current),
                    ),
                    timestamp=current_tai(),
                )

                await self.controllers.mtmount.tel_cameraCableWrap.set_write(
                    actualPosition=self.controllers.mtrotator.tel_rotation.
                    data.actualPosition,
                    timestamp=current_tai(),
                )
                await self.controllers.mtmount.evt_cameraCableWrapTarget.set_write(
                    position=self.controllers.mtrotator.tel_rotation.data.
                    actualPosition,
                    taiTime=current_tai(),
                    force_output=True,
                )

                await self.controllers.mtmount.evt_target.write()

            await asyncio.sleep(HEARTBEAT_INTERVAL)