class CameraFrame(BaseCoordinateFrame): ''' Astropy CoordinateFrame representing coordinates in the CameraPlane Attributes ---------- pointing_direction: astropy.coordinates.AltAz The pointing direction of the telescope obstime: astropy.Time The timestamp of the observation, only needed to directly transform to Equatorial coordinates, transforming to AltAz does not need this. location: astropy.coordinates.EarthLocation The location of the observer, only needed to directly transform to Equatorial coordinates, transforming to AltAz does not need this, default is FACT's location rotated: bool True means x points right and y points up when looking on the camera from the dish, which is the efinition of FACT-Tools >= 1.0 and Mars. False means x points up and y points left, which is definition in the original FACTPixelMap file. ''' default_representation = PlanarRepresentation pointing_direction = CoordinateAttribute(frame=AltAz, default=None) obstime = TimeAttribute(default=None) location = EarthLocationAttribute(default=LOCATION) rotated = Attribute(default=True)
class ReferencePlaneFrame(coord.BaseCoordinateFrame): """A coordinate frame aligned with the reference plane coordinates of a Kepler orbit, centered on the barycenter or reference point of the orbit. See :ref:`celestial-reference-plane` for more information. ``ReferencePlaneFrame`` objects always have generic component names for spherical coordinates of ``lon``/``lat``, *not* the component names for the frame of the ``origin``. Parameters ---------- representation : `BaseRepresentation` or None A representation object or None to have no data (or use the other keywords). origin : `SkyCoord` or low-level coordinate object. The coordinate which specifies the origin of this frame. This is typically a sky position and a distance to the barycenter or reference point of the orbit at a particular epoch. Notes ----- ``ReferencePlaneFrame`` is a factory class. That is, the objects that it yields are *not* actually objects of class ``ReferencePlaneFrame``. Instead, distinct classes are created on-the-fly for whatever the frame class is of ``origin``. """ origin = CoordinateAttribute(default=None, frame=None) def __new__(cls, *args, **kwargs): # We don't want to call this method if we've already set up # an skyoffset frame for this class. if not (issubclass(cls, ReferencePlaneFrame) and cls is not ReferencePlaneFrame): # We get the origin argument, and handle it here. Default is ICRS: # the user might want to use arbitrary reference plane coordinates # without every transforming them origin_frame = kwargs.get('origin', ICRS()) if hasattr(origin_frame, 'frame'): origin_frame = origin_frame.frame newcls = _make_cls(origin_frame.__class__) return newcls.__new__(newcls, *args, **kwargs) # http://stackoverflow.com/questions/19277399/why-does-object-new-work-differently-in-these-three-cases # See above for why this is necessary. Basically, because some child # may override __new__, we must override it here to never pass # arguments to the object.__new__ method. if super().__new__ is object.__new__: return super().__new__(cls) return super().__new__(cls, *args, **kwargs) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if self.origin is not None and not self.origin.has_data: raise ValueError('The origin supplied to ReferencePlaneFrame has ' 'no data.') if self.has_data and hasattr(self.data, 'lon'): self.data.lon.wrap_angle = 180 * u.deg
class Heliocentric(BaseCoordinateFrame): """ A coordinate or frame in the Heliocentric system. This frame may either be specified in Cartesian or cylindrical representation. Cylindrical representation replaces (x, y) with (rho, psi) where rho is the impact parameter and psi is the position angle in degrees. Parameters ---------- representation: `~astropy.coordinates.BaseRepresentation` or None. A representation object. If specified, other parameters must be in keyword form and if x, y and z are specified, it must be None. x: `Quantity` object. X-axis coordinate, optional, must be keyword. y: `Quantity` object. Y-axis coordinate, optional, must be keyword. z: `Quantity` object. Shared by both representations. Z-axis coordinate, optional, must be keyword. D0: `Quantity` object. Represents the distance between the observer and the Sun center. Defaults to 1AU. Examples -------- >>> from astropy.coordinates import SkyCoord, CartesianRepresentation >>> import sunpy.coordinates >>> import astropy.units as u >>> sc = SkyCoord(CartesianRepresentation(10*u.km, 1*u.km, 2*u.km), ... dateobs="2011/01/05T00:00:50", frame="heliocentric") >>> sc <SkyCoord (HelioCentric): dateobs=2011-01-05 00:00:50, D0=149597870.7 km, x=10.0 km, y=1.0 km, z=2.0 km> >>> sc = SkyCoord([1,2]*u.km, [3,4]*u.m, [5,6]*u.cm, frame="heliocentric", dateobs="2011/01/01T00:00:54") >>> sc <SkyCoord (HelioCentric): dateobs=2011-01-01 00:00:54, D0=149597870.7 km, (x, y, z) in (km, m, cm) [(1.0, 3.0, 5.0), (2.0, 4.0, 6.0)]> """ default_representation = CartesianRepresentation _frame_specific_representation_info = { 'cylindrical': [RepresentationMapping('phi', 'psi', u.deg)] } dateobs = TimeFrameAttributeSunPy() observer = CoordinateAttribute(HeliographicStonyhurst, default=HeliographicStonyhurst( 0 * u.deg, 0 * u.deg, 1 * u.AU))
def __new__(cls, name, bases, members): # Only 'origin' is needed here, to set the origin frame properly. members['origin'] = CoordinateAttribute(frame=framecls, default=None) # This has to be done because FrameMeta will set these attributes # to the defaults from BaseCoordinateFrame when it creates the base # SkyOffsetFrame class initially. members[ '_default_representation'] = framecls._default_representation members['_default_differential'] = framecls._default_differential newname = name[:-5] if name.endswith('Frame') else name newname += framecls.__name__ return super().__new__(cls, newname, bases, members)
class TelescopeFrame(BaseCoordinateFrame): """ Telescope coordinate frame. A Frame using a UnitSphericalRepresentation. This is basically the same as a HorizonCoordinate, but the origin is at the telescope's pointing direction. This is used to specify coordinates in the field of view of a telescope that is independent of the optical properties of the telescope. ``fov_lon`` is aligned with azimuth and ``fov_lat`` is aligned with altitude of the horizontal coordinate frame as implemented in ``astropy.coordinates.AltAz``. This is what astropy calls a SkyOffsetCoordinate. Attributes ---------- telescope_pointing: SkyCoord[AltAz] Coordinate of the telescope pointing in AltAz obstime: Tiem Observation time location: EarthLocation Location of the telescope """ frame_specific_representation_info = { UnitSphericalRepresentation: [ RepresentationMapping("lon", "fov_lon"), RepresentationMapping("lat", "fov_lat"), ] } default_representation = UnitSphericalRepresentation telescope_pointing = CoordinateAttribute(default=None, frame=AltAz) obstime = TimeAttribute(default=None) location = EarthLocationAttribute(default=None) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # make sure telescope coordinate is in range [-180°, 180°] if isinstance(self._data, UnitSphericalRepresentation): self._data.lon.wrap_angle = Angle(180, unit=u.deg)
class TiltedGroundFrame(BaseCoordinateFrame): """Tilted ground coordinate frame. The tilted ground coordinate frame is a cartesian system describing the 2 dimensional projected positions of objects in a tilted plane described by pointing_direction Typically this frame will be used for the reconstruction of the shower core position Frame attributes: * ``pointing_direction`` Alt,Az direction of the tilted reference plane """ default_representation = PlanarRepresentation # Pointing direction of the tilted system (alt,az), # could be the telescope pointing direction or the reconstructed shower # direction pointing_direction = CoordinateAttribute(default=None, frame=AltAz)
class NominalFrame(BaseCoordinateFrame): """ Nominal coordinate frame. A Frame using a UnitSphericalRepresentation. This is basically the same as a HorizonCoordinate, but the origin is at an arbitray position in the sky. This is what astropy calls a SkyOffsetCoordinate If the telescopes are in divergent pointing, this Frame can be used to transform to a common system. Attributes ---------- origin: SkyCoord[AltAz] Origin of this frame as a HorizonCoordinate obstime: Tiem Observation time location: EarthLocation Location of the telescope """ frame_specific_representation_info = { UnitSphericalRepresentation: [ RepresentationMapping("lon", "fov_lon"), RepresentationMapping("lat", "fov_lat"), ] } default_representation = UnitSphericalRepresentation origin = CoordinateAttribute(default=None, frame=AltAz) obstime = TimeAttribute(default=None) location = EarthLocationAttribute(default=None) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # make sure telescope coordinate is in range [-180°, 180°] if isinstance(self._data, UnitSphericalRepresentation): self._data.lon.wrap_angle = Angle(180, unit=u.deg)
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 TelescopeFrame(BaseCoordinateFrame): ''' Telescope coordinate frame. A Frame using a UnitSphericalRepresentation. This is basically the same as a HorizonCoordinate, but the origin is at the telescope's pointing direction. This is what astropy calls a SkyOffsetCoordinate Attributes ---------- telescope_pointing: SkyCoord[HorizonFrame] Coordinate of the telescope pointing in HorizonFrame obstime: Tiem Observation time location: EarthLocation Location of the telescope ''' frame_specific_representation_info = { UnitSphericalRepresentation: [ RepresentationMapping('lon', 'delta_az'), RepresentationMapping('lat', 'delta_alt'), ] } default_representation = UnitSphericalRepresentation telescope_pointing = CoordinateAttribute(default=None, frame=HorizonFrame) obstime = TimeAttribute(default=None) location = EarthLocationAttribute(default=None) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # make sure telescope coordinate is in range [-180°, 180°] if isinstance(self._data, UnitSphericalRepresentation): self._data.lon.wrap_angle = Angle(180, unit=u.deg)
class Helioprojective(BaseCoordinateFrame): """ A coordinate or frame in the Helioprojective (Cartesian) system. This is a projective coordinate system centered around the observer. It is a full spherical coordinate system with position given as longitude theta_x and latitude theta_y. Parameters ---------- representation: `~astropy.coordinates.BaseRepresentation` or None. A representation object. If specified, other parameters must be in keyword form. Tx: `~astropy.coordinates.Angle` or `~astropy.units.Quantity` X-axis coordinate. Ty: `~astropy.coordinates.Angle` or `~astropy.units.Quantity` Y-axis coordinate. distance: `~astropy.units.Quantity` The radial distance from the observer to the coordinate point. L0: `~astropy.coordinates.Angle` The Heliographic (Stonyhurst) Longitude of the observer. B0: `~astropy.coordinates.Angle` or `~astropy.units.Quantity` The Heliographic (Stonyhurst) Latitude of the observer. D0: `Quantity` object. Represents the distance between observer and solar center. **Defaults to 1 AU**. 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, dateobs="2010/01/01T00:00:00", ... frame="helioprojective") >>> sc <SkyCoord (HelioProjective): dateobs=2010-01-01 00:00:00, D0=149597870.7 km , Tx=0.0 arcsec, Ty=0.0 arcsec, distance=5.0 km> >>> sc = SkyCoord(0*u.deg, 0*u.deg, dateobs="2010/01/01T00:00:00", frame="helioprojective") >>> sc <SkyCoord (HelioProjective): dateobs=2010-01-01 00:00:00, D0=149597870.7 km , Tx=0.0 arcsec, Ty=0.0 arcsec, distance=149597870.7 km> """ default_representation = SphericalWrap180Representation _frame_specific_representation_info = { 'spherical': [ RepresentationMapping('lon', 'Tx', u.arcsec), RepresentationMapping('lat', 'Ty', u.arcsec), RepresentationMapping('distance', 'distance', u.km) ], 'sphericalwrap180': [ RepresentationMapping('lon', 'Tx', u.arcsec), RepresentationMapping('lat', 'Ty', u.arcsec), RepresentationMapping('distance', 'distance', u.km) ], 'unitspherical': [ RepresentationMapping('lon', 'Tx', u.arcsec), RepresentationMapping('lat', 'Ty', u.arcsec) ], 'unitsphericalwrap180': [ RepresentationMapping('lon', 'Tx', u.arcsec), RepresentationMapping('lat', 'Ty', u.arcsec) ] } dateobs = TimeFrameAttributeSunPy() rsun = FrameAttribute(default=RSUN_METERS.to(u.km)) observer = CoordinateAttribute(HeliographicStonyhurst, default=HeliographicStonyhurst( 0 * u.deg, 0 * u.deg, 1 * u.AU)) def __init__(self, *args, **kwargs): _rep_kwarg = kwargs.get('representation', None) BaseCoordinateFrame.__init__(self, *args, **kwargs) # Convert from Spherical to SphericalWrap180 # If representation was explicitly passed, do not change the rep. if not _rep_kwarg: # The base __init__ will make this a UnitSphericalRepresentation # This makes it Wrap180 instead if isinstance(self._data, UnitSphericalRepresentation): self._data = UnitSphericalWrap180Representation( lat=self._data.lat, lon=self._data.lon) self.representation = UnitSphericalWrap180Representation # Make a Spherical Wrap180 instead elif isinstance(self._data, SphericalRepresentation): self._data = SphericalWrap180Representation( lat=self._data.lat, lon=self._data.lon, distance=self._data.distance) self.representation = SphericalWrap180Representation def calculate_distance(self): """ This method calculates the third coordinate of the Helioprojective frame. It assumes that the coordinate point is on the disk of the Sun at the rsun radius. 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 isinstance(self._data, SphericalRepresentation): return self rep = self.represent_as(UnitSphericalWrap180Representation) lat, lon = rep.lat, rep.lon alpha = np.arccos(np.cos(lat) * np.cos(lon)).to(lat.unit) c = self.observer.radius**2 - self.rsun**2 b = -2 * self.observer.radius * np.cos(alpha) d = ((-1 * b) - np.sqrt(b**2 - 4 * c)) / 2 return self.realize_frame( SphericalWrap180Representation(lon=lon, lat=lat, distance=d))