a
    ߙfbp|                     @   s  d dl Z d dlmZ d dlmZ d dlZd dlm	Z	 d dl
mZmZmZmZ d dlmZ d dlmZmZ d dlmZ dgZG d	d
 d
eZG dd deZejej Ze	eZeg de Zdej Z dgZ!dd Z"dd Z#dd Z$dddZ%dd Z&dd Z'G dd deZ(dS )    N)indent)c)ICRSCartesianDifferentialCartesianRepresentationSkyCoord)SpectralQuantity)BaseCoordinateFrameframe_transform_graph)AstropyUserWarningSpectralCoordc                   @   s   e Zd ZdS )NoVelocityWarningN__name__
__module____qualname__ r   r   Flib/python3.9/site-packages/astropy/coordinates/spectral_coordinate.pyr      s   r   c                   @   s   e Zd ZdS )NoDistanceWarningNr   r   r   r   r   r      s   r   )r   r   r   g    .AzSpectralCoord.*c                 C   s"   | t  }td| d|  d S )z8
    Convert a velocity to a relativistic redshift.
       )C_KMSnpsqrt)velocitybetar   r   r   _velocity_to_redshift%   s    r   c                 C   s    d|  d }t |d  |d  S )z8
    Convert a relativistic redshift to a velocity.
    r      )r   )redshiftZzponesqr   r   r   _redshift_to_velocity-   s    r   c                 C   s   |  t}|t }td| d|  }|jtjr>|| S |jtj	sl|jtj
sl|jdtj rt|| S |jtr|tj	| |jS td|j ddS )a  
    Given a `SpectralQuantity` and a velocity, return a new `SpectralQuantity`
    that is Doppler shifted by this amount.

    Note that the Doppler shift applied is the full relativistic one, so
    `SpectralQuantity` currently expressed in velocity and not using the
    relativistic convention will temporarily be converted to use the
    relativistic convention while the shift is applied.

    Positive velocities are assumed to redshift the spectral quantity,
    while negative velocities blueshift the spectral quantity.
    r   z$Unexpected units in velocity shift: zM. This should not happen, so please report this in the astropy issue tracker!N)viewr   r   r   r   unitis_equivalentumZHzZeVKMStoRuntimeError)Zscoordr   Z	squantityr   Zdoppler_factorr   r   r   !_apply_relativistic_doppler_shift5   s    
r'   Fc           	      C   s   |j jstdd|jv r2t| dr2|j| jd}| t }|t }|j 	t
tj}|j 	t
|}||}|r|| }n
||}|jt
tdS )a  
    Given an original coordinate object, update the differentials so that
    the final coordinate is at the same location as the original coordinate
    but co-moving with the velocity reference object.

    If preserve_original_frame is set to True, the resulting object will be in
    the frame of the original coordinate, otherwise it will be in the frame of
    the velocity reference.
    z!Reference frame has no velocitiesobstime)r(   Zrepresentation_typeZdifferential_type)datadifferentials
ValueErrorZframe_attributeshasattr	replicater(   transform_tor   Zrepresent_asr   r   with_differentialsrealize_frame)	ZoriginalZvelocity_referencepreserve_observer_frameZoriginal_icrsZvelocity_reference_icrsr+   Zdata_with_differentialsZ
final_icrsfinalr   r   r   update_differentials_to_matchX   s&    

r4   c                 C   s   | j t}| |S )zH
    Set the differentials to be stationary on a coordinate object.
    )	cartesianr0   ZERO_VELOCITIESr1   )coordnew_datar   r   r   attach_zero_velocities   s    r9   c                 C   s   d| j jv r| jS tS d S )Ns)r*   r+   r   r6   )r7   r   r   r   _get_velocities   s    r;   c                	       s  e Zd ZdZejejej dd' fdd	Z fddZ	e
d(d	d
Zd)ddZedd Zedd Zejdd Zedd Zejdd Zedd Zedd Ze
d*ddZe
dd Zejejej dd+dd Zd,d!d"Zd#d$ Zd%d& Z  ZS )-r   a  
    A spectral coordinate with its corresponding unit.

    .. note:: The |SpectralCoord| class is new in Astropy v4.1 and should be
              considered experimental at this time. Note that we do not fully
              support cases where the observer and target are moving
              relativistically relative to each other, so care should be taken
              in those cases. It is possible that there will be API changes in
              future versions of Astropy based on user feedback. If you have
              specific ideas for how it might be improved, please  let us know
              on the `astropy-dev mailing list`_ or at
              http://feedback.astropy.org.

    Parameters
    ----------
    value : ndarray or `~astropy.units.Quantity` or `SpectralCoord`
        Spectral values, which should be either wavelength, frequency,
        energy, wavenumber, or velocity values.
    unit : unit-like
        Unit for the given spectral values.
    observer : `~astropy.coordinates.BaseCoordinateFrame` or `~astropy.coordinates.SkyCoord`, optional
        The coordinate (position and velocity) of observer. If no velocities
        are present on this object, the observer is assumed to be stationary
        relative to the frame origin.
    target : `~astropy.coordinates.BaseCoordinateFrame` or `~astropy.coordinates.SkyCoord`, optional
        The coordinate (position and velocity) of target. If no velocities
        are present on this object, the target is assumed to be stationary
        relative to the frame origin.
    radial_velocity : `~astropy.units.Quantity` ['speed'], optional
        The radial velocity of the target with respect to the observer. This
        can only be specified if ``redshift`` is not specified.
    redshift : float, optional
        The relativistic redshift of the target with respect to the observer.
        This can only be specified if ``radial_velocity`` cannot be specified.
    doppler_rest : `~astropy.units.Quantity`, optional
        The rest value to use when expressing the spectral value as a velocity.
    doppler_convention : str, optional
        The Doppler convention to use when expressing the spectral value as a velocity.
    )radial_velocityNc           	         s   t  j| |fd|i|}|d urB|d urB|d us:|d urBtd|d ur|d urZtdt|}|jtjs|tdt	|}|d u rt
|dd }|d u rt
|dd }|d u s|d u r|d u rt
|dd }||_| j|dd|_| j|dd|_|S )	Nr    zTCannot specify radial velocity or redshift if both target and observer are specifiedz.Cannot set both a radial velocity and redshiftz redshift should be dimensionlessobservertargetr<   label)super__new__r,   r"   Quantityr    r!   one
UnitsErrorr   getattr_radial_velocity_validate_coordinate	_observer_target)	clsvaluer    r=   r>   r<   r   kwargsobj	__class__r   r   rB      s,    	

zSpectralCoord.__new__c                    s:   t  | t|dd | _t|dd | _t|dd | _d S )NrG   rI   rJ   )rA   __array_finalize__rF   rG   rI   rJ   )selfrN   rO   r   r   rQ      s    z SpectralCoord.__array_finalize__ c                 C   s   | du rdS t | jts8t| tr*| j} nt| dtjdd t	| dd}W d   n1 sf0    Y  |dur|j
jdkrt| td} tdt d	t d
| jjvrtdt dt t| } | S )ad  
        Checks the type of the frame and whether a velocity differential and a
        distance has been defined on the frame object.

        If no distance is defined, the target is assumed to be "really far
        away", and the observer is assumed to be "in the solar system".

        Parameters
        ----------
        coord : `~astropy.coordinates.BaseCoordinateFrame`
            The new frame to be used for target or observer.
        label : str, optional
            The name of the object being validated (e.g. 'target' or 'observer'),
            which is then used in error messages.
        Nz0 must be a SkyCoord or coordinate frame instanceignore)alldistancedimensionless)rV   zODistance on coordinate object is dimensionless, an arbitrary distance value of z will be set instead.r:   z'No velocity defined on frame, assuming .)
issubclassrP   r	   
isinstancer   frame	TypeErrorr   ZerrstaterF   r    physical_typeDEFAULT_DISTANCEwarningswarnr   r*   r+   r6   r   r9   )r7   r@   rV   r   r   r   rH      s.    
*
z"SpectralCoord._validate_coordinateFc
           
      C   s  t |tjr,|durtdn|j|j }}|dur8|n| j}|pF| j}| |pV| j}| |pf| j}|pp| j	}|pz| j
}|	r| }| jdu s| jdu r|du r|du r| j}t 8 tdt | j||||||||dd	W  d   S 1  s0    Y  dS )a  
        Return a replica of the `SpectralCoord`, optionally changing the
        values or attributes.

        Note that no conversion is carried out by this method - this keeps
        all the values and attributes the same, except for the ones explicitly
        passed to this method which are changed.

        If ``copy`` is set to `True` then a full copy of the internal arrays
        will be made.  By default the replica will use a reference to the
        original arrays when possible to save memory.

        Parameters
        ----------
        value : ndarray or `~astropy.units.Quantity` or `SpectralCoord`, optional
            Spectral values, which should be either wavelength, frequency,
            energy, wavenumber, or velocity values.
        unit : unit-like
            Unit for the given spectral values.
        observer : `~astropy.coordinates.BaseCoordinateFrame` or `~astropy.coordinates.SkyCoord`, optional
            The coordinate (position and velocity) of observer.
        target : `~astropy.coordinates.BaseCoordinateFrame` or `~astropy.coordinates.SkyCoord`, optional
            The coordinate (position and velocity) of target.
        radial_velocity : `~astropy.units.Quantity` ['speed'], optional
            The radial velocity of the target with respect to the observer.
        redshift : float, optional
            The relativistic redshift of the target with respect to the observer.
        doppler_rest : `~astropy.units.Quantity`, optional
            The rest value to use when expressing the spectral value as a velocity.
        doppler_convention : str, optional
            The Doppler convention to use when expressing the spectral value as a velocity.
        copy : bool, optional
            If `True`, and ``value`` is not specified, the values are copied to
            the new `SkyCoord` - otherwise a reference to the same values is used.

        Returns
        -------
        sc : `SpectralCoord` object
            Replica of this object
        Nz8Cannot specify value as a Quantity and also specify unitrT   F)	rL   r    r=   r>   r<   r   doppler_conventiondoppler_restcopy)rZ   r"   rC   r,   rL   r    rH   r=   r>   ra   rb   rc   r<   r_   catch_warningssimplefilterr   rP   )
rR   rL   r    r=   r>   r<   r   ra   rb   rc   r   r   r   r.   ,  s*    .



$
zSpectralCoord.replicatec                 C   s   |  tjS )a  
        Convert the ``SpectralCoord`` to a `~astropy.units.Quantity`.
        Equivalent to ``self.view(u.Quantity)``.

        Returns
        -------
        `~astropy.units.Quantity`
            This object viewed as a `~astropy.units.Quantity`.

        )r   r"   rC   rR   r   r   r   quantityw  s    zSpectralCoord.quantityc                 C   s   | j S )a>  
        The coordinates of the observer.

        If set, and a target is set as well, this will override any explicit
        radial velocity passed in.

        Returns
        -------
        `~astropy.coordinates.BaseCoordinateFrame`
            The astropy coordinate frame representing the observation.
        )rI   rf   r   r   r   r=     s    zSpectralCoord.observerc                 C   s6   | j d urtd| j|dd| _| jd ur2d | _d S )Nzobserver has already been setr=   r?   )r=   r,   rH   rI   rJ   rG   rR   rL   r   r   r   r=     s
    

c                 C   s   | j S )aI  
        The coordinates of the target being observed.

        If set, and an observer is set as well, this will override any explicit
        radial velocity passed in.

        Returns
        -------
        `~astropy.coordinates.BaseCoordinateFrame`
            The astropy coordinate frame representing the target.
        )rJ   rf   r   r   r   r>     s    zSpectralCoord.targetc                 C   s6   | j d urtd| j|dd| _| jd ur2d | _d S )Nztarget has already been setr>   r?   )r>   r,   rH   rJ   rI   rG   rh   r   r   r   r>     s
    

c                 C   sF   | j du s| jdu r.| jdu r&dt S | jS n| j| j | jddS dS )a  
        Radial velocity of target relative to the observer.

        Returns
        -------
        `~astropy.units.Quantity` ['speed']
            Radial velocity of target.

        Notes
        -----
        This is different from the ``.radial_velocity`` property of a
        coordinate frame in that this calculates the radial velocity with
        respect to the *observer*, not the origin of the frame.
        Nr   T	as_scalar)rI   rJ   rG   r$   _calculate_radial_velocityrf   r   r   r   r<     s    
zSpectralCoord.radial_velocityc                 C   s
   t | jS )z
        Redshift of target relative to observer. Calculated from the radial
        velocity.

        Returns
        -------
        float
            Redshift of target.
        )r   r<   rf   r   r   r   r     s    zSpectralCoord.redshiftc                 C   sN   |  t }| t }t||}|j|j }||}|rB|S || S dS )az  
        Compute the line-of-sight velocity from the observer to the target.

        Parameters
        ----------
        observer : `~astropy.coordinates.BaseCoordinateFrame`
            The frame of the observer.
        target : `~astropy.coordinates.BaseCoordinateFrame`
            The frame of the target.
        as_scalar : bool
            If `True`, the magnitude of the velocity vector will be returned,
            otherwise the full vector will be returned.

        Returns
        -------
        `~astropy.units.Quantity` ['speed']
            The radial velocity of the target with respect to the observer.
        N)r/   r   r   _normalized_position_vectorr   dot)r=   r>   rj   observer_icrstarget_icrspos_hatZd_velZvel_magr   r   r   rk     s    
z(SpectralCoord._calculate_radial_velocityc                 C   s:   |j  | j   }| }d|j ||dk< || }|S )a  
        Calculate the normalized position vector between two frames.

        Parameters
        ----------
        observer : `~astropy.coordinates.BaseCoordinateFrame` or `~astropy.coordinates.SkyCoord`
            The observation frame or coordinate.
        target : `~astropy.coordinates.BaseCoordinateFrame` or `~astropy.coordinates.SkyCoord`
            The target frame or coordinate.

        Returns
        -------
        pos_hat : `BaseRepresentation`
            Position representation.
        r   r   )r5   Zwithout_differentialsZnormr    )r=   r>   Zd_posZdp_normrp   r   r   r   rl     s    z)SpectralCoord._normalized_position_vector)r   c                 C   s  | j du s| jdu rtdt|tr,|j}t|tr|js`|t	dt
j dt
j dt
j }|jjrz|durtdn(|du rt}nt|}||j|}t|ttfrRt|tr|}nt|trt|}|du rdt
j t
j dt
j t
j dt
j t
j f}n|jdkr$td|dt
j dt
j dt
j g|R ddd}t| j ||d	}| j| j | jd
d}| j|| jd
d}t| || }	| j|	|d}
|
S )aL  
        A new  `SpectralCoord` with the velocity of the observer altered,
        but not the position.

        If a coordinate frame is specified, the observer velocities will be
        modified to be stationary in the specified frame. If a coordinate
        instance is specified, optionally with non-zero velocities, the
        observer velocities will be updated so that the observer is co-moving
        with the specified coordinates.

        Parameters
        ----------
        frame : str, `~astropy.coordinates.BaseCoordinateFrame` or `~astropy.coordinates.SkyCoord`
            The observation frame in which the observer will be stationary. This
            can be the name of a frame (e.g. 'icrs'), a frame class, frame instance
            with no data, or instance with data. This can optionally include
            velocities.
        velocity : `~astropy.units.Quantity` or `~astropy.coordinates.CartesianDifferential`, optional
            If ``frame`` does not contain velocities, these can be specified as
            a 3-element `~astropy.units.Quantity`. In the case where this is
            also not specified, the velocities default to zero.
        preserve_observer_frame : bool
            If `True`, the final observer frame class will be the same as the
            original one, and if `False` it will be the frame of the velocity
            reference class.

        Returns
        -------
        new_coord : `SpectralCoord`
            The new coordinate object representing the spectral data
            transformed based on the observer's new velocity frame.
        NzZThis method can only be used if both observer and target are defined on the SpectralCoord.r   z=frame already has differentials, cannot also specify velocity)   z4velocity should be a Quantity vector with 3 elementsr5   r)   )r2   Tri   )rL   r=   )r=   r>   r,   rZ   r   r[   r	   Zhas_datar1   r   r"   kmr*   r+   r6   r   r0   typestrr
   Zlookup_namer#   r:   shaper4   rk   r'   r.   )rR   r[   r   r2   r+   Z	frame_clsr=   init_obs_velfin_obs_velr8   Z	new_coordr   r   r   $with_observer_stationary_relative_to#  sJ    #

$




0z2SpectralCoord.with_observer_stationary_relative_toc                 C   s  |dur$| j du s| jdu r$tddd ||fD D ],}t|tjr6|jtjt	fs6t
dq6|du r| jdu s| jdu r|  S dt	 }nLt|}|jjdkrt|}| jdu s| jdu r| jt| || j| dS |du rdt	 }n t|}|jjdkrt|}| jt }| jt }t||}t|||  }t|||  }t|j}t|j}||j|| j}	||j|| j}
| j||d	d
}| j|
|	d	d
}t| || }| j||
|	dS )a  
        Apply a velocity shift to this spectral coordinate.

        The shift can be provided as a redshift (float value) or radial
        velocity (`~astropy.units.Quantity` with physical type of 'speed').

        Parameters
        ----------
        target_shift : float or `~astropy.units.Quantity` ['speed']
            Shift value to apply to current target.
        observer_shift : float or `~astropy.units.Quantity` ['speed']
            Shift value to apply to current observer.

        Returns
        -------
        `SpectralCoord`
            New spectral coordinate with the target/observer velocity changed
            to incorporate the shift. This is always a new object even if
            ``target_shift`` and ``observer_shift`` are both `None`.
        NzMBoth an observer and target must be defined before applying a velocity shift.c                 S   s   g | ]}|d ur|qS )Nr   ).0xr   r   r   
<listcomp>      z<SpectralCoord.with_radial_velocity_shift.<locals>.<listcomp>zaArgument must have unit physical type 'speed' for radial velocty or 'dimensionless' for redshift.r   rW   )rL   r<   Tri   )rL   r=   r>   )r>   r=   r,   rZ   r"   rC   r    r!   rD   r$   rE   rI   rJ   r.   r]   r   r'   r<   r/   r   r   rl   r;   r   Zxyzr1   r5   r0   rk   )rR   Ztarget_shiftZobserver_shiftargro   rn   rp   Ztarget_velocityZobserver_velocity
new_targetZnew_observerrv   rw   r8   r   r   r   with_radial_velocity_shiftz  s^    







z(SpectralCoord.with_radial_velocity_shiftc                 C   sB   | j dur | jdur | | jS t| | j }| j|dt ddS )zA
        Transforms the spectral axis to the rest frame.
        Ng        )rL   r<   r   )r=   r>   rx   r'   r<   r.   r$   )rR   resultr   r   r   to_rest  s    zSpectralCoord.to_restc                 C   s  d| j j d }z| j}| j}W n ty:   d }}Y n0 | g}| jd urrtt| jd }|	d|  | j
d urtt| j
d }|	d|  | jd ur| jd us| jd ur
| jd ur| j
d ur|	d n
|	d	 |	d
|  |	d|  | jd us"| jd urF|	d| j  |	d| j  tj| tjddd}t|dkr|d  | | jd7  < n<d|d   |d< |d  d7  < |	d| | jd d|d S )N< Z	Undefinedz              z    observer: z            z    target: z-    observer to target (computed from above):z    observer to target:z      radial_velocity=z      redshift=z    doppler_rest=z    doppler_convention=z, z  )Z	separatorprefixr   r   r:   z   ()
>)rP   r   r<   r   r,   r=   r   reprlstripappendr>   rI   rJ   rG   rb   ra   r   Zarray2stringr   ZndarraylenZ_unitstrjoin)rR   Z	prefixstrr<   r   Z
repr_itemsZobserver_reprZtarget_reprZarrstrr   r   r   __repr__  s>    


 
zSpectralCoord.__repr__)NNNNN)rS   )	NNNNNNNNF)F)NF)NN)r   r   r   __doc__r"   Zquantity_inputrr   r:   rB   rQ   staticmethodrH   r.   propertyrg   r=   setterr>   r<   r   rk   rl   rx   r   r   r   __classcell__r   r   rO   r   r      sL   (   54     
K






$
V
S)F))r_   textwrapr   Zastropy.unitsZunitsr"   Znumpyr   Zastropy.constantsr   Zastropy.coordinatesr   r   r   r   Z%astropy.coordinates.spectral_quantityr   Zastropy.coordinates.baseframer	   r
   Zastropy.utils.exceptionsr   __all__r   r   rr   r:   r$   r%   r   r6   Zkpcr^   Z__doctest_skip__r   r   r'   r4   r9   r;   r   r   r   r   r   <module>   s.   

#
+