Example #1
0
def topo_posvels(xyz, toa):
    """
    topo_posvels(xyz, toa)

    This routine returns a PosVel instance , containing the positions
    (m) and velocities (m / UT1 s) at the time of the toa and
    referenced to the ITRF geocentric coordinates.  This routine is
    basically SOFA's pvtob() with an extra rotation from c2ixys.
    """
    # All the times are passed as TT
    tt = toa.mjd.tt.jd1, toa.mjd.tt.jd2

    # Get a floating-point MJD to use for interpolating IERS values
    mjd = toa.mjd.utc.mjd

    # Gets x,y coords of Celestial Intermediate Pole and CIO locator s
    X, Y, S = erfa.xys00a(*tt)
    # Get dX and dY from IERS A
    #dX = numpy.interp(mjd, iers_tab['MJD'], iers_tab['dX_2000A_B']) * u.arcsec
    #dY = numpy.interp(mjd, iers_tab['MJD'], iers_tab['dY_2000A_B']) * u.arcsec
    # Get dX and dY from IERS B
    dX = numpy.interp(mjd, iers_tab['MJD'], iers_tab['dX_2000A']) * u.arcsec
    dY = numpy.interp(mjd, iers_tab['MJD'], iers_tab['dY_2000A']) * u.arcsec
    # Get GCRS to CIRS matrix
    rc2i = erfa.c2ixys(X+dX.to(u.rad).value, Y+dY.to(u.rad).value, S)

    # Gets the TIO locator s'
    sp = erfa.sp00(*tt)
    # Get X and Y from IERS A
    #xp = numpy.interp(mjd, iers_tab['MJD'], iers_tab['PM_X_B']) * u.arcsec
    #yp = numpy.interp(mjd, iers_tab['MJD'], iers_tab['PM_Y_B']) * u.arcsec
    # Get X and Y from IERS B
    xp = numpy.interp(mjd, iers_tab['MJD'], iers_tab['PM_x']) * u.arcsec
    yp = numpy.interp(mjd, iers_tab['MJD'], iers_tab['PM_y']) * u.arcsec
    # Get the polar motion matrix
    rpm = erfa.pom00(xp.to(u.rad).value, yp.to(u.rad).value, sp)

    # Observatory XYZ coords in meters
    xyzm = [a.to(u.m).value for a in xyz]
    x, y, z = erfa.trxp(rpm, xyzm)

    # Functions of Earth Rotation Angle
    ut1 = toa.mjd.ut1.jd1, toa.mjd.ut1.jd2
    theta = erfa.era00(*ut1)
    s, c = math.sin(theta), math.cos(theta)

    # Position
    pos = numpy.asarray([c*x - s*y, s*x + c*y, z])
    pos = erfa.trxp(rc2i, pos) * u.m

    # Earth rotation rate in radians per UT1 second
    OM = 1.00273781191135448 * 2 * math.pi / erfa.DAYSEC

    # Velocity
    vel = numpy.asarray([OM * (-s*x - c*y), OM * (c*x - s*y), 0.0])
    vel = erfa.trxp(rc2i, vel) * u.m / u.s

    return utils.PosVel(pos, vel, obj=toa.obs, origin="EARTH")
Example #2
0
def cirs_to_itrs_mat(time):
    # compute the polar motion p-matrix
    xp, yp = get_polar_motion(time)
    sp = erfa.sp00(*get_jd12(time, 'tt'))
    pmmat = erfa.pom00(xp, yp, sp)

    # now determine the Earth Rotation Angle for the input obstime
    # era00 accepts UT1, so we convert if need be
    era = erfa.era00(*get_jd12(time, 'ut1'))

    # c2tcio expects a GCRS->CIRS matrix, but we just set that to an I-matrix
    # because we're already in CIRS
    return erfa.c2tcio(np.eye(3), era, pmmat)
Example #3
0
    def apio(frame_or_coord):
        '''
        Slightly modified equivalent of ``erfa.apio``, used in conversions AltAz <-> CIRS.

        Since we use a topocentric CIRS frame, we have dropped the steps needed to calculate
        diurnal aberration.

        Arguments
        ---------
        frame_or_coord: ``astropy.coordinates.BaseCoordinateFrame`` or ``astropy.coordinates.SkyCoord``
            Frame or coordinate instance in the corresponding frame
            for which to calculate the calculate the astrom values.
            For this function, an AltAz frame is expected.
        '''
        lon, lat, height = frame_or_coord.location.to_geodetic('WGS84')
        jd1_tt, jd2_tt = get_jd12(frame_or_coord.obstime, 'tt')

        # we need an empty astrom structure before we fill in the required sections
        astrom = np.zeros(frame_or_coord.obstime.shape, dtype=erfa.dt_eraASTROM)

        # longitude with adjustment for TIO locator s'
        astrom['along'] = lon.to_value(u.radian) + erfa.sp00(jd1_tt, jd2_tt)

        # Polar motion, rotated onto local meridian
        xp, yp = get_polar_motion(frame_or_coord.obstime)
        sl = np.sin(astrom['along'])
        cl = np.cos(astrom['along'])
        astrom['xpl'] = xp*cl - yp*sl
        astrom['ypl'] = xp*sl + yp*cl

        # Functions of latitude
        astrom['sphi'] = np.sin(lat)
        astrom['cphi'] = np.cos(lat)

        # refraction constants
        astrom['refa'], astrom['refb'] = erfa.refco(
            frame_or_coord.pressure.to_value(u.hPa),
            frame_or_coord.temperature.to_value(u.deg_C),
            frame_or_coord.relative_humidity.value,
            frame_or_coord.obswl.to_value(u.micron)
        )

        # update local earth rotation angle
        astrom['eral'] = erfa.era00(*get_jd12(frame_or_coord.obstime, 'ut1')) + astrom['along']
        return astrom
Example #4
0
    def apco(self, frame_or_coord):
        '''
        Wrapper for ``erfa.apco``, used in conversions AltAz <-> ICRS and CIRS <-> ICRS

        Arguments
        ---------
        frame_or_coord: ``astropy.coordinates.BaseCoordinateFrame`` or ``astropy.coordinates.SkyCoord``
            Frame or coordinate instance in the corresponding frame
            for which to calculate the calculate the astrom values.
            For this function, an AltAz or CIRS frame is expected.
        '''
        lon, lat, height = frame_or_coord.location.to_geodetic('WGS84')
        obstime = frame_or_coord.obstime
        support = self._get_support_points(obstime)
        jd1_tt, jd2_tt = get_jd12(obstime, 'tt')

        # get the position and velocity arrays for the observatory.  Need to
        # have xyz in last dimension, and pos/vel in one-but-last.
        earth_pv, earth_heliocentric = self._prepare_earth_position_vel(support, obstime)

        xp, yp = self._get_polar_motion(support, obstime)
        sp = erfa.sp00(jd1_tt, jd2_tt)
        x, y, s = self._get_cip(support, obstime)
        era = erfa.era00(*get_jd12(obstime, 'ut1'))

        # refraction constants
        if hasattr(frame_or_coord, 'pressure'):
            # an AltAz like frame. Include refraction
            refa, refb = erfa.refco(
                frame_or_coord.pressure.to_value(u.hPa),
                frame_or_coord.temperature.to_value(u.deg_C),
                frame_or_coord.relative_humidity.value,
                frame_or_coord.obswl.to_value(u.micron)
            )
        else:
            # a CIRS like frame - no refraction
            refa, refb = 0.0, 0.0

        return erfa.apco(
            jd1_tt, jd2_tt, earth_pv, earth_heliocentric, x, y, s, era,
            lon.to_value(u.radian),
            lat.to_value(u.radian),
            height.to_value(u.m),
            xp, yp, sp, refa, refb
        )
Example #5
0
    def apco(frame_or_coord):
        '''
        Wrapper for ``erfa.apco``, used in conversions AltAz <-> ICRS and CIRS <-> ICRS

        Arguments
        ---------
        frame_or_coord: ``astropy.coordinates.BaseCoordinateFrame`` or ``astropy.coordinates.SkyCoord``
            Frame or coordinate instance in the corresponding frame
            for which to calculate the calculate the astrom values.
            For this function, an AltAz or CIRS frame is expected.
        '''
        lon, lat, height = frame_or_coord.location.to_geodetic('WGS84')
        obstime = frame_or_coord.obstime

        jd1_tt, jd2_tt = get_jd12(obstime, 'tt')
        xp, yp = get_polar_motion(obstime)
        sp = erfa.sp00(jd1_tt, jd2_tt)
        x, y, s = get_cip(jd1_tt, jd2_tt)
        era = erfa.era00(*get_jd12(obstime, 'ut1'))
        earth_pv, earth_heliocentric = prepare_earth_position_vel(obstime)

        # refraction constants
        if hasattr(frame_or_coord, 'pressure'):
            # this is an AltAz like frame. Calculate refraction
            refa, refb = erfa.refco(
                frame_or_coord.pressure.to_value(u.hPa),
                frame_or_coord.temperature.to_value(u.deg_C),
                frame_or_coord.relative_humidity.value,
                frame_or_coord.obswl.to_value(u.micron)
            )
        else:
            # This is not an AltAz frame, so don't bother computing refraction
            refa, refb = 0.0, 0.0

        return erfa.apco(
            jd1_tt, jd2_tt, earth_pv, earth_heliocentric, x, y, s, era,
            lon.to_value(u.radian),
            lat.to_value(u.radian),
            height.to_value(u.m),
            xp, yp, sp, refa, refb
        )
Example #6
0
X, Y, S = erfa.xys00a(DJMJD0, TT)

# Add CIP corrections.
X = X + DX00
Y = Y + DY00

print("CIP corrections")
print('X = %.17f\nY = %.17f\nS = %.17f' % (X, Y, S * R2AS))

# GCRS to CIRS matrix.
RC2I = erfa.c2ixys(X, Y, S)
print("NPB matrix, CIO based")
pprint(RC2I)

# Earth rotation angle.
ERA = erfa.era00(DJMJD0 + DATE, TUT)
print("Earth rotation angle")
print('ERA = %.17f radians' % ERA)
print("    = %.17f degrees" % math.degrees(ERA))
print("    = %s%dd%dm%d.%ds" % erfa.a2af(6, ERA))
print("    = %s%dh%dm%d.%ds" % erfa.a2tf(6, ERA))

# Form celestial-terrestrial matrix (no polar motion yet).
##RC2TI = erfa.cr(RC2I)
##RC2TI = erfa.rz(ERA, RC2TI)
RC2TI = erfa.rz(ERA, RC2I)
print("celestial to terrestrial matrix (no polar motion)")
pprint(RC2TI)

# Polar motion matrix (TIRS->ITRS, IERS 2003).
RPOM = erfa.pom00(XP, YP, erfa.sp00(DJMJD0, TT))
Example #7
0
    def observationInfo2visitInfo(obsInfo, log=None):
        """Construct a `~lsst.afw.image.VisitInfo` from an
        `~astro_metadata_translator.ObservationInfo`

        Parameters
        ----------
        obsInfo : `astro_metadata_translator.ObservationInfo`
            Information gathered from the observation metadata.
        log : `logging.Logger` or `lsst.log.Log`, optional
            Logger to use for logging informational messages.
            If `None` logging will be disabled.

        Returns
        -------
        visitInfo : `lsst.afw.image.VisitInfo`
            `~lsst.afw.image.VisitInfo` derived from the supplied
            `~astro_metadata_translator.ObservationInfo`.
        """
        argDict = dict()

        # Map the translated information into a form suitable for VisitInfo
        if obsInfo.exposure_time is not None:
            argDict["exposureTime"] = obsInfo.exposure_time.to_value("s")
        if obsInfo.dark_time is not None:
            argDict["darkTime"] = obsInfo.dark_time.to_value("s")
        argDict["exposureId"] = obsInfo.detector_exposure_id

        # VisitInfo uses the middle of the observation for the date
        if obsInfo.datetime_begin is not None and obsInfo.datetime_end is not None:
            tdelta = obsInfo.datetime_end - obsInfo.datetime_begin
            middle = obsInfo.datetime_begin + 0.5 * tdelta

            # DateTime uses nanosecond resolution, regardless of the resolution
            # of the original date
            middle.precision = 9
            # isot is ISO8601 format with "T" separating date and time and no
            # time zone
            argDict["date"] = DateTime(middle.tai.isot, DateTime.TAI)

            # Derive earth rotation angle from UT1 (being out by a second is
            # not a big deal given the uncertainty over exactly what part of
            # the observation we are needing it for).
            # ERFA needs a UT1 time split into two floats
            # We ignore any problems with DUT1 not being defined for now.
            try:
                # Catch any warnings about the time being in the future
                # since there is nothing we can do about that for simulated
                # data and it tells us nothing for data from the past.
                with warnings.catch_warnings():
                    # If we are using the real erfa it is not an AstropyWarning
                    # During transition period filter both
                    warnings.simplefilter(
                        "ignore",
                        category=astropy.utils.exceptions.AstropyWarning)
                    if ErfaWarning is not None:
                        warnings.simplefilter("ignore", category=ErfaWarning)
                    ut1time = middle.ut1
            except iers.IERSRangeError:
                ut1time = middle

            era = erfa.era00(ut1time.jd1, ut1time.jd2)
            argDict["era"] = era * radians
        else:
            argDict["date"] = DateTime()

        # Coordinates
        if obsInfo.tracking_radec is not None:
            icrs = obsInfo.tracking_radec.transform_to("icrs")
            argDict["boresightRaDec"] = SpherePoint(icrs.ra.degree,
                                                    icrs.dec.degree,
                                                    units=degrees)

        altaz = obsInfo.altaz_begin
        if altaz is not None:
            argDict["boresightAzAlt"] = SpherePoint(altaz.az.degree,
                                                    altaz.alt.degree,
                                                    units=degrees)

        argDict["boresightAirmass"] = obsInfo.boresight_airmass

        if obsInfo.boresight_rotation_angle is not None:
            argDict[
                "boresightRotAngle"] = obsInfo.boresight_rotation_angle.degree * degrees

        if obsInfo.boresight_rotation_coord is not None:
            rotType = RotType.UNKNOWN
            if obsInfo.boresight_rotation_coord == "sky":
                rotType = RotType.SKY
            argDict["rotType"] = rotType

        # Weather and Observatory Location
        temperature = float("nan")
        if obsInfo.temperature is not None:
            temperature = obsInfo.temperature.to_value(
                "deg_C", astropy.units.temperature())
        pressure = float("nan")
        if obsInfo.pressure is not None:
            pressure = obsInfo.pressure.to_value("Pa")
        relative_humidity = float("nan")
        if obsInfo.relative_humidity is not None:
            relative_humidity = obsInfo.relative_humidity
        argDict["weather"] = Weather(temperature, pressure, relative_humidity)

        if obsInfo.location is not None:
            geolocation = obsInfo.location.to_geodetic()
            argDict["observatory"] = Observatory(
                geolocation.lon.degree * degrees,
                geolocation.lat.degree * degrees,
                geolocation.height.to_value("m"))

        for key in list(
                argDict.keys()):  # use a copy because we may delete items
            if argDict[key] is None:
                if log is not None:
                    log.warn("argDict[%s] is None; stripping", key)
                del argDict[key]

        return VisitInfo(**argDict)
Example #8
0
    def apio(frame_or_coord):
        '''
        Slightly modified equivalent of ``erfa.apio``, used in conversions AltAz <-> CIRS.

        Since we use a topocentric CIRS frame, we have dropped the steps needed to calculate
        diurnal aberration.

        Parameters
        ----------
        frame_or_coord : ``astropy.coordinates.BaseCoordinateFrame`` or ``astropy.coordinates.SkyCoord``
            Frame or coordinate instance in the corresponding frame
            for which to calculate the calculate the astrom values.
            For this function, an AltAz frame is expected.
        '''
        # Calculate erfa.apio input parameters.
        # TIO locator s'
        sp = erfa.sp00(*get_jd12(frame_or_coord.obstime, 'tt'))

        # Earth rotation angle.
        theta = erfa.era00(*get_jd12(frame_or_coord.obstime, 'ut1'))

        # Longitude and latitude in radians.
        lon, lat, height = frame_or_coord.location.to_geodetic('WGS84')
        elong = lon.to_value(u.radian)
        phi = lat.to_value(u.radian)

        # Polar motion, rotated onto local meridian
        xp, yp = get_polar_motion(frame_or_coord.obstime)

        # we need an empty astrom structure before we fill in the required sections
        astrom = np.zeros(frame_or_coord.obstime.shape,
                          dtype=erfa.dt_eraASTROM)

        # Form the rotation matrix, CIRS to apparent [HA,Dec].
        r = (rotation_matrix(elong, 'z', unit=u.radian) @ rotation_matrix(
            -yp, 'x', unit=u.radian) @ rotation_matrix(-xp, 'y', unit=u.radian)
             @ rotation_matrix(theta + sp, 'z', unit=u.radian))

        # Solve for local Earth rotation angle.
        a = r[..., 0, 0]
        b = r[..., 0, 1]
        eral = np.arctan2(b, a)
        astrom['eral'] = eral

        # Solve for polar motion [X,Y] with respect to local meridian.
        c = r[..., 0, 2]
        astrom['xpl'] = np.arctan2(c, np.sqrt(a * a + b * b))
        a = r[..., 1, 2]
        b = r[..., 2, 2]
        astrom['ypl'] = -np.arctan2(a, b)

        # Adjusted longitude.
        astrom['along'] = erfa.anpm(eral - theta)

        # Functions of latitude.
        astrom['sphi'] = np.sin(phi)
        astrom['cphi'] = np.cos(phi)

        # Omit two steps that are zero for a geocentric observer:
        # Observer's geocentric position and velocity (m, m/s, CIRS).
        # Magnitude of diurnal aberration vector.

        # Refraction constants.
        astrom['refa'], astrom['refb'] = erfa.refco(
            frame_or_coord.pressure.to_value(u.hPa),
            frame_or_coord.temperature.to_value(u.deg_C),
            frame_or_coord.relative_humidity.value,
            frame_or_coord.obswl.to_value(u.micron))
        return astrom
Example #9
0
X, Y, S = erfa.xys00a(DJMJD0, TT)

# Add CIP corrections.
X = X + DX00
Y = Y + DY00

print("CIP corrections")
print('X = %.17f\nY = %.17f\nS = %.17f'%(X, Y, S*R2AS))

# GCRS to CIRS matrix.
RC2I = erfa.c2ixys(X, Y, S)
print("NPB matrix, CIO based")
pprint(RC2I)

# Earth rotation angle.
ERA = erfa.era00(DJMJD0+DATE, TUT)
print("Earth rotation angle")
print('ERA = %.17f radians'%ERA)
print("    = %.17f degrees"%math.degrees(ERA))
print("    = %s%dd%dm%d.%ds"%erfa.a2af(6, ERA))
print("    = %s%dh%dm%d.%ds"%erfa.a2tf(6, ERA))

# Form celestial-terrestrial matrix (no polar motion yet).
##RC2TI = erfa.cr(RC2I)
##RC2TI = erfa.rz(ERA, RC2TI)
RC2TI = erfa.rz(ERA, RC2I)
print("celestial to terrestrial matrix (no polar motion)")
pprint(RC2TI)

# Polar motion matrix (TIRS->ITRS, IERS 2003).
RPOM = erfa.pom00(XP, YP, erfa.sp00(DJMJD0,TT))
Example #10
0
def old_gcrs_posvel_from_itrf(loc, toas, obsname="obs"):
    """Return a list of PosVel instances for the observatory at the TOA times.

    Observatory location should be given in the loc argument as an astropy
    EarthLocation object. This location will be in the ITRF frame (i.e.
    co-rotating with the Earth).

    The optional obsname argument will be used as label in the returned
    PosVel instance.

    This routine returns a list of PosVel instances, containing the
    positions (m) and velocities (m / s) at the times of the toas and
    referenced to the Earth-centered Inertial (ECI, aka GCRS) coordinates.
    This routine is basically SOFA's pvtob() [Position and velocity of
    a terrestrial observing station] with an extra rotation from c2ixys()
    [Form the celestial to intermediate-frame-of-date matrix given the CIP
    X,Y and the CIO locator s].
    """
    unpack = False
    # If the input is a single TOA (i.e. a row from the table),
    # then put it into a list
    if type(toas) == table.row.Row:
        ttoas = Time([toas["mjd"]])
        unpack = True
    elif type(toas) == table.table.Table:
        ttoas = toas["mjd"]
    elif isinstance(toas, Time):
        if toas.isscalar:
            ttoas = Time([toas])
            unpack = True
        else:
            ttoas = toas
    else:
        if np.isscalar(toas):
            ttoas = Time([toas], format="mjd")
            unpack = True
        else:
            ttoas = toas
    N = len(ttoas)
    if len(ttoas.shape) != 1:
        raise ValueError("At most one-dimensional array of times possible, "
                         "shape was {}".format(ttoas.shape))

    # Get various times from the TOAs as arrays
    tts = np.asarray([(t.jd1, t.jd2) for t in ttoas.tt]).T
    ut1s = np.asarray([(t.jd1, t.jd2) for t in ttoas.ut1]).T
    mjds = np.asarray(ttoas.mjd)

    iers_b = get_iers_b_up_to_date(mjds.max())

    # Get x, y coords of Celestial Intermediate Pole and CIO locator s
    X, Y, S = erfa.xys00a(*tts)

    # Get dX and dY from IERS A in arcsec and convert to radians
    # dX = np.interp(mjds, iers_tab['MJD'], iers_tab['dX_2000A_B']) * asec2rad
    # dY = np.interp(mjds, iers_tab['MJD'], iers_tab['dY_2000A_B']) * asec2rad
    # Get dX and dY from IERS B in arcsec and convert to radians
    dX = np.interp(mjds, iers_b["MJD"].to_value(u.d),
                   iers_b["dX_2000A"].to_value(u.rad))
    dY = np.interp(mjds, iers_b["MJD"].to_value(u.d),
                   iers_b["dY_2000A"].to_value(u.rad))

    # Get GCRS to CIRS matrices
    rc2i = erfa.c2ixys(X + dX, Y + dY, S)

    # Gets the TIO locator s'
    sp = erfa.sp00(*tts)

    # Get X and Y from IERS A in arcsec and convert to radians
    # xp = np.interp(mjds, iers_tab['MJD'], iers_tab['PM_X_B']) * asec2rad
    # yp = np.interp(mjds, iers_tab['MJD'], iers_tab['PM_Y_B']) * asec2rad
    # Get X and Y from IERS B in arcsec and convert to radians
    xp = np.interp(mjds, iers_b["MJD"].to_value(u.d),
                   iers_b["PM_x"].to_value(u.rad))
    yp = np.interp(mjds, iers_b["MJD"].to_value(u.d),
                   iers_b["PM_y"].to_value(u.rad))

    # Get the polar motion matrices
    rpm = erfa.pom00(xp, yp, sp)

    # Observatory geocentric coords in m
    xyzm = np.array([a.to_value(u.m) for a in loc.geocentric])
    x, y, z = np.dot(xyzm, rpm).T

    # Functions of Earth Rotation Angle
    theta = erfa.era00(*ut1s)
    s, c = np.sin(theta), np.cos(theta)
    sx, cx = s * x, c * x
    sy, cy = s * y, c * y

    # Initial positions and velocities
    iposs = np.asarray([cx - sy, sx + cy, z]).T
    ivels = np.asarray([OM * (-sx - cy), OM * (cx - sy), np.zeros_like(x)]).T
    # There is probably a way to do this with np.einsum or something...
    # and here it is .
    poss = np.empty((N, 3), dtype=np.float64)
    vels = np.empty((N, 3), dtype=np.float64)
    poss = np.einsum("ij,ijk->ik", iposs, rc2i)
    vels = np.einsum("ij,ijk->ik", ivels, rc2i)
    r = PosVel(poss.T * u.m, vels.T * u.m / u.s, obj=obsname, origin="earth")
    if unpack:
        return r[0]
    else:
        return r