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")
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)
def tete_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 greenwich apparent siderial time for the input obstime # we use the 2006A model for consistency with RBPN matrix use in GCRS <-> TETE ujd1, ujd2 = get_jd12(time, 'ut1') jd1, jd2 = get_jd12(time, 'tt') gast = erfa.gst06a(ujd1, ujd2, jd1, jd2) # c2tcio expects a GCRS->CIRS matrix, but we just set that to an I-matrix # because we're already in CIRS equivalent frame return erfa.c2tcio(np.eye(3), gast, pmmat)
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
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 )
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 )
def _polar_mot_matrix(obstime): """ Form the matrix of polar motion for a given date, IAU 2000. The matrix operates in the sense V(TRS) = rpom * V(CIP), meaning that it is the final rotation when computing the pointing direction to a celestial source. Parameters ---------- obstime : Time time at which the polar motion should be calculated. Returns ------- 3x3 rotation matrix due to polar motion """ # compute the polar motion p-matrix xp, yp = get_polar_motion(obstime) # noinspection PyArgumentList sp = erfa.sp00(*get_jd12(obstime, "tt")) polar_mot_mat = erfa.pom00(xp, yp, sp) return polar_mot_mat
def tete_to_itrs_mat(time, rbpn=None): """Compute the polar motion p-matrix at the given time. If the nutation-precession matrix is already known, it should be passed in, as this is by far the most expensive calculation. """ xp, yp = get_polar_motion(time) sp = erfa.sp00(*get_jd12(time, 'tt')) pmmat = erfa.pom00(xp, yp, sp) # now determine the greenwich apparent siderial time for the input obstime # we use the 2006A model for consistency with RBPN matrix use in GCRS <-> TETE ujd1, ujd2 = get_jd12(time, 'ut1') jd1, jd2 = get_jd12(time, 'tt') if rbpn is None: # erfa.gst06a calls pnm06a to calculate rbpn and then gst06. Use it in # favour of getting rbpn with erfa.pnm06a to avoid a possibly large array. gast = erfa.gst06a(ujd1, ujd2, jd1, jd2) else: gast = erfa.gst06(ujd1, ujd2, jd1, jd2, rbpn) # c2tcio expects a GCRS->CIRS matrix, but we just set that to an I-matrix # because we're already in CIRS equivalent frame return erfa.c2tcio(np.eye(3), gast, pmmat)
def test_era(self): """Comare ERA relative to erfa.era00 test case.""" t = Time(2400000.5, 54388.0, format='jd', location=(0, 0), scale='ut1') era = t.earth_rotation_angle() expected = 0.4022837240028158102 * u.radian # Without the TIO locator/polar motion, this should be close already. assert np.abs(era - expected) < 1e-10 * u.radian # And with it, one should reach full precision. sp = erfa.sp00(t.tt.jd1, t.tt.jd2) iers_table = iers.earth_orientation_table.get() xp, yp = [c.to_value(u.rad) for c in iers_table.pm_xy(t)] r = erfa.rx(-yp, erfa.ry(-xp, erfa.rz(sp, np.eye(3)))) expected1 = expected + (np.arctan2(r[0, 1], r[0, 0]) << u.radian) assert np.abs(era - expected1) < 1e-12 * u.radian # Now try at a longitude different from 0. t2 = Time(2400000.5, 54388.0, format='jd', location=(45, 0), scale='ut1') era2 = t2.earth_rotation_angle() r2 = erfa.rz(np.deg2rad(45), r) expected2 = expected + (np.arctan2(r2[0, 1], r2[0, 0]) << u.radian) assert np.abs(era2 - expected2) < 1e-12 * u.radian
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)) # Form celestial-terrestrial matrix (including polar motion). RC2IT = erfa.rxr(RPOM, RC2TI) print("celestial to terrestrial matrix (including polar motion)") pprint(RC2IT) ##B = scipy.matrix(RC2IT) print(''' ================================================ IAU 2000A, equinox based, using classical angles ================================================ ''') # Nutation, IAU 2000A.
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
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)) # Form celestial-terrestrial matrix (including polar motion). RC2IT = erfa.rxr(RPOM, RC2TI) print("celestial to terrestrial matrix (including polar motion)") pprint(RC2IT) ##B = scipy.matrix(RC2IT) print(''' ================================================ IAU 2000A, equinox based, using classical angles ================================================ ''') # Nutation, IAU 2000A.
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
for c,a in erfa.a2af(6, ERA): print(" = %s"%c, end='') print("%dd%dm%d.%ds"%tuple(a)) for c,a in erfa.a2af(6, ERA): print(" = %s"%c, end='') print("%dd%dm%d.%ds"%tuple(a)) # 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(np.array([XP]), np.array([YP]), erfa.sp00(DJMJD0,TT)) # Form celestial-terrestrial matrix (including polar motion). RC2IT = erfa.rxr(RPOM, RC2TI) print("celestial to terrestrial matrix (including polar motion)") pprint(RC2IT) ##B = scipy.matrix(RC2IT) print(''' ================================================ IAU 2000A, equinox based, using classical angles ================================================ ''') # Nutation, IAU 2000A.