예제 #1
0
class CameraFrame(BaseCoordinateFrame):
    """
    Camera coordinate frame.

    The camera frame is a 2d cartesian frame,
    describing position of objects in the focal plane of the telescope.

    The frame is defined as in H.E.S.S., starting at the horizon,
    the telescope is pointed to magnetic north in azimuth and then up to zenith.

    Now, x points north and y points west, so in this orientation, the
    camera coordinates line up with the CORSIKA ground coordinate system.

    MAGIC and FACT use a different camera coordinate system:
    Standing at the dish, looking at the camera, x points right, y points up.
    To transform MAGIC/FACT to ctapipe, do x' = -y, y' = -x.

    Attributes
    ----------

    focal_length : u.Quantity[length]
        Focal length of the telescope as a unit quantity (usually meters)
    rotation : u.Quantity[angle]
        Rotation angle of the camera (0 deg in most cases)
    telescope_pointing : SkyCoord[AltAz]
        Pointing direction of the telescope as SkyCoord in AltAz
    obstime : Time
        Observation time
    location : EarthLocation
        location of the telescope
    """

    default_representation = PlanarRepresentation

    focal_length = QuantityAttribute(default=0, unit=u.m)
    rotation = QuantityAttribute(default=0 * u.deg, unit=u.rad)
    telescope_pointing = CoordinateAttribute(frame=AltAz, default=None)

    obstime = TimeAttribute(default=None)
    location = EarthLocationAttribute(default=None)
예제 #2
0
class PulsarEcliptic(coord.BaseCoordinateFrame):
    """A Pulsar Ecliptic coordinate system is defined by rotating ICRS coordinate
    about x-axis by obliquity angle. Historical, This coordinate is used by
    tempo/tempo2 for a better fitting error treatment.
    The obliquity angle values respect to time are given in the file named "ecliptic.dat"
    in the pint/datafile directory.
    Parameters
    ----------
    representation : `BaseRepresentation` or None
        A representation object or None to have no data (or use the other keywords)
    Lambda : `Angle`, optional, must be keyword
        The longitude-like angle corresponding to Sagittarius' orbit.
    Beta : `Angle`, optional, must be keyword
        The latitude-like angle corresponding to Sagittarius' orbit.
    distance : `Quantity`, optional, must be keyword
        The Distance for this object along the line-of-sight.
    """

    default_representation = coord.SphericalRepresentation
    # NOTE: The feature below needs astropy verison 2.0. Disable it right now
    default_differential = coord.SphericalCosLatDifferential
    obliquity = QuantityAttribute(default=OBL["DEFAULT"], unit=u.arcsec)

    def __init__(self, *args, **kwargs):
        # Allow using 'pm_lat' and 'pm_lon_coslat' keywords under astropy 2.
        # This matches the behavior of the built-in frames in astropy 2,
        # and the behavior of custom frames in astropy 3+.
        if int(astropy_version.split(".")[0]) <= 2:
            try:
                kwargs["d_lon_coslat"] = kwargs["pm_lon_coslat"]
                del kwargs["pm_lon_coslat"]
            except KeyError:
                pass
            try:
                kwargs["d_lat"] = kwargs["pm_lat"]
                del kwargs["pm_lat"]
            except KeyError:
                pass

        if "ecl" in kwargs:
            try:
                kwargs["obliquity"] = OBL[kwargs["ecl"]]
            except KeyError:
                raise ValueError(
                    "No obliquity " + kwargs["ecl"] + " provided. "
                    "Check your pint/datafile/ecliptic.dat file."
                )
            del kwargs["ecl"]

        super(PulsarEcliptic, self).__init__(*args, **kwargs)
예제 #3
0
class BaseHeliographic(SunPyBaseCoordinateFrame):
    """
    Base class for HeliographicCarrington (HGC) and HeliographicStonyhurst (HGS) frames.

    This class is not intended to be used directly and has no transformations defined.
    """
    frame_specific_representation_info = {
        SphericalRepresentation: [
            RepresentationMapping('lon', 'lon', u.deg),
            RepresentationMapping('lat', 'lat', u.deg),
            RepresentationMapping('distance', 'radius', None)
        ],
        SphericalDifferential: [
            RepresentationMapping('d_lon', 'd_lon', u.arcsec / u.s),
            RepresentationMapping('d_lat', 'd_lat', u.arcsec / u.s),
            RepresentationMapping('d_distance', 'd_radius', u.km / u.s)
        ],
    }

    rsun = QuantityAttribute(default=_RSUN, unit=u.km)

    def make_3d(self):
        """
        Returns a fully 3D coordinate based on this coordinate.

        If this coordinate is only 2D (i.e., no ``radius`` component) or is a
        unit vector (i.e., the norm of the coordinate is unity), a new
        coordinate is created that corresponds to the surface of the Sun.
        That is, the 3D coordinate will retain the ``lon`` and ``lat``, and
        ``radius`` will be set to the frame's ``rsun`` frame attribute.

        If this coordinate is already fully 3D, it is directly returned, even
        if it does not lie on the surface of the Sun.

        Returns
        -------
        frame : `~sunpy.coordinates.frames.BaseHeliographic`
            The fully 3D coordinate
        """
        # Check if the coordinate is 2D
        if (self._data is not None and self._data.norm().unit is u.one
                and u.allclose(self._data.norm(), 1 * u.one)):
            return self.realize_frame(self._data * self.rsun)

        # The coordinate is already 3D
        return self
예제 #4
0
 class MyFrame(BaseCoordinateFrame):
     a = QuantityAttribute(unit=u.m)
예제 #5
0
 class MyFrame(BaseCoordinateFrame):
     default_representation = CartesianRepresentation
     my_attr = QuantityAttribute(default=0, unit=u.m)
예제 #6
0
 class MyCoord(BaseCoordinateFrame):
     someval = QuantityAttribute()
예제 #7
0
 class MyCoord2(BaseCoordinateFrame):
     someval = QuantityAttribute(15 * u.deg)
예제 #8
0
class Helioprojective(SunPyBaseCoordinateFrame):
    """
    A coordinate or frame in the Helioprojective Cartesian (HPC) system, which is observer-based.

    - The origin is the location of the observer.
    - ``Tx`` (aka "theta_x") is the angle relative to the plane containing the Sun-observer line
      and the Sun's rotation axis, with positive values in the direction of the Sun's west limb.
    - ``Ty`` (aka "theta_y") is the angle relative to the Sun's equatorial plane, with positive
      values in the direction of the Sun's north pole.
    - ``distance`` is the Sun-observer distance.

    This system is frequently used in a projective form without ``distance`` specified.  For
    observations looking very close to the center of the Sun, where the small-angle approximation
    is appropriate, ``Tx`` and ``Ty`` can be approximated as Cartesian components.

    A new instance can be created using the following signatures
    (note that if supplied, ``obstime`` and ``observer`` must be keyword arguments)::

        Helioprojective(Tx, Ty, obstime=obstime, observer=observer)
        Helioprojective(Tx, Ty, distance, obstime=obstime, observer=observer)

    Parameters
    ----------
    {data}
    Tx : `~astropy.coordinates.Angle` or `~astropy.units.Quantity`
        The theta_x coordinate for this object. Not needed if ``data`` is given.
    Ty : `~astropy.coordinates.Angle` or `~astropy.units.Quantity`
        The theta_y coordinate for this object. Not needed if ``data`` is given.
    distance : `~astropy.units.Quantity`
        The distance coordinate from the observer for this object.
        Not needed if ``data`` is given.
    {observer}
    {rsun}
    {common}

    Examples
    --------
    >>> from astropy.coordinates import SkyCoord
    >>> import sunpy.coordinates
    >>> import astropy.units as u
    >>> sc = SkyCoord(0*u.deg, 0*u.deg, 5*u.km,
    ...               obstime="2010/01/01T00:00:00", observer="earth", frame="helioprojective")
    >>> sc
    <SkyCoord (Helioprojective: obstime=2010-01-01T00:00:00.000, rsun=695700.0 km, observer=<HeliographicStonyhurst Coordinate for 'earth'>): (Tx, Ty, distance) in (arcsec, arcsec, km)
        (0., 0., 5.)>
    >>> sc = SkyCoord(0*u.deg, 0*u.deg,
    ...               obstime="2010/01/01T00:00:00", observer="earth", frame="helioprojective")
    >>> sc
    <SkyCoord (Helioprojective: obstime=2010-01-01T00:00:00.000, rsun=695700.0 km, observer=<HeliographicStonyhurst Coordinate for 'earth'>): (Tx, Ty) in arcsec
        (0., 0.)>
    >>> sc = SkyCoord(CartesianRepresentation(1*u.AU, 1e5*u.km, -2e5*u.km),
    ...               obstime="2011/01/05T00:00:50", observer="earth", frame="helioprojective")
    >>> sc
    <SkyCoord (Helioprojective: obstime=2011-01-05T00:00:50.000, rsun=695700.0 km, observer=<HeliographicStonyhurst Coordinate for 'earth'>): (Tx, Ty, distance) in (arcsec, arcsec, AU)
        (137.87948623, -275.75878762, 1.00000112)>
    """
    frame_specific_representation_info = {
        SphericalRepresentation: [RepresentationMapping('lon', 'Tx', u.arcsec),
                                  RepresentationMapping('lat', 'Ty', u.arcsec),
                                  RepresentationMapping('distance', 'distance', None)],
        SphericalDifferential: [RepresentationMapping('d_lon', 'd_Tx', u.arcsec/u.s),
                                RepresentationMapping('d_lat', 'd_Ty', u.arcsec/u.s),
                                RepresentationMapping('d_distance', 'd_distance', u.km/u.s)],
        UnitSphericalRepresentation: [RepresentationMapping('lon', 'Tx', u.arcsec),
                                      RepresentationMapping('lat', 'Ty', u.arcsec)],
    }

    rsun = QuantityAttribute(default=_RSUN, unit=u.km)
    observer = ObserverCoordinateAttribute(HeliographicStonyhurst)

    @property
    def angular_radius(self):
        """
        Angular radius of the Sun as seen by the observer.

        The ``rsun`` frame attribute is the radius of the Sun in length units.
        The tangent vector from the observer to the edge of the Sun forms a
        right-angle triangle with the radius of the Sun as the far side and the
        Sun-observer distance as the hypotenuse. Thus, the sine of the angular
        radius of the Sun is ratio of these two distances.
        """
        from sunpy.coordinates.sun import _angular_radius  # avoiding a circular import

        if not isinstance(self.observer, HeliographicStonyhurst):
            if self.observer is None:
                raise ValueError("The observer must be defined, not `None`.")
            raise ValueError("The observer must be fully defined by specifying `obstime`.")
        return _angular_radius(self.rsun, self.observer.radius)

    def make_3d(self):
        """
        This method calculates the third coordinate of the Helioprojective
        frame. It assumes that the coordinate point is on the surface of the Sun.

        If a point in the frame is off limb then NaN will be returned.

        Returns
        -------
        new_frame : `~sunpy.coordinates.frames.Helioprojective`
            A new frame instance with all the attributes of the original but
            now with a third coordinate.
        """
        # Skip if we already are 3D
        if not self._is_2d:
            return self

        if not isinstance(self.observer, BaseCoordinateFrame):
            raise ConvertError("Cannot calculate distance to the Sun "
                               f"for observer '{self.observer}' "
                               "without `obstime` being specified.")

        rep = self.represent_as(UnitSphericalRepresentation)
        lat, lon = rep.lat, rep.lon

        # Check for the use of floats with lower precision than the native Python float
        if not set([lon.dtype.type, lat.dtype.type]).issubset([float, np.float64, np.longdouble]):
            raise SunpyUserWarning("The Helioprojective component values appear to be lower "
                                   "precision than the native Python float: "
                                   f"Tx is {lon.dtype.name}, and Ty is {lat.dtype.name}. "
                                   "To minimize precision loss, you may want to cast the values to "
                                   "`float` or `numpy.float64` via the NumPy method `.astype()`.")

        # Calculate the distance to the surface of the Sun using the law of cosines
        cos_alpha = np.cos(lat) * np.cos(lon)
        c = self.observer.radius**2 - self.rsun**2
        b = -2 * self.observer.radius * cos_alpha
        # Ignore sqrt of NaNs
        with np.errstate(invalid='ignore'):
            d = ((-1*b) - np.sqrt(b**2 - 4*c)) / 2  # use the "near" solution

        if self._spherical_screen:
            sphere_center = self._spherical_screen['center'].transform_to(self).cartesian
            c = sphere_center.norm()**2 - self._spherical_screen['radius']**2
            b = -2 * sphere_center.dot(rep)
            # Ignore sqrt of NaNs
            with np.errstate(invalid='ignore'):
                dd = ((-1*b) + np.sqrt(b**2 - 4*c)) / 2  # use the "far" solution

            d = np.fmin(d, dd) if self._spherical_screen['only_off_disk'] else dd

        return self.realize_frame(SphericalRepresentation(lon=lon,
                                                          lat=lat,
                                                          distance=d))

    _spherical_screen = None

    @classmethod
    @contextmanager
    def assume_spherical_screen(cls, center, only_off_disk=False):
        """
        Context manager to interpret 2D coordinates as being on the inside of a spherical screen.

        The radius of the screen is the distance between the specified ``center`` and Sun center.
        This ``center`` does not have to be the same as the observer location for the coordinate
        frame.  If they are the same, then this context manager is equivalent to assuming that the
        helioprojective "zeta" component is zero.

        This replaces the default assumption where 2D coordinates are mapped onto the surface of the
        Sun.

        Parameters
        ----------
        center : `~astropy.coordinates.SkyCoord`
            The center of the spherical screen
        only_off_disk : `bool`, optional
            If `True`, apply this assumption only to off-disk coordinates, with on-disk coordinates
            still mapped onto the surface of the Sun.  Defaults to `False`.

        Examples
        --------

        .. minigallery:: sunpy.coordinates.Helioprojective.assume_spherical_screen

        >>> import astropy.units as u
        >>> from sunpy.coordinates import Helioprojective
        >>> h = Helioprojective(range(7)*u.arcsec*319, [0]*7*u.arcsec,
        ...                     observer='earth', obstime='2020-04-08')
        >>> print(h.make_3d())
        <Helioprojective Coordinate (obstime=2020-04-08T00:00:00.000, rsun=695700.0 km, observer=<HeliographicStonyhurst Coordinate for 'earth'>): (Tx, Ty, distance) in (arcsec, arcsec, AU)
            [(   0., 0., 0.99660825), ( 319., 0., 0.99687244),
             ( 638., 0., 0.99778472), ( 957., 0., 1.00103285),
             (1276., 0.,        nan), (1595., 0.,        nan),
             (1914., 0.,        nan)]>

        >>> with Helioprojective.assume_spherical_screen(h.observer):
        ...     print(h.make_3d())
        <Helioprojective Coordinate (obstime=2020-04-08T00:00:00.000, rsun=695700.0 km, observer=<HeliographicStonyhurst Coordinate for 'earth'>): (Tx, Ty, distance) in (arcsec, arcsec, AU)
            [(   0., 0., 1.00125872), ( 319., 0., 1.00125872),
             ( 638., 0., 1.00125872), ( 957., 0., 1.00125872),
             (1276., 0., 1.00125872), (1595., 0., 1.00125872),
             (1914., 0., 1.00125872)]>

        >>> with Helioprojective.assume_spherical_screen(h.observer, only_off_disk=True):
        ...     print(h.make_3d())
        <Helioprojective Coordinate (obstime=2020-04-08T00:00:00.000, rsun=695700.0 km, observer=<HeliographicStonyhurst Coordinate for 'earth'>): (Tx, Ty, distance) in (arcsec, arcsec, AU)
            [(   0., 0., 0.99660825), ( 319., 0., 0.99687244),
             ( 638., 0., 0.99778472), ( 957., 0., 1.00103285),
             (1276., 0., 1.00125872), (1595., 0., 1.00125872),
             (1914., 0., 1.00125872)]>
        """
        try:
            old_spherical_screen = cls._spherical_screen  # nominally None

            center_hgs = center.transform_to(HeliographicStonyhurst(obstime=center.obstime))
            cls._spherical_screen = {
                'center': center,
                'radius': center_hgs.radius,
                'only_off_disk': only_off_disk
            }
            yield
        finally:
            cls._spherical_screen = old_spherical_screen
예제 #9
0
 class MyCoord3(BaseCoordinateFrame):
     someval = QuantityAttribute(unit=u.arcsec, shape=(3,))