a
    ߙfb:                     @   s   d Z ddlZddlZddlZddlmZ ddlmZ ddl	m
Z ddlmZ ddlmZmZmZmZmZ ddlmZ g ZG d	d
 d
ZG dd deZG dd deZdS )z
This module contains a helper function to fill erfa.astrom struct and a
ScienceState, which allows to speed up coordinate transformations at the
expense of accuracy.
    N)Time)ScienceState)AstropyWarning   )get_jd12get_cipprepare_earth_position_velget_polar_motionpav2pv)rotation_matrixc                   @   s4   e Zd ZdZedd Zedd Zedd ZdS )	
ErfaAstromz
    The default provider for astrometry values.
    A utility class to extract the necessary arguments for
    erfa functions from frame attributes, call the corresponding
    erfa functions and return the astrom object.
    c                 C   s   | j d\}}}| j}t|d\}}t|\}}t||}	t||\}
}}tjt|d }t	|\}}t
| drt| jtj| jtj| jj| jtj\}}nd\}}t|||||
||||tj|tj|tj|||	||S a  
        Wrapper for ``erfa.apco``, used in conversions AltAz <-> ICRS and CIRS <-> ICRS

        Parameters
        ----------
        frame_or_coord : ``astropy.coordinates.BaseCoordinateFrame`` or ``astropy.coordinates.SkyCoord``
            Frame or coordinate instance in the corresponding frame
            for which to calculate the calculate the astrom values.
            For this function, an AltAz or CIRS frame is expected.
        WGS84ttut1pressure)        r   )locationto_geodeticobstimer   r	   erfasp00r   era00r   hasattrrefcor   to_valueuhPatemperaturedeg_Crelative_humidityvalueobswlmicronapcoradianm)frame_or_coordlonlatheightr   jd1_ttjd2_ttxpypspxyseraearth_pvearth_heliocentricrefarefb r8   >lib/python3.9/site-packages/astropy/coordinates/erfa_astrom.pyr$   "   s.    





zErfaAstrom.apcoc                 C   sR   t | jd\}}t| jjddj| jjddj}t| j\}}t	|||||S )a  
        Wrapper for ``erfa.apcs``, used in conversions GCRS <-> ICRS

        Parameters
        ----------
        frame_or_coord : ``astropy.coordinates.BaseCoordinateFrame`` or ``astropy.coordinates.SkyCoord``
            Frame or coordinate instance in the corresponding frame
            for which to calculate the calculate the astrom values.
            For this function, a GCRS frame is expected.
        r   Zxyz_axis)
r   r   r
   	obsgeolocget_xyzr!   	obsgeovelr   r   apcs)r'   r+   r,   Zobs_pvr4   r5   r8   r8   r9   r?   M   s    zErfaAstrom.apcsc                 C   s  t jt| jd }t jt| jd }| jd\}}}|tj	}|tj	}t
| j\}}	tj| jjt jd}
t|dtj	dt|	 dtj	d t| dtj	d t|| dtj	d }|d	 }|d
 }t||}||
d< |d }t|t|| ||  |
d< |d }|d }t|| |
d< t || |
d< t||
d< t||
d< t | jtj| jtj| jj| jtj\|
d< |
d< |
S )a1  
        Slightly modified equivalent of ``erfa.apio``, used in conversions AltAz <-> CIRS.

        Since we use a topocentric CIRS frame, we have dropped the steps needed to calculate
        diurnal aberration.

        Parameters
        ----------
        frame_or_coord : ``astropy.coordinates.BaseCoordinateFrame`` or ``astropy.coordinates.SkyCoord``
            Frame or coordinate instance in the corresponding frame
            for which to calculate the calculate the astrom values.
            For this function, an AltAz frame is expected.
        r   r   r   Zdtypez)Zunitr0   r1   ).r   r   ).r   r   eral).r      Zxpl).r   rC   ).rC   rC   ZyplZalongZsphiZcphir6   r7   )r   r   r   r   r   r   r   r   r   r%   r	   npZzerosshapeZdt_eraASTROMr   Zarctan2ZsqrtZanpmZsinZcosr   r   r   r   r   r    r!   r"   r#   )r'   r/   Zthetar(   r)   r*   ZelongZphir-   r.   ZastromrabrB   cr8   r8   r9   apioa   sB    "zErfaAstrom.apioN)__name__
__module____qualname____doc__staticmethodr$   r?   rJ   r8   r8   r8   r9   r      s   
*
r   c                   @   sn   e Zd ZdZejejddd Zdd Ze	dd Z
e	d	d
 Ze	dd Ze	dd Zdd Zdd ZdS )ErfaAstromInterpolatora  
    A provider for astrometry values that does not call erfa
    for each individual timestamp but interpolates linearly
    between support points.

    For the interpolation, float64 MJD values are used, so time precision
    for the interpolation will be around a microsecond.

    This can dramatically speed up coordinate transformations,
    e.g. between CIRS and ICRS,
    when obstime is an array of many values (factors of 10 to > 100 depending
    on the selected resolution, number of points and the time range of the values).

    The precision of the transformation will still be in the order of microseconds
    for reasonable values of time_resolution, e.g. ``300 * u.s``.

    Users should benchmark performance and accuracy with the default transformation
    for their specific use case and then choose a suitable ``time_resolution``
    from there.

    This class is intended be used together with the ``erfa_astrom`` science state,
    e.g. in a context manager like this

    Example
    -------
    >>> from astropy.coordinates import SkyCoord, CIRS
    >>> from astropy.coordinates.erfa_astrom import erfa_astrom, ErfaAstromInterpolator
    >>> import astropy.units as u
    >>> from astropy.time import Time
    >>> import numpy as np

    >>> obstime = Time('2010-01-01T20:00:00') + np.linspace(0, 4, 1000) * u.hour
    >>> crab = SkyCoord(ra='05h34m31.94s', dec='22d00m52.2s')
    >>> with erfa_astrom.set(ErfaAstromInterpolator(300 * u.s)):
    ...    cirs = crab.transform_to(CIRS(obstime=obstime))
    )time_resolutionc                 C   s:   | tjdk r(td| jj dt | tj| _	d S )N
   zUsing z with `time_resolution` below 10 microseconds might lead to numerical inaccuracies as the MJD-based interpolation is limited by floating point  precision to about a microsecond of precision)
r   r   uswarningswarn	__class__rK   r   daymjd_resolution)selfrQ   r8   r8   r9   __init__   s    zErfaAstromInterpolator.__init__c                 C   sH   t |j| j }t t t |t |g}t|| j d|j	dS )aH  
        Calculate support points for the interpolation.

        We divide the MJD by the time resolution (as single float64 values),
        and calculate ceil and floor.
        Then we take the unique and sorted values and scale back to MJD.
        This will create a sparse support for non-regular input obstimes.
        mjd)formatscale)
rD   Zravelr[   rX   uniqueZconcatenateZfloorZceilr   r]   )rY   r   Z
mjd_scaledZmjd_ur8   r8   r9   _get_support_points   s    	z*ErfaAstromInterpolator._get_support_pointsc              	   C   s   t | \}}tj|jtjd}t|jd }tdD ]Z}dD ].}t|j| j|| d|f || d|f< q>t|j| j|d|f |d|f< q6||fS )z
        Calculate Earth's position and velocity.

        Uses the coarser grid ``support`` to do the calculation, and interpolates
        onto the finer grid ``obstime``.
        r@   )   r`   pv.)	r   rD   emptyrE   r   Zdt_pvrangeinterpr[   )supportr   Z
pv_supportZheliocentric_supportr4   r5   Zdimkeyr8   r8   r9   _prepare_earth_position_vel   s    z2ErfaAstromInterpolator._prepare_earth_position_velc              
   C   sp   t | d\}}t||}t|jd }tdD ]8}tdD ]*}t|j| j|d||f |d||f< q>q2|S )z
        Calculate the Celestial-to-Intermediate rotation matrix.

        Uses the coarser grid ``support`` to do the calculation, and interpolates
        onto the finer grid ``obstime``.
        r   )r`   r`   r`   .)	r   r   Zc2i06arD   rb   rE   rc   rd   r[   )re   r   jd1_tt_supportjd2_tt_supportZc2i_supportZc2iZdim1Zdim2r8   r8   r9   _get_c2i  s    *zErfaAstromInterpolator._get_c2ic                    s0   t d\}}t||}t fdd|D S )z
        Find the X, Y coordinates of the CIP and the CIO locator, s.

        Uses the coarser grid ``support`` to do the calculation, and interpolates
        onto the finer grid ``obstime``.
        r   c                 3   s    | ]}t  jj|V  qd S NrD   rd   r[   ).0Zcip_componentr   re   r8   r9   	<genexpr>'  s   z2ErfaAstromInterpolator._get_cip.<locals>.<genexpr>)r   r   tuple)re   r   rh   ri   Zcip_supportr8   rn   r9   _get_cip  s
    
zErfaAstromInterpolator._get_cipc                    s    t }t fdd|D S )z
        Find the two polar motion components in radians

        Uses the coarser grid ``support`` to do the calculation, and interpolates
        onto the finer grid ``obstime``.
        c                 3   s    | ]}t  jj|V  qd S rk   rl   )rm   Zpolar_motion_componentrn   r8   r9   ro   5  s   z;ErfaAstromInterpolator._get_polar_motion.<locals>.<genexpr>)r	   rp   )re   r   Zpolar_motion_supportr8   rn   r9   _get_polar_motion,  s    z(ErfaAstromInterpolator._get_polar_motionc                 C   s  |j d\}}}|j}| |}t|d\}}| ||\}	}
| ||\}}t||}| 	||\}}}tj
t|d }t|drt|jtj|jtj|jj|jtj\}}nd\}}t|||	|
|||||tj|tj|tj|||||S r   )r   r   r   r_   r   rg   rr   r   r   rq   r   r   r   r   r   r   r   r   r   r    r!   r"   r#   r$   r%   r&   )rY   r'   r(   r)   r*   r   re   r+   r,   r4   r5   r-   r.   r/   r0   r1   r2   r3   r6   r7   r8   r8   r9   r$   :  s0    






zErfaAstromInterpolator.apcoc           	      C   sb   |j }| |}| ||\}}t|jjddj|jjddj}t|d\}}t	
|||||S )a  
        Wrapper for ``erfa.apci``, used in conversions GCRS <-> ICRS

        Parameters
        ----------
        frame_or_coord : ``astropy.coordinates.BaseCoordinateFrame`` or ``astropy.coordinates.SkyCoord``
            Frame or coordinate instance in the corresponding frame
            for which to calculate the calculate the astrom values.
            For this function, a GCRS frame is expected.
        r:   r;   r   )r   r_   rg   r
   r<   r=   r!   r>   r   r   r?   )	rY   r'   r   re   r4   r5   ra   r+   r,   r8   r8   r9   r?   h  s    
zErfaAstromInterpolator.apcsN)rK   rL   rM   rN   r   Zquantity_inputrW   rZ   r_   rO   rg   rj   rq   rr   r$   r?   r8   r8   r8   r9   rP      s   %




.rP   c                   @   s"   e Zd ZdZe Zedd ZdS )erfa_astromz`
    ScienceState to select with astrom provider is used in
    coordinate transformations.
    c                 C   s   t |tstdt|S )NzMust be an instance of )
isinstancer   	TypeError)clsr!   r8   r8   r9   validate  s    
zerfa_astrom.validateN)rK   rL   rM   rN   r   Z_valueclassmethodrw   r8   r8   r8   r9   rs     s   rs   )rN   rT   ZnumpyrD   r   Zastropy.timer   Zastropy.utils.stater   Zastropy.unitsZunitsr   Zastropy.utils.exceptionsr   Zbuiltin_frames.utilsr   r   r   r	   r
   Zmatrix_utilitiesr   __all__r   rP   rs   r8   r8   r8   r9   <module>   s     Y