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)
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)
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
class MyFrame(BaseCoordinateFrame): a = QuantityAttribute(unit=u.m)
class MyFrame(BaseCoordinateFrame): default_representation = CartesianRepresentation my_attr = QuantityAttribute(default=0, unit=u.m)
class MyCoord(BaseCoordinateFrame): someval = QuantityAttribute()
class MyCoord2(BaseCoordinateFrame): someval = QuantityAttribute(15 * u.deg)
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
class MyCoord3(BaseCoordinateFrame): someval = QuantityAttribute(unit=u.arcsec, shape=(3,))