Source code for neclib.safety.decelerate

r"""Additionally restrict the maximum drive speed near drive range limits.

If you understand the junior-high physics, you know the algorithm:

.. math::

   v &= \int a \ dt = a \int dt = at \qquad (v_0 = 0) \\
   x &= \int v \ dt = a \int t \ dt = \frac{a t^2}{2} \qquad (x_0 = 0) \\
   v(x) &= a \sqrt{\frac{2x}{a}} = \sqrt{2ax}

where :math:`x` is the distance between encoder reading and drive limits.

"""

from typing import Optional

import astropy.units as u

from ..core import ValueRange, get_quantity, math
from ..core.types import DimensionLess, Union, UnitType


[docs]class Decelerate: """Decelerate the telescope drive as it nears the drive range limits. Parameters ---------- limit The range of encoder values that are considered the drive range limits. max_acceleration The maximum (absolute) acceleration of the telescope drive. Examples -------- >>> limit = neclib.core.ValueRange(5 << u.deg, 355 << u.deg) >>> calculator = neclib.safety.Decelerate(limit, 1.0 << u.deg / u.s**2) >>> calculator(354.6 << u.deg, 1 << u.deg / u.s) <Quantity 0.89442719 deg / s> >>> calculator(354.6 << u.deg, -1 << u.deg / u.s) <Quantity -1. deg / s> """ def __init__( self, limit: ValueRange[u.Quantity], # type: ignore max_acceleration: u.Quantity, ) -> None: self.limit = limit self.max_acceleration = max_acceleration def __call__( self, encoder_reading: Union[DimensionLess, u.Quantity], velocity: Union[DimensionLess, u.Quantity], angle_unit: Optional[UnitType] = None, ) -> u.Quantity: velocity = get_quantity( velocity, unit=(None if angle_unit is None else f"{angle_unit}/s") ) encoder_reading = get_quantity(encoder_reading, unit=angle_unit) if encoder_reading not in self.limit: return 0 << velocity.unit # type: ignore position_relative_to_limits = (limit - encoder_reading for limit in self.limit) max_velocity_squared = ( 2 * self.max_acceleration * rel for rel in position_relative_to_limits ) max_velocity = ( v**0.5 if v.value == 0 else v / (abs(v) ** 0.5) # type: ignore for v in max_velocity_squared ) return math.clip(velocity, *max_velocity) # type: ignore