Пример #1
0
    def visibility_altaz(self, source_radec, site, hardcoded=True):
        """
        To each time grid point associates AltAz coordinates.
        :param source_radec: source coordinates (astropy SkyCoord object)
        :param site: (str)
        :return:
        """
        if not hasattr(self, 'vis_points'):
            raise AttributeError(
                'Must invoke visibility_points() before using this method')
        if not hardcoded:
            site_coords = EarthLocation.of_site(site)
        else:
            if site.lower() in ('north', 'roque de los muchachos'):
                #site_coords = EarthLocation.from_geodetic('342.1184', '28.7606', 2326. * u.meter)
                site_coords = EarthLocation.from_geocentric(5327285.09211954,
                                                            -1718777.11250295,
                                                            3051786.7327476,
                                                            unit="m")

            elif site.lower() in ('south', 'paranal'):
                #site_coords = EarthLocation.from_geodetic('289.5972', '-24.6253', 2635. * u.meter)
                site_coords = EarthLocation.from_geocentric(1946635.7979987,
                                                            -5467633.94561753,
                                                            -2642498.5212285,
                                                            unit="m")
            else:
                raise Warning(f"{site} is not a valid site choice")
        self.altaz = source_radec.transform_to(
            AltAz(obstime=self.vis_points, location=site_coords))
        return self
Пример #2
0
def eci2el(x, y, z, dt):
    """
    Convert Earth-Centered Inertial (ECI) cartesian coordinates to ITRS for astropy EarthLocation object.

    Inputs :
    x = ECI X-coordinate 
    y = ECI Y-coordinate 
    z = ECI Z-coordinate 
    dt = UTC time (datetime object)
    """

    from astropy.coordinates import GCRS, ITRS, EarthLocation, CartesianRepresentation
    import astropy.units as u

    # convert datetime object to astropy time object
    tt = Time(dt, format='datetime')

    # Read the coordinates in the Geocentric Celestial Reference System
    gcrs = GCRS(CartesianRepresentation(x=x, y=y, z=z), obstime=tt)

    # Convert it to an Earth-fixed frame
    itrs = gcrs.transform_to(ITRS(obstime=tt))

    el = EarthLocation.from_geocentric(itrs.x, itrs.y, itrs.z)

    return el
Пример #3
0
    def astropy_parallactic_angles(times, antenna_positions, field_centre):
        """
        Computes parallactic angles per timestep for the given
        reference antenna position and field centre.
        """
        ap = antenna_positions
        fc = field_centre

        # Convert from MJD second to MJD
        times = Time(times / 86400.00, format='mjd', scale='utc')

        ap = EarthLocation.from_geocentric(ap[:, 0],
                                           ap[:, 1],
                                           ap[:, 2],
                                           unit='m')
        fc = SkyCoord(ra=fc[0], dec=fc[1], unit=units.rad, frame='fk5')
        pole = SkyCoord(ra=0, dec=90, unit=units.deg, frame='fk5')

        cirs_frame = CIRS(obstime=times)
        pole_cirs = pole.transform_to(cirs_frame)
        fc_cirs = fc.transform_to(cirs_frame)

        altaz_frame = AltAz(location=ap[None, :], obstime=times[:, None])
        pole_altaz = pole_cirs[:, None].transform_to(altaz_frame)
        fc_altaz = fc_cirs[:, None].transform_to(altaz_frame)
        return fc_altaz.position_angle(pole_altaz)
Пример #4
0
    def earth_location_itrf(self, time=None):
        '''Return RXTE spacecraft location in ITRF coordinates'''

        if self.tt2tdb_mode.lower().startswith('none'):
            log.warning('Using location=None for TT to TDB conversion')
            return None
        elif self.tt2tdb_mode.lower().startswith('geo'):
            log.warning('Using location geocenter for TT to TDB conversion')
            return EarthLocation.from_geocentric(0.0*u.m,0.0*u.m,0.0*u.m)
        elif self.tt2tdb_mode.lower().startswith('spacecraft'):
            # First, interpolate ECI geocentric location from orbit file.
            # These are inertial coorinates aligned with ICRF
            pos_gcrs =  GCRS(CartesianRepresentation(self.X(time.tt.mjd)*u.m,
                                                     self.Y(time.tt.mjd)*u.m,
                                                     self.Z(time.tt.mjd)*u.m),
                             obstime=time)

            # Now transform ECI (GCRS) to ECEF (ITRS)
            # By default, this uses the WGS84 ellipsoid
            pos_ITRS = pos_gcrs.transform_to(ITRS(obstime=time))

            # Return geocentric ITRS coordinates as an EarthLocation object
            return pos_ITRS.earth_location
        else:
            log.error('Unknown tt2tdb_mode %s, using None', self.tt2tdb_mode)
            return None
Пример #5
0
    def earth_location_itrf(self, time=None):
        """Return NICER spacecraft location in ITRF coordinates"""

        if self.tt2tdb_mode.lower().startswith("pint"):
            return None
        elif self.tt2tdb_mode.lower().startswith("geo"):
            return EarthLocation.from_geocentric(0.0 * u.m, 0.0 * u.m,
                                                 0.0 * u.m)
        elif self.tt2tdb_mode.lower().startswith("astropy"):
            # First, interpolate ECI geocentric location from orbit file.
            # These are inertial coorinates aligned with ICRF
            pos_gcrs = GCRS(
                CartesianRepresentation(
                    self.X(time.tt.mjd) * u.m,
                    self.Y(time.tt.mjd) * u.m,
                    self.Z(time.tt.mjd) * u.m,
                ),
                obstime=time,
            )

            # Now transform ECI (GCRS) to ECEF (ITRS)
            # By default, this uses the WGS84 ellipsoid
            pos_ITRS = pos_gcrs.transform_to(ITRS(obstime=time))

            # Return geocentric ITRS coordinates as an EarthLocation object
            return pos_ITRS.earth_location
        else:
            log.error("Unknown tt2tdb_mode %s, using None" % self.tt2tdb_mode)
            return None
    def from_itrf(ant_itrf_xyz, ant_labels):
        """
        Instantiate telescope model from ITRF co-ordinates of antennae

        (aka ECEF-coords, i.e. Earth-Centered-Earth-Fixed reference frame.)

        Takes care of calculating central Latitude and Longitude from the mean
        antenna position, and converts the antenna positions into local-XYZ
        frame.

        Args:
            ant_itrf_xyz (numpy.ndarray): Array co-ordinatates in the ITRF frame
            ant_labels (list[str]): Antennae labels
        Returns:
            Telescope: A telescope class with the given array co-ords.
        """
        mean_posn = np.mean(ant_itrf_xyz, axis=0)
        centre = EarthLocation.from_geocentric(mean_posn[0],
                                               mean_posn[1],
                                               mean_posn[2],
                                               unit=u.m,
                                               )
        lon, lat, height = centre.to_geodetic()

        mean_subbed_itrf = ant_itrf_xyz - mean_posn

        rotation = z_rotation_matrix(lon)
        ant_local_xyz = np.dot(rotation, mean_subbed_itrf.T).T

        return Telescope(
            centre=centre,
            ant_labels=ant_labels,
            ant_itrf_xyz=ant_itrf_xyz,
            ant_local_xyz=ant_local_xyz,
        )
Пример #7
0
def get_moon_j2000(epoch, line1, line2, position = None):
    '''
    
    Code to determine the apparent J2000 position for a given
    time and at a given position for the observatory.
    
    epoch needs to be a datetime or Time object.
    
    position is a list/tuple of X/Y/Z positions
    
    '''
    
    from astropy.time import Time
    from astropy.coordinates import get_moon, EarthLocation
    import astropy.units as u
    
    if position is None:
        position = get_nustar_location(epoch, line1, line2)
        
    t = Time(epoch)
    
    loc = EarthLocation.from_geocentric(*position * u.km)

    moon_coords = get_moon(t,loc)
    
    # Get just the coordinates in degrees
    
    ra_moon, dec_moon = moon_coords.ra.degree * u.deg, moon_coords.dec.degree*u.deg
    return ra_moon, dec_moon
Пример #8
0
def load_data(filename):
    with open(filename) as f:
        line = f.readline()
    [x, y, z] = [float(a) for a in line.split()]
    site = EarthLocation.from_geocentric(x, y, z, unit='meter').to('kilometer')

    yr, mon, day, hr, min = np.loadtxt(filename,
                                       dtype=int,
                                       usecols=(0, 1, 2, 3, 4),
                                       skiprows=1,
                                       unpack=True)
    sec, ra, de = np.loadtxt(filename,
                             dtype=float,
                             usecols=(5, 6, 7),
                             skiprows=1,
                             unpack=True)
    angles = np.array([ra, de])
    angles = angles.transpose()
    epochs = []
    for i in np.arange(np.size(yr)):
        s = int(sec[i])
        ms = int((sec[i] - s) * 1e6)
        epoch = Time(datetime(yr[i], mon[i], day[i], hr[i], min[i], s, ms),
                     scale='utc')
        epochs = np.append(epochs, epoch)
    return site, epochs, Angle(angles * u.deg)
Пример #9
0
    def __init__(self, name, ft2name, maxextrap=2, overwrite=False):
        self.FT2 = load_orbit(name, ft2name)

        # Now build the interpolator.  This extrapolation will fail quickly,
        # which is where maxextrap comes in.
        tt = self.FT2["MJD_TT"]
        self.X = InterpolatedUnivariateSpline(tt,
                                              self.FT2["X"],
                                              ext="extrapolate")
        self.Y = InterpolatedUnivariateSpline(tt,
                                              self.FT2["Y"],
                                              ext="extrapolate")
        self.Z = InterpolatedUnivariateSpline(tt,
                                              self.FT2["Z"],
                                              ext="extrapolate")
        self.Vx = InterpolatedUnivariateSpline(tt,
                                               self.FT2["Vx"],
                                               ext="extrapolate")
        self.Vy = InterpolatedUnivariateSpline(tt,
                                               self.FT2["Vy"],
                                               ext="extrapolate")
        self.Vz = InterpolatedUnivariateSpline(tt,
                                               self.FT2["Vz"],
                                               ext="extrapolate")
        self._geocenter = EarthLocation.from_geocentric(
            0.0 * u.m, 0.0 * u.m, 0.0 * u.m)
        self._maxextrap = maxextrap
        super(SatelliteObs, self).__init__(name=name, overwrite=overwrite)
Пример #10
0
    def earth_location_itrf(self, time=None):
        '''Return Fermi spacecraft location in ITRF coordinates'''

        if self.tt2tdb_mode.lower().startswith('none'):
            log.warning('Using location=None for TT to TDB conversion')
            return None
        elif self.tt2tdb_mode.lower().startswith('geo'):
            log.warning('Using location geocenter for TT to TDB conversion')
            return EarthLocation.from_geocentric(0.0*u.m,0.0*u.m,0.0*u.m)
        elif self.tt2tdb_mode.lower().startswith('spacecraft'):
            # First, interpolate Earth-Centered Inertial (ECI) geocentric
            # location from orbit file.
            # These are inertial coordinates aligned with ICRS, called GCRS
            # <http://docs.astropy.org/en/stable/api/astropy.coordinates.GCRS.html>
            pos_gcrs =  GCRS(CartesianRepresentation(self.X(time.tt.mjd)*u.m,
                                                     self.Y(time.tt.mjd)*u.m,
                                                     self.Z(time.tt.mjd)*u.m),
                             obstime=time)

            # Now transform ECI (GCRS) to ECEF (ITRS)
            # By default, this uses the WGS84 ellipsoid
            pos_ITRS = pos_gcrs.transform_to(ITRS(obstime=time))

            # Return geocentric ITRS coordinates as an EarthLocation object
            return pos_ITRS.earth_location
        else:
            log.error('Unknown tt2tdb_mode %s, using None', self.tt2tdb_mode)
            return None
Пример #11
0
    def earth_location_itrf(self, time=None):
        '''Return POLAR spacecraft location in ITRF coordinates'''

        if self.tt2tdb_mode.lower().startswith('none'):
            log.warning('Using location=None for TT to TDB conversion')
            return None
        elif self.tt2tdb_mode.lower().startswith('geo'):
            log.warning('Using location geocenter for TT to TDB conversion')
            return EarthLocation.from_geocentric(0.0 * u.m, 0.0 * u.m,
                                                 0.0 * u.m)
        elif self.tt2tdb_mode.lower().startswith('spacecraft'):
            # First, interpolate Earth-Centered Inertial (ECI) geocentric
            # location from orbit file.
            # These are inertial coordinates aligned with ICRS, called GCRS
            # <http://docs.astropy.org/en/stable/api/astropy.coordinates.GCRS.html>
            pos_gcrs = GCRS(CartesianRepresentation(
                self.X(time.utc.unix) * u.m,
                self.Y(time.utc.unix) * u.m,
                self.Z(time.utc.unix) * u.m),
                            obstime=time)

            # Now transform ECI (GCRS) to ECEF (ITRS)
            # By default, this uses the WGS84 ellipsoid
            pos_ITRS = pos_gcrs.transform_to(ITRS(obstime=time))

            # Return geocentric ITRS coordinates as an EarthLocation object
            return pos_ITRS.earth_location
        else:
            log.error('Unknown tt2tdb_mode %s, using None', self.tt2tdb_mode)
            return None
Пример #12
0
def eci2el(x,y,z,dt):
    """
    Convert Earth-Centered Inertial (ECI) cartesian coordinates to ITRS for astropy EarthLocation object.

    Inputs :
    x = ECI X-coordinate 
    y = ECI Y-coordinate 
    z = ECI Z-coordinate 
    dt = UTC time (datetime object)
    """

    from astropy.coordinates import GCRS, ITRS, EarthLocation, CartesianRepresentation
    import astropy.units as u
    
    # convert datetime object to astropy time object
    tt=Time(dt,format='datetime')

    # Read the coordinates in the Geocentric Celestial Reference System
    gcrs = GCRS(CartesianRepresentation(x=x, y=y,z=z), obstime=tt)

    # Convert it to an Earth-fixed frame
    itrs = gcrs.transform_to(ITRS(obstime=tt))

    el = EarthLocation.from_geocentric(itrs.x, itrs.y, itrs.z) 

    return el
Пример #13
0
def eci2lla(x, y, z, dt):
    """
    Convert Earth-Centered Inertial (ECI) cartesian coordinates to latitude, longitude, and altitude, using astropy.

    Inputs :
    x = ECI X-coordinate (m)
    y = ECI Y-coordinate (m)
    z = ECI Z-coordinate (m)
    dt = UTC time (datetime object)
    Outputs :
    lon = longitude (radians)

    lat = geodetic latitude (radians)
    alt = height above WGS84 ellipsoid (m)
    """
    # convert datetime object to astropy time object
    tt = Time(dt, format='datetime')

    # Read the coordinates in the Geocentric Celestial Reference System
    gcrs = GCRS(CartesianRepresentation(x=x * u.m, y=y * u.m, z=z * u.m),
                obstime=tt)

    # Convert it to an Earth-fixed frame
    itrs = gcrs.transform_to(ITRS(obstime=tt))

    el = EarthLocation.from_geocentric(itrs.x, itrs.y, itrs.z)

    # conversion to geodetic
    lon, lat, alt = el.to_geodetic()
    # lon_val = lon.value

    return lon.value, lat.value, alt.value
Пример #14
0
    def get_location(self):

        arrayHDU = self.get_arrayHDU()
        header = arrayHDU.header
        xyz = [header.get('ARRAY' + var, 0) for var in ['X', 'Y', 'Z']]
        loc = EarthLocation.from_geocentric(*xyz, unit="m")

        return loc
    def set_beam_target(self,
                        target_or_ra,
                        dec=None,
                        load_time=None,
                        verbose=True):
        """
        Given the name of an astronomical target, 'sun', or 'zenith', compute the
        current topocentric position of the body and point the beam at it.  If
        the 'dec' keyword is not None, the target is intepreted to be a RA.
        """

        # Force to string
        target_or_ra = str(target_or_ra)
        if dec is not None:
            dec = str(dec)

        # Figure out what to do with the name
        if target_or_ra.lower() in ('z', 'zen', 'zenith'):
            ## Zenith is easy
            az, alt = 0.0, 90.0
        else:
            ## Load in where we are
            obs = EarthLocation.from_geocentric(*self.station.ecef, unit=u.m)

            ## Resolve the name into coordinates
            if dec is not None:
                ra = Angle(target_or_ra, unit='hourangle')
                dec = Angle(dec, unit='deg')
                sc = SkyCoord(ra, dec, frame='fk5')
                if verbose:
                    print(
                        f"Resolved '{target_or_ra}, {dec}' to RA {sc.ra}, Dec. {sc.dec}"
                    )

            elif target_or_ra.lower() in solar_system_ephemeris.bodies:
                if target_or_ra.lower().startswith('earth'):
                    raise ValueError(f"Invalid target: '{target_or_ra}'")

                sc = get_body(target_or_ra.lower(), Time.now(), location=obs)
                if verbose:
                    print(
                        f"Resolved '{target_or_ra}' to {target_or_ra.lower()}")
            else:
                sc = SkyCoord.from_name(target_or_ra)
                if verbose:
                    print(
                        f"Resolved '{target_or_ra}' to RA {sc.ra}, Dec. {sc.dec}"
                    )

            ## Figure out where it is right now
            aa = sc.transform_to(AltAz(obstime=Time.now(), location=obs))
            az = aa.az.deg
            alt = aa.alt.deg
            if verbose:
                print(f"Currently at azimuth {aa.az}, altitude {aa.alt}")

        # Point the beam
        self.set_beam_pointing(az, alt, degrees=True, load_time=load_time)
Пример #16
0
    def __init__(self, name, tempo_code=None, itoa_code=None, aliases=None,
                 itrf_xyz=None, clock_file='time.dat', clock_dir='PINT',
                 clock_fmt='tempo', include_gps=True, include_bipm=True,
                 bipm_version='BIPM2015'):
        # ITRF coordinates are required
        if itrf_xyz is None:
            raise ValueError(
                    "ITRF coordinates not given for observatory '%s'" % name)

        # Convert coords to standard format.  If no units are given, assume
        # meters.
        if not has_astropy_unit(itrf_xyz):
            xyz = numpy.array(itrf_xyz) * u.m
        else:
            xyz = itrf_xyz.to(u.m)

        # Check for correct array dims
        if xyz.shape != (3,):
            raise ValueError(
                    "Incorrect coordinate dimensions for observatory '%s'" % (
                        name))

        # Convert to astropy EarthLocation, ensuring use of ITRF geocentric coordinates
        self._loc_itrf = EarthLocation.from_geocentric(*xyz)

        # Save clock file info, the data will be read only if clock
        # corrections for this site are requested.
        self.clock_file = clock_file
        self._multiple_clock_files = not isinstance(clock_file,str)
        self.clock_dir = clock_dir
        self.clock_fmt = clock_fmt
        self._clock = None # The ClockFile object, will be read on demand

        # If using TEMPO time.dat we need to know the 1-char tempo-style
        # observatory code.
        if (clock_dir=='TEMPO' and clock_file=='time.dat'
                and tempo_code is None):
            raise ValueError("No tempo_code set for observatory '%s'" % name)

        # GPS corrections
        self.include_gps = include_gps
        self._gps_clock = None

        # BIPM corrections
        self.include_bipm = include_bipm
        self.bipm_version = bipm_version
        self._bipm_clock = None

        self.tempo_code = tempo_code
        if aliases is None: aliases = []
        for code in (tempo_code, itoa_code):
            if code is not None: aliases.append(code)

        super(TopoObs,self).__init__(name,aliases=aliases, tt2tdb_mode='astropy')
Пример #17
0
def ecef_to_lla(r: np.ndarray) -> List:
    """
    Converts coordinate in ECEF frame to Lat and Lon
    :param r: Position in ECEF frame. Numpy array Units [km]
    """
    loc = EarthLocation.from_geocentric(r[0] * u.km, r[1] * u.km, r[2] * u.km)
    lat = loc.geodetic.lat
    lon = loc.geodetic.lon
    alt = loc.geodetic.height
    lla = [lat, lon, alt]
    return lla
Пример #18
0
def test_gaussbeam_values():
    """
        Make the long-line point sources up to 10 degrees from zenith.
        Obtain visibilities
        Confirm that the values match the expected beam values at those zenith angles.
    """
    sigma = 0.05
    hera_uv = UVData()
    hera_uv.read_uvfits(EW_uvfits_file)

    array_location = EarthLocation.from_geocentric(
        hera_uv.telescope_location[0],
        hera_uv.telescope_location[1],
        hera_uv.telescope_location[2],
        unit='m')
    freq = hera_uv.freq_array[0, 0] * units.Hz

    time = Time(hera_uv.time_array[0], scale='utc', format='jd')

    catalog, mock_keywords = pyuvsim.create_mock_catalog(
        time=time,
        arrangement='long-line',
        Nsrcs=41,
        max_za=10.,
        array_location=array_location)

    beam = pyuvsim.AnalyticBeam('gaussian', sigma=sigma)
    array = pyuvsim.Telescope('telescope_name', array_location, [beam])

    # Need a dummy baseline for this test.
    antenna1 = pyuvsim.Antenna('ant1', 1, np.array([0, 0, 0]), 0)
    antenna2 = pyuvsim.Antenna('ant2', 2, np.array([107, 0, 0]), 0)

    baseline = pyuvsim.Baseline(antenna1, antenna2)
    coherencies = []
    zenith_angles = []
    for src in catalog:
        task = pyuvsim.UVTask(src, time, freq, baseline, array)
        engine = pyuvsim.UVEngine(task)
        engine.apply_beam()
        #        task.source.az_za_calc(time, array_location)
        zenith_angles.append(task.source.az_za[1])  # In radians.
        coherencies.append(
            np.real(engine.apparent_coherency[0, 0]).astype(
                float))  # All four components should be identical

    coherencies = np.array(coherencies)
    zenith_angles = np.array(zenith_angles)

    # Confirm the coherency values (ie., brightnesses) match the beam values.

    beam_values = np.exp(-(zenith_angles)**2 / (2 * beam.sigma**2))
    nt.assert_true(np.all(beam_values**2 == coherencies))
Пример #19
0
def test_gaussbeam_values():
    """
    Make the long-line point sources up to 10 degrees from zenith.
    Confirm that the coherencies match the expected beam values at those zenith angles.
    """
    sigma = 0.05
    hera_uv = UVData()
    hera_uv.read_uvfits(EW_uvfits_file)

    array_location = EarthLocation.from_geocentric(*hera_uv.telescope_location,
                                                   unit='m')
    freq = hera_uv.freq_array[0, 0] * units.Hz

    time = Time(hera_uv.time_array[0], scale='utc', format='jd')

    catalog, mock_keywords = pyuvsim.create_mock_catalog(
        time=time,
        arrangement='long-line',
        Nsrcs=41,
        min_alt=80.,
        array_location=array_location)

    catalog.update_positions(time, array_location)
    beam = pyuvsim.AnalyticBeam('gaussian', sigma=sigma)
    array = pyuvsim.Telescope('telescope_name', array_location, [beam])

    # Need a dummy baseline for this test.
    antenna1 = pyuvsim.Antenna('ant1', 1, np.array([0, 0, 0]), 0)
    antenna2 = pyuvsim.Antenna('ant2', 2, np.array([107, 0, 0]), 0)

    baseline = pyuvsim.Baseline(antenna1, antenna2)

    task = pyuvsim.UVTask(catalog, time, freq, baseline, array)

    engine = pyuvsim.UVEngine(task)
    engine.apply_beam()
    altitudes = task.sources.alt_az[0]  # In radians.
    # All four components should be identical
    if isinstance(engine.apparent_coherency, units.Quantity):
        coherency_use = engine.apparent_coherency.to_value("Jy")
    else:
        coherency_use = engine.apparent_coherency

    coherencies = np.real(coherency_use[0, 0] +
                          coherency_use[1, 1]).astype(float)

    zenith_angles, _ = simutils.altaz_to_zenithangle_azimuth(
        altitudes, np.zeros_like(np.array(altitudes)))

    # Confirm the coherency values (ie., brightnesses) match the beam values.
    beam_values = np.exp(-(zenith_angles)**2 / (2 * beam.sigma**2))
    assert np.all(beam_values**2 == coherencies)
Пример #20
0
    def __init__(self, name, ft2name):
        self.FT2 = load_orbit(name, ft2name)

        # Now build the interpolator here:
        tt = self.FT2["MJD_TT"]
        self.X = InterpolatedUnivariateSpline(tt, self.FT2["X"], ext="raise")
        self.Y = InterpolatedUnivariateSpline(tt, self.FT2["Y"], ext="raise")
        self.Z = InterpolatedUnivariateSpline(tt, self.FT2["Z"], ext="raise")
        self.Vx = InterpolatedUnivariateSpline(tt, self.FT2["Vx"], ext="raise")
        self.Vy = InterpolatedUnivariateSpline(tt, self.FT2["Vy"], ext="raise")
        self.Vz = InterpolatedUnivariateSpline(tt, self.FT2["Vz"], ext="raise")
        self._geocenter = EarthLocation.from_geocentric(0.0 * u.m, 0.0 * u.m, 0.0 * u.m)
        super(SatelliteObs, self).__init__(name=name)
Пример #21
0
    def to_location(self):
        """Calculate the observatory location.

        Uses FITS standard ``OBSGEO-`` headers.

        Returns
        -------
        location : `astropy.coordinates.EarthLocation`
            An object representing the location of the telescope.
        """
        cards = [f"OBSGEO-{c}" for c in ("X", "Y", "Z")]
        coords = [self._header[c] for c in cards]
        value = EarthLocation.from_geocentric(*coords, unit=u.m)
        self._used_these_cards(*cards)
        return value
Пример #22
0
def test_single_zenith_source_uvdata():
    """Test single zenith source using test uvdata file."""
    hera_uv = UVData()
    hera_uv.read_uvfits(EW_uvfits_file)

    time = Time(hera_uv.time_array[0], scale='utc', format='jd')
    array_location = EarthLocation.from_geocentric(
        hera_uv.telescope_location[0],
        hera_uv.telescope_location[1],
        hera_uv.telescope_location[2],
        unit='m')
    freq = hera_uv.freq_array[0, 0] * units.Hz

    # get antennas positions into ENU
    antpos = hera_uv.antenna_positions[0:2, :] + hera_uv.telescope_location
    antpos = uvutils.ENU_from_ECEF(antpos.T,
                                   *hera_uv.telescope_location_lat_lon_alt).T

    antenna1 = pyuvsim.Antenna('ant1', 1, np.array(antpos[0, :]), 0)
    antenna2 = pyuvsim.Antenna('ant2', 2, np.array(antpos[1, :]), 0)

    # setup the things that don't come from pyuvdata:
    # make a source at zenith
    time.location = array_location
    source = create_zenith_source(time, 'zensrc')

    beam = UVBeam()
    beam.read_cst_beam(beam_files,
                       beam_type='efield',
                       frequency=[100e6, 123e6],
                       telescope_name='HERA',
                       feed_name='PAPER',
                       feed_version='0.1',
                       feed_pol=['x'],
                       model_name='E-field pattern - Rigging height 4.9m',
                       model_version='1.0')

    beam_list = [beam]

    baseline = pyuvsim.Baseline(antenna1, antenna2)
    array = pyuvsim.Telescope('telescope_name', array_location, beam_list)
    task = pyuvsim.UVTask(source, time, freq, baseline, array)
    engine = pyuvsim.UVEngine(task)

    visibility = engine.make_visibility()

    nt.assert_true(np.allclose(visibility, np.array([.5, .5, 0, 0]),
                               atol=5e-3))
Пример #23
0
def get_PP_PD(sp, directions, times, hIon, ncpu):
    ''' 
    This is a wrapper function to parallelize get_PP_PD_per_source()
    and unpack the return.
    Get the Pierce Points and the Pierce Directions for <l> directions,
    <m> stations and <n> timestamps. 
    
    Parameters
    ----------
    sp : (m, 3) ndarray
        Station positions in meters ITRS XYZ
    directions : (l,2) ndarray
        Source directions RA and DEC in degree
    times : (n,) ndarray
        Array containing timestamps in mjd seconds.
    hIon : float, Ionosphere height in m
    ncpu : int,

    Returns
    -------
    PP : (n,m,l,3) ndarray
        Pierce points in geocentric ITRS. Unit: meter
        The (n,m,l3) shape corresponds to (timestamp, station, direction, xyz).
    PD : (n,l,3) ndarray
        Pierce direction unit verctors in geocentric ITRS.
        The directions are oriented such that the point from the source
        towards earth.
        The (n,l,3) shape corresponds to (timestamp, direction, xyz).
        Since the coord sys is geocentric and not horizontal, the source 
        directions are the same for every station.
    '''
    sp = EarthLocation.from_geocentric(x=sp[:, 0],
                                       y=sp[:, 1],
                                       z=sp[:, 2],
                                       unit='meter')
    itrs = ITRS(obstime=Time(times / (3600 * 24), format='mjd'))
    map_args = [(sp, d, itrs, hIon) for d in directions]
    pool = mp.Pool(processes=ncpu)
    PP_PD = pool.map(get_PP_PD_per_source, map_args)
    pool.close()  # w/o close+join: OSError: [Errno 12] Cannot allocate memory
    pool.join()
    # PP shaped as (timestamp, station, sources, xyz)
    PP = np.array([u for (u, v) in PP_PD]).swapaxes(0, 1).swapaxes(1, 2)
    # PD shaped as (timestamp, source, xyz)
    PD = np.array([v for (u, v) in PP_PD]).swapaxes(0, 1)
    return PP, PD
Пример #24
0
def search_code_mpc():
    """ Reads the MPC Observer Database

    Returns:
        observatories (dict): A python dictionaty with all the sites as an Astropy EarthLocation object
    """
    obs = MPC.get_observatory_codes()
    observatories = {}
    for line in obs:
        code = line['Code']
        lon = line['Longitude'] * u.deg
        rcphi = line['cos'] * 6378.137 * u.km
        rsphi = line['sin'] * 6378.137 * u.km
        name = line['Name']
        site = EarthLocation.from_geocentric(rcphi * np.cos(lon),
                                             rcphi * np.sin(lon), rsphi)
        observatories[code] = (name, site)
    return observatories
Пример #25
0
def _gettec(altaz_args):
    alltec = []
    altaz, stationpositions, A12, times, tidAmp, tidLen, tidVel = altaz_args
    direction = altaz.geocentrictrueecliptic.cartesian.xyz.value
    for ant in stationpositions:
        pp, am = post.getPPsimple([200.e3] * direction[0].shape[0], ant,
                                  direction)
        ppa = EarthLocation.from_geocentric(pp[:, 0],
                                            pp[:, 1],
                                            pp[:, 2],
                                            unit=u.m)
        ppaproj = EarthLocation.from_geodetic(-ppa.lon.deg + A12.lon.deg,
                                              -ppa.lat.deg + A12.lat.deg,
                                              ppa.height)
        x = ppaproj.z.value
        y = ppaproj.y.value
        tec = _tid(x, times * 3600. * 24, tidAmp, tidLen, tidVel)
        alltec.append([tec, x, y, altaz.secz])
    return alltec
def ECEF2lla(x, y, z):
    #This function takes a position in ECEF and converts to geodetic position
    #(lon,lat,alt above ellipsiod)
    #
    # Input
    # x = Position in ECEF (m)
    # y = Position in ECEF (m)
    # z = Position in ECEF (m)

    # Output
    # lon = geodetic latitude (WGS-84)
    # lat = geodetic latitude (WGS-84)
    # alt = altitude above ellipsoid (m)

    #FIXME: check if obstime is needed

    el = EarthLocation.from_geocentric(x, y, z, unit=units.m)
    geo = el.to_geodetic()
    lat = geo[1]
    lon = geo[0]
    alt = geo[2]
    return lat, lon, alt
Пример #27
0
def ITRS_to_geodetic(x, y, z, radians=False, ellipsoid=None):
    '''Use `astropy.coordinates.EarthLocation` to transform from geodetic to ITRS.
    '''

    cord = EarthLocation.from_geocentric(
        x=x * units.m,
        y=y * units.m,
        z=z * units.m,
    )
    lon, lat, height = cord.to_geodetic(ellipsoid=ellipsoid)

    llh = np.empty((3, ), dtype=np.float64)

    if radians:
        u_ = units.rad
    else:
        u_ = units.deg
    llh[0] = lat.to(u_).value
    llh[1] = lon.to(u_).value
    llh[2] = height.to(units.m).value

    return llh
Пример #28
0
    def from_tree(cls, node, ctx):
        if isinstance(node, (str, list, np.ndarray)):
            t = time.Time(node)
            fmt = _astropy_format_to_asdf_format.get(t.format, t.format)
            if fmt not in _guessable_formats:
                raise ValueError(f"Invalid time '{node}'")
            return t

        value = node['value']
        fmt = node.get('format')
        scale = node.get('scale')
        location = node.get('location')
        if location is not None:
            unit = location.get('unit', u.m)
            # This ensures that we can read the v.1.0.0 schema and convert it
            # to the new EarthLocation object, which expects Quantity components
            for comp in ['x', 'y', 'z']:
                if not isinstance(location[comp], Quantity):
                    location[comp] = Quantity(location[comp], unit=unit)
            location = EarthLocation.from_geocentric(
                location['x'], location['y'], location['z'])

        return time.Time(value, format=fmt, scale=scale, location=location)
 def get_time_frame(self, element):
         #ref_location = self.table_mapper.search_instance_by_role("coords:StdRefLocation.position", 
         #                                                root_element=ele)[0]['@value']
         #print(ref_location)               
         frame_instance = self.table_mapper.search_instance_by_type("coords:TimeFrame", 
                                                         root_element=element)[0]
         ref_position_block =  frame_instance["coords:TimeFrame.refPosition"]                                           
         ref_scale = self.table_mapper.search_instance_by_role(
             "coords:StdRefLocation.position", 
             root_element=ref_position_block)[0]['@value'].upper()  
             
         ref_location_block = frame_instance["coords:TimeFrame.refLocation"]
         ref_location = self.table_mapper.search_instance_by_role(
             "coords:StdRefLocation.position", 
             root_element=ref_location_block)[0]['@value'].upper() 
              
         if ref_scale == "BARYCENTER":
             ref_scale = "tcb"   
         if ref_location == "GEOCENTRIC":
             ref_location = EarthLocation.from_geocentric(0,0,0, 'm') 
                                                                         
         astrotime = Time(50000., scale=ref_scale, format='mjd', location=ref_location)    
         return astrotime.scale, astrotime.location, astrotime.format                                
Пример #30
0
    def from_tree(cls, node, ctx):
        if isinstance(node, (str, list, np.ndarray)):
            t = time.Time(node)
            format = _astropy_format_to_asdf_format.get(t.format, t.format)
            if format not in _guessable_formats:
                raise ValueError("Invalid time '{0}'".format(node))
            return t

        value = node['value']
        format = node.get('format')
        scale = node.get('scale')
        location = node.get('location')
        if location is not None:
            unit = location.get('unit', u.m)
            # This ensures that we can read the v.1.0.0 schema and convert it
            # to the new EarthLocation object, which expects Quantity components
            for comp in ['x', 'y', 'z']:
                if not isinstance(location[comp], Quantity):
                    location[comp] = Quantity(location[comp], unit=unit)
            location = EarthLocation.from_geocentric(
                location['x'], location['y'], location['z'])

        return time.Time(value, format=format, scale=scale, location=location)
Пример #31
0
def itrs2horizon(station, ts, ts_quasi_mjd, positions, coord_type):
    '''
    Convert cartesian coordinates of targets in ITRF to spherical coordinates in topocentric reference frame for a specific station.

    Usage: 
    az,alt,r = itrs2horizon(station,ts,ts_quasi_mjd,positions,coord_type)

    Inputs:
    station -> [numercial array or list with 3 elements] coordinates of station. It can either be geocentric(x, y, z) coordinates or geodetic(lon, lat, height) coordinates.
    Unit for (x, y, z) are meter, and for (lon, lat, height) are degree and meter.
    ts -> [str array] iso-formatted UTC for interpolated prediction
    ts_quasi_mjd -> [float array] quasi MJD for interpolated prediction
    positions -> [2d float array] target positions in cartesian coordinates in meters w.r.t. ITRF for interpolated prediction.
    coord_type -> [str] coordinates type for coordinates of station; it can either be 'geocentric' or 'geodetic'.

    Outputs:
    az -> [float array] Azimuth for interpolated prediction in degrees
    alt -> [float array] Altitude for interpolated prediction in degrees
    r -> [float array] Range for interpolated prediction in meters
    '''
    if coord_type == 'geocentric':
        x, y, z = station
        site = EarthLocation.from_geocentric(x, y, z, unit='m')
    elif coord_type == 'geodetic':
        lat, lon, height = station
        site = EarthLocation.from_geodetic(lon, lat, height)

    coords = SkyCoord(positions,
                      unit='m',
                      representation_type='cartesian',
                      frame='itrs',
                      obstime=Time(ts))
    horizon = coords.transform_to(AltAz(obstime=Time(ts), location=site))

    az, alt, r = horizon.az.deg, horizon.alt.deg, horizon.distance.m

    return az, alt, r
    def from_itrf(ant_itrf_xyz, ant_labels):
        """
        Instantiate telescope model from ITRF co-ordinates of antennae

        (aka ECEF-coords, i.e. Earth-Centered-Earth-Fixed reference frame.)

        Takes care of calculating central Latitude and Longitude from the mean
        antenna position, and converts the antenna positions into local-XYZ
        frame.

        Args:
            ant_itrf_xyz (numpy.ndarray): Array co-ordinatates in the ITRF frame
            ant_labels (list[str]): Antennae labels
        Returns:
            Telescope: A telescope class with the given array co-ords.
        """
        mean_posn = np.mean(ant_itrf_xyz, axis=0)
        centre = EarthLocation.from_geocentric(
            mean_posn[0],
            mean_posn[1],
            mean_posn[2],
            unit=u.m,
        )
        lon, lat, height = centre.to_geodetic()

        mean_subbed_itrf = ant_itrf_xyz - mean_posn

        rotation = z_rotation_matrix(lon)
        ant_local_xyz = np.dot(rotation, mean_subbed_itrf.T).T

        return Telescope(
            centre=centre,
            ant_labels=ant_labels,
            ant_itrf_xyz=ant_itrf_xyz,
            ant_local_xyz=ant_local_xyz,
        )
Пример #33
0
def archive_TO_elev_start_end(archive):
    """
    Fonction to give elevation in the first and last subintegration
    EarthLocation is set for Nancay

    Input:
        archives : list of PSRCHIVE archive objects
    Output
        float(elev0),float(elevlast)
    """
    try:
        x, y, z = archive.get_ant_xyz()
    except:
        print('warning: archive.get_ant_xyz() faild will used nancay location')
        x = 4324016.70769
        y = 165545.525467
        z = 4670271.363
    Site = EarthLocation.from_geocentric(x=float(x) * u.m,
                                         y=float(y) * u.m,
                                         z=float(z) * u.m)

    ra = archive.get_coordinates().ra().getDegrees()
    dec = archive.get_coordinates().dec().getDegrees()
    c = SkyCoord(ra=ra * u.degree, dec=dec * u.degree)

    MJD0 = archive.get_Integration(0).get_epoch().in_days()
    MJDlast = archive.get_Integration(int(archive.get_nsubint()) -
                                      int(1)).get_epoch().in_days()

    MJD0 = Time(MJD0, format='mjd')
    MJDlast = Time(MJDlast, format='mjd')

    elev0 = c.transform_to(AltAz(obstime=MJD0, location=Site))
    elevlast = c.transform_to(AltAz(obstime=MJDlast, location=Site))

    return elev0.alt.degree, elevlast.alt.degree
Пример #34
0
    def earth_location_itrf(self, time=None):
        '''Return NICER spacecraft location in ITRF coordinates'''

        if self.tt2tdb_mode.lower().startswith('none'):
            return None
        elif self.tt2tdb_mode.lower().startswith('geo'):
            return EarthLocation.from_geocentric(0.0*u.m,0.0*u.m,0.0*u.m)
        elif self.tt2tdb_mode.lower().startswith('spacecraft'):
            # First, interpolate ECI geocentric location from orbit file.
            # These are inertial coorinates aligned with ICRF
            pos_gcrs =  GCRS(CartesianRepresentation(self.X(time.tt.mjd)*u.m,
                                                     self.Y(time.tt.mjd)*u.m,
                                                     self.Z(time.tt.mjd)*u.m),
                             obstime=time)

            # Now transform ECI (GCRS) to ECEF (ITRS)
            # By default, this uses the WGS84 ellipsoid
            pos_ITRS = pos_gcrs.transform_to(ITRS(obstime=time))

            # Return geocentric ITRS coordinates as an EarthLocation object
            return pos_ITRS.earth_location
        else:
            log.error('Unknown tt2tdb_mode %s, using None', self.tt2tdb_mode)
            return None
Пример #35
0
def hx_aer_astropy(x_gcrs, time, observer_lla=None, trans_matrix=None, observer_itrs=None):
    """
    desc: measurement function - convert state into a measurement where measurements are [azimuth, elevation]
    :param x_gcrs:
    :param time:
    :param observer_lla:
    :param trans_matrix:
    :param observer_itrs:   Observer coordinates in itrs (meters)
    :return: array[azimuth, elevation, slant]
    """
    object = SkyCoord(x=x_gcrs[0] * u.m, y=x_gcrs[1] * u.m, z=x_gcrs[2] * u.m, frame='gcrs',
                   representation_type='cartesian', obstime=time)
    if observer_itrs is None:
        obs = EarthLocation.from_geodetic(lat=observer_lla[0] * u.rad, lon=observer_lla[1] * u.rad, height=observer_lla[2] * u.m)
    else:
        obs = EarthLocation.from_geocentric(x=observer_itrs[0] * u.m, y=observer_itrs[1] * u.m, z=observer_itrs[2] * u.m)
    AltAz_frame = AltAz(obstime=time, location=obs)
    results = object.transform_to(AltAz_frame)

    az = results.az.to_value(u.rad)
    alt = results.alt.to_value(u.rad)
    sr = results.distance.to_value(u.m)
    aer = np.array([az, alt, sr])
    return aer
Пример #36
0
 def earth_location(self):
     return EarthLocation.from_geocentric(0.0,0.0,0.0,unit=u.m)
Пример #37
0
 def earth_location_itrf(self, time=None):
     return EarthLocation.from_geocentric(0.0,0.0,0.0,unit=u.m)
Пример #38
0
    def __init__(self, name, tempo_code=None, itoa_code=None, aliases=None,
                 itrf_xyz=None, clock_file='time.dat', clock_dir='PINT',
                 clock_fmt='tempo', include_gps=True, include_bipm=True,
                 bipm_version='BIPM2015'):
        """
        Required arguments:

            name     = The name of the observatory
            itrf_xyz = IRTF site coordinates (len-3 array).  Can include
                       astropy units.  If no units are given, meters are
                       assumed.

        Optional arguments:

            tempo_code  = 1-character tempo code for the site.  Will be
                          automatically added to aliases.  Note, this is
                          REQUIRED only if using TEMPO time.dat clock file.
            itoa_code   = 2-character ITOA code.  Will be added to aliases.
            aliases     = List of other aliases for the observatory name.
            clock_file  = Name of the clock correction file.
                          Default='time.dat'
            clock_dir   = Location of the clock file.  Special values
                          'TEMPO', 'TEMPO2', or 'PINT' mean to use the
                          standard directory for the package.  Otherwise
                          can be set to a full path to the directory
                          containing the clock_file.  Default='TEMPO'
            clock_fmt   = Format of clock file (see ClockFile class for allowed
                          values).  Default='tempo'
            include_gps = Set False to disable UTC(GPS)->UTC clock
                          correction.
            include_bipm= Set False to disable UTC-> TT BIPM clock
                          correction. If False, it only apply TAI->TT correction
                          TT = TAI+32.184s, the same as TEMPO2 TT(TAI) in the
                          parfile. If Ture, it will apply the correction from
                          BIPM TT=TT(BIPMYYYY). See the link:
                          http://www.bipm.org/en/bipm-services/timescales/time-ftp/ttbipm.html
            bipm_version= Set the version of TT BIPM clock correction file to
                          use, the default is BIPM2015.  It has to be in the format
                          like 'BIPM2015'
        """

        # ITRF coordinates are required
        if itrf_xyz is None:
            raise ValueError(
                    "ITRF coordinates not given for observatory '%s'" % name)

        # Convert coords to standard format.  If no units are given, assume
        # meters.
        if not has_astropy_unit(itrf_xyz):
            xyz = numpy.array(itrf_xyz) * u.m
        else:
            xyz = itrf_xyz.to(u.m)

        # Check for correct array dims
        if xyz.shape != (3,):
            raise ValueError(
                    "Incorrect coordinate dimensions for observatory '%s'" % (
                        name))

        # Convert to astropy EarthLocation, ensuring use of ITRF geocentric coordinates
        self._loc_itrf = EarthLocation.from_geocentric(*xyz)

        # Save clock file info, the data will be read only if clock
        # corrections for this site are requested.
        self.clock_file = clock_file
        self.clock_dir = clock_dir
        self.clock_fmt = clock_fmt
        self._clock = None # The ClockFile object, will be read on demand

        # If using TEMPO time.dat we need to know the 1-char tempo-style
        # observatory code.
        if (clock_dir=='TEMPO' and clock_file=='time.dat'
                and tempo_code is None):
            raise ValueError("No tempo_code set for observatory '%s'" % name)

        # GPS corrections not implemented yet
        self.include_gps = include_gps
        self._gps_clock = None

        # BIPM corrections not implemented yet
        self.include_bipm = include_bipm
        self.bipm_version = bipm_version
        self._bipm_clock = None

        self.tempo_code = tempo_code
        if aliases is None: aliases = []
        for code in (tempo_code, itoa_code):
            if code is not None: aliases.append(code)

        super(TopoObs,self).__init__(name,aliases=aliases)
Пример #39
0
def _verify_global_info(global_info):
    """
    Given the global time reference frame information, verify that
    each global time coordinate attribute will be given a valid value.

    Parameters
    ----------
    global_info : dict
        Global time reference frame information.
    """

    # Translate FITS deprecated scale into astropy scale, or else just convert
    # to lower case for further checks.
    global_info['scale'] = FITS_DEPRECATED_SCALES.get(global_info['TIMESYS'],
                                                      global_info['TIMESYS'].lower())

    # Verify global time scale
    if global_info['scale'] not in Time.SCALES:

        # 'GPS' and 'LOCAL' are FITS recognized time scale values
        # but are not supported by astropy.

        if global_info['scale'] == 'gps':
            warnings.warn(
                'Global time scale (TIMESYS) has a FITS recognized time scale '
                'value "GPS". In Astropy, "GPS" is a time from epoch format '
                'which runs synchronously with TAI; GPS is approximately 19 s '
                'ahead of TAI. Hence, this format will be used.', AstropyUserWarning)
            # Assume that the values are in GPS format
            global_info['scale'] = 'tai'
            global_info['format'] = 'gps'

        if global_info['scale'] == 'local':
            warnings.warn(
                'Global time scale (TIMESYS) has a FITS recognized time scale '
                'value "LOCAL". However, the standard states that "LOCAL" should be '
                'tied to one of the existing scales because it is intrinsically '
                'unreliable and/or ill-defined. Astropy will thus use the default '
                'global time scale "UTC" instead of "LOCAL".', AstropyUserWarning)
            # Default scale 'UTC'
            global_info['scale'] = 'utc'
            global_info['format'] = None

        else:
            raise AssertionError(
                'Global time scale (TIMESYS) should have a FITS recognized '
                'time scale value (got {!r}). The FITS standard states that '
                'the use of local time scales should be restricted to alternate '
                'coordinates.'.format(global_info['TIMESYS']))
    else:
        # Scale is already set
        global_info['format'] = None

    # Check if geocentric global location is specified
    obs_geo = [global_info[attr] for attr in ('OBSGEO-X', 'OBSGEO-Y', 'OBSGEO-Z')
               if attr in global_info]

    # Location full specification is (X, Y, Z)
    if len(obs_geo) == 3:
        global_info['location'] = EarthLocation.from_geocentric(*obs_geo, unit=u.m)
    else:
        # Check if geodetic global location is specified (since geocentric failed)

        # First warn the user if geocentric location is partially specified
        if obs_geo:
            warnings.warn(
                'The geocentric observatory location {} is not completely '
                'specified (X, Y, Z) and will be ignored.'.format(obs_geo),
                AstropyUserWarning)

        # Check geodetic location
        obs_geo = [global_info[attr] for attr in ('OBSGEO-L', 'OBSGEO-B', 'OBSGEO-H')
                   if attr in global_info]

        if len(obs_geo) == 3:
            global_info['location'] = EarthLocation.from_geodetic(*obs_geo)
        else:
            # Since both geocentric and geodetic locations are not specified,
            # location will be None.

            # Warn the user if geodetic location is partially specified
            if obs_geo:
                warnings.warn(
                    'The geodetic observatory location {} is not completely '
                    'specified (lon, lat, alt) and will be ignored.'.format(obs_geo),
                    AstropyUserWarning)
            global_info['location'] = None

    # Get global time reference
    # Keywords are listed in order of precedence, as stated by the standard
    for key, format_ in (('MJDREF', 'mjd'), ('JDREF', 'jd'), ('DATEREF', 'fits')):
        if key in global_info:
            global_info['ref_time'] = {'val': global_info[key], 'format': format_}
            break
    else:
        # If none of the three keywords is present, MJDREF = 0.0 must be assumed
        global_info['ref_time'] = {'val': 0, 'format': 'mjd'}