def test_posvel_reject_bogus_sizes(): with pytest.raises(ValueError): PosVel([1, 0], [1, 0, 0]) with pytest.raises(ValueError): PosVel([1, 0, 0], [1, 0, 0, 0]) with pytest.raises(ValueError): PosVel(np.array([1, 0]) * u.m, [1, 0, 0]) with pytest.raises(ValueError): PosVel([1, 0, 0, 0], np.array([1, 0]) * u.m)
def posvel(self, t, ephem): """Return position and velocity vectors of satellite, wrt SSB. These positions and velocites are in inertial coordinates (i.e. aligned with ICRS) t is an astropy.Time or array of astropy.Times """ # Compute vector from SSB to Earth geo_posvel = objPosVel_wrt_SSB("earth", t, ephem) # Now add vector from Earth to satellite sat_pos_geo = ( np.array([self.X(t.tt.mjd), self.Y(t.tt.mjd), self.Z(t.tt.mjd)]) * self.FT2["X"].unit) log.debug("[{0}] sat_pos_geo {1}".format(self.name, sat_pos_geo[:, 0])) sat_vel_geo = ( np.array([self.Vx(t.tt.mjd), self.Vy(t.tt.mjd), self.Vz(t.tt.mjd)]) * self.FT2["Vx"].unit) sat_posvel = PosVel(sat_pos_geo, sat_vel_geo, origin="earth", obj=self.name) # Vector add to geo_posvel to get full posvel vector. return geo_posvel + sat_posvel
def posvel_gcrs(self, t, maxextrap=2): """Return GCRS position and velocity vectors of NICER. t is an astropy.Time or array of astropy.Times maxextrap is the longest (in minutes) it is acceptable to extrapolate the S/C position """ # this is a simple edge check mainly to prevent use of the wrong # orbit file or a single orbit file with a merged event file; if # needed, can check to make sure there is a spline anchor point # sufficiently close to all event times tmin = np.min(self.FPorb["MJD_TT"]) tmax = np.max(self.FPorb["MJD_TT"]) if tmin - np.min(t.tt.mjd) > float(maxextrap) / (60 * 24) or np.max( t.tt.mjd) - tmax > float(maxextrap) / (60 * 24): log.error("Extrapolating NICER position by more than %d minutes!" % maxextrap) raise ValueError("Bad extrapolation of S/C file.") # Now add vector from Earth to NICER nicer_pos_geo = ( np.array([self.X(t.tt.mjd), self.Y(t.tt.mjd), self.Z(t.tt.mjd)]) * self.FPorb["X"].unit) nicer_vel_geo = ( np.array([self.Vx(t.tt.mjd), self.Vy(t.tt.mjd), self.Vz(t.tt.mjd)]) * self.FPorb["Vx"].unit) nicer_posvel = PosVel(nicer_pos_geo, nicer_vel_geo, origin="earth", obj="nicer") # Vector add to geo_posvel to get full posvel vector. return nicer_posvel
def test_posvel_slice_indexing(pos_vel_ix): pos, vel, ix = pos_vel_ix pv = PosVel(pos, vel) pvs = pv[ix] ix_full = (slice(None, None, None), ) + ix assert_array_equal(pvs.pos, pos[ix_full]) assert_array_equal(pvs.vel, vel[ix_full])
def objPosVel_wrt_SSB(objname, t, ephem, path=None, link=None): """This function computes a solar system object position and velocity respect to solar system barycenter using astropy coordinates get_body_barycentric() method. The coordinate frame is that of the underlying solar system ephemeris, which has been the ICRF (J2000) since the DE4XX series. Parameters ---------- objname: str Solar system object name. Current support solar system bodies are listed in astropy.coordinates.solar_system_ephemeris.bodies attribution. t: Astropy.time.Time object Observation time in Astropy.time.Time object format. ephem: str The ephem to for computing solar system object position and velocity path : str, optional Local path to the ephemeris file. link : str, optional Location of path on the internet. Returns ------- PosVel object with 3-vectors for the position and velocity of the object """ objname = objname.lower() load_kernel(ephem, path=path, link=link) pos, vel = astropy.coordinates.get_body_barycentric_posvel(objname, t) return PosVel(pos.xyz, vel.xyz.to(u.km / u.second), origin="ssb", obj=objname)
def posvel_gcrs(self, t, group, ephem=None): """Return spacecraft GCRS position and velocity; this assumes position flags in tim file are in km and velocity flags are in km/s""" if group is None: raise ValueError( "TOA group table needed for SpacecraftObs posvel_gcrs") try: vx = np.array([flags["vx"] for flags in group["flags"]]) vy = np.array([flags["vy"] for flags in group["flags"]]) vz = np.array([flags["vz"] for flags in group["flags"]]) except: log.error( "Missing flag. TOA line should have vx,vy,vz flags for GCRS velocity in km/s." ) raise ValueError( "Missing flag. TOA line should have vx,vy,vz flags for GCRS velocity in km/s." ) vel_geo = np.vstack((vx, vy, vz)) * (u.km / u.s) vdim = (3, ) + t.shape if vel_geo.shape != vdim: raise ValueError( "GCRS velocity vector has wrong shape: ", vel.shape, " instead of ", vdim.shape, ) pos_geo = self.get_gcrs(t, group, ephem=None) stl_posvel = PosVel(pos_geo, vel_geo, origin="earth", obj="spacecraft") return stl_posvel
def posvel(self, t, ephem): vdim = (3, ) + t.shape return PosVel( numpy.zeros(vdim) * u.m, numpy.zeros(vdim) * u.m / u.s, obj=self.name, origin="ssb", )
def test_posvel_respects_longdouble(): pos = np.ones(3, dtype=np.longdouble) pos[0] += np.finfo(np.longdouble).eps vel = np.ones(3, dtype=np.longdouble) vel[1] += np.finfo(np.longdouble).eps pv = PosVel(pos, vel) assert_array_equal(pv.pos, pos) assert_array_equal(pv.vel, vel)
def posvel(self, t, ephem, group=None): vdim = (3, ) + t.shape return PosVel( np.zeros(vdim) * u.m, np.zeros(vdim) * u.m / u.s, obj=self.name, origin="ssb", )
def test_posvel_respects_label_constraints(): p1 = [1, 0, 0] p2 = [0, 1, 0] v1 = [1, 0, 0] v2 = [0, 1, 0] earth_mars = PosVel(p1, v1, origin="earth", obj="mars") mars_earth = PosVel(p2, v2, origin="mars", obj="earth") pv = earth_mars + mars_earth assert pv.origin == pv.obj == "earth" pv = mars_earth + earth_mars assert pv.origin == pv.obj == "mars" with pytest.raises(ValueError): pv = earth_mars - mars_earth with pytest.raises(ValueError): PosVel(p2, v2, origin="else") with pytest.raises(ValueError): PosVel(p2, v2, obj="else")
def test_posvel_str_sensible(): assert "->" in str(PosVel([1, 0, 0], [0, 1, 0], "earth", "mars")) assert "earth" in str(PosVel([1, 0, 0], [0, 1, 0], "earth", "mars")) assert "mars" in str(PosVel([1, 0, 0], [0, 1, 0], "earth", "mars")) assert "->" not in str(PosVel([1, 0, 0], [0, 1, 0])) assert "17" in str(PosVel([17, 0, 0], [0, 1, 0])) assert str(PosVel([17, 0, 0], [0, 1, 0])).startswith("PosVel(")
def 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]. This version uses astropy's internal routines, which use IERS A data rather than the final IERS B values. These do differ, and yield results that are different by ~20 m. """ 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 t = ttoas pos, vel = loc.get_gcrs_posvel(t) r = PosVel(pos.xyz, vel.xyz, obj=obsname, origin="earth") if unpack: return r[0] else: return r
def posvel_gcrs(self, t, ephem=None): """Return GCRS position and velocity vectors of S/C. t is an astropy.Time or array of astropy.Times """ self._check_bounds(t) # Compute vector from Earth to satellite sat_pos_geo = ( np.array([self.X(t.tt.mjd), self.Y(t.tt.mjd), self.Z(t.tt.mjd)]) * self.FT2["X"].unit ) log.debug("[{0}] sat_pos_geo {1}".format(self.name, sat_pos_geo[:, 0])) sat_vel_geo = ( np.array([self.Vx(t.tt.mjd), self.Vy(t.tt.mjd), self.Vz(t.tt.mjd)]) * self.FT2["Vx"].unit ) sat_posvel = PosVel(sat_pos_geo, sat_vel_geo, origin="earth", obj=self.name) return sat_posvel
def objPosVel(obj1, obj2, t, ephem, path=None, link=None): """Compute the position and velocity for solar system obj2 referenced at obj1. This function uses astropy solar system Ephemerides module. Parameters ---------- obj1: str The name of reference solar system object obj2: str The name of target solar system object tdb: Astropy.time.Time object TDB time in Astropy.time.Time object format ephem: str The ephem to for computing solar system object position and velocity path : str, optional Local path to the ephemeris file. link : str, optional Location of path on the internet. Return ------ PosVel object. solar system obj1's position and velocity with respect to obj2 in the J2000 cartesian coordinate. """ if obj1.lower() == "ssb" and obj2.lower() != "ssb": obj2pv = objPosVel_wrt_SSB(obj2, t, ephem, path=path, link=link) return obj2pv elif obj2.lower() == "ssb" and obj1.lower() != "ssb": obj1pv = objPosVel_wrt_SSB(obj1, t, ephem, path=path, link=link) return -obj1pv elif obj2.lower() != "ssb" and obj1.lower() != "ssb": obj1pv = objPosVel_wrt_SSB(obj1, t, ephem, path=path, link=link) obj2pv = objPosVel_wrt_SSB(obj2, t, ephem, path=path, link=link) return obj2pv - obj1pv else: # user asked for velocity between ssb and ssb return PosVel( np.zeros((3, len(t))) * u.km, np.zeros((3, len(t))) * u.km / u.second)
def posvel(self, t, ephem): """Return position and velocity vectors of NuSTAR. t is an astropy.Time or array of astropy.Times """ # Compute vector from SSB to Earth geo_posvel = objPosVel_wrt_SSB("earth", t, ephem) # Now add vector from Earth to NuSTAR nustar_pos_geo = ( np.array([self.X(t.tt.mjd), self.Y(t.tt.mjd), self.Z(t.tt.mjd)]) * self.FPorb["X"].unit) nustar_vel_geo = ( np.array([self.Vx(t.tt.mjd), self.Vy(t.tt.mjd), self.Vz(t.tt.mjd)]) * self.FPorb["Vx"].unit) nustar_posvel = PosVel(nustar_pos_geo, nustar_vel_geo, origin="earth", obj="nustar") # Vector add to geo_posvel to get full posvel vector. return geo_posvel + nustar_posvel
log.info("TOA in tt difference is: %.2f ns" % \ ((TOA['mjd'].tt - tempo_tt.tt).sec * u.s).to(u.ns).value) pint_opv = erfautils.topo_posvels(Observatory.get( TOA['obs']).earth_location, TOA, obsname=TOA['obs']) pint_opv = utils.PosVel(pint_opv.pos.T[0], pint_opv.vel.T[0]) #print " obs T2:", t2_opv.pos.to(u.m).value, t2_opv.vel.to(u.m/u.s) #print " obs PINT:", pint_opv.pos.to(u.m), pint_opv.vel.to(u.m/u.s) dopv = pint_opv - t2_opv dpos = numpy.sqrt(numpy.dot(dopv.pos.to(u.m), dopv.pos.to(u.m))) dvel = numpy.sqrt( numpy.dot(dopv.vel.to(u.mm / u.s), dopv.vel.to(u.mm / u.s))) log.info(" obs diff: %.2f m, %.3f mm/s" % (dpos, dvel)) assert (dpos < 2.0 and dvel < 0.02) pint_ssb2obs = PosVel(numpy.asarray(TOA['ssb_obs_pos']) * u.km, numpy.asarray(TOA['ssb_obs_vel']) * u.km / u.s, origin='SSB', obj='OBS') #print " topo T2:", t2_ssb2obs.pos.to(u.km), t2_ssb2obs.vel.to(u.km/u.s) #print " topo PINT:", pint_ssb2obs.pos.to(u.km), pint_ssb2obs.vel.to(u.km/u.s) dtopo = pint_ssb2obs - t2_ssb2obs dpos = numpy.sqrt(numpy.dot(dtopo.pos.to(u.m), dtopo.pos.to(u.m))) dvel = numpy.sqrt( numpy.dot(dtopo.vel.to(u.mm / u.s), dtopo.vel.to(u.mm / u.s))) log.info(" topo diff: %.2f m, %.3f m/s" % (dpos, dvel)) assert (dpos < 2.0 and dvel < 0.02)
def test_posvel_broadcast_retains_quantity(pos_vel_shape, l_unit, t_unit): pos, vel, shape = pos_vel_shape pv = PosVel(pos * l_unit, vel * l_unit / t_unit) assert pv.pos.shape == pv.vel.shape == shape assert pv.pos.unit == l_unit assert pv.vel.unit == l_unit / t_unit
def test_posvel_broadcasts(pos_vel_shape): pos, vel, shape = pos_vel_shape pv = PosVel(pos, vel) assert pv.pos.shape == pv.vel.shape == shape
def test_posvel_different_lengths_raises(): pos = np.random.randn(3, 4, 5) vel = np.random.randn(3, 6) with pytest.raises(ValueError): PosVel(pos, vel)
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
def test_times_against_tempo2(): log.setLevel("ERROR") # for nice output info, set the following instead # log.setLevel('INFO') ls = u.def_unit("ls", const.c * 1.0 * u.s) log.info("Reading TOAs into PINT") ts = toa.get_TOAs(datadir + "/testtimes.tim", include_bipm=False, usepickle=False) if log.level < 25: ts.print_summary() ts.table.sort("index") log.info("Calling TEMPO2") # cmd = 'tempo2 -output general2 -f tests/testtimes.par tests/testtimes.tim -s "XXX {clock0} {clock1} {clock2} {clock3} {tt} {t2tb} {telSSB} {telVel} {Ttt}\n"' # cmd = 'tempo2 -output general2 -f ' + datadir+'/testtimes.par ' + datadir + \ # '/testtimes.tim -s "XXX {clock0} {clock1} {clock2} {clock3} {tt} {t2tb} {earth_ssb1} {earth_ssb2} {earth_ssb3} {earth_ssb4} {earth_ssb5} {earth_ssb6} {telEpos} {telEVel} {Ttt}\n"' # args = shlex.split(cmd) # # tout = subprocess.check_output(args) # goodlines = [x for x in tout.split("\n") if x.startswith("XXX")] # # assert(len(goodlines)==len(ts.table)) # t2result = numpy.genfromtxt('datafile/testtimes.par' + '.tempo2_test', names=True, comments = '#') f = open(datadir + "/testtimes.par" + ".tempo2_test") lines = f.readlines() goodlines = lines[1:] # Get the output lines from the TOAs for line, TOA in zip(goodlines, ts.table): assert (len(line.split()) == 19 ), "tempo2 general2 does not support all needed outputs" ( oclk, gps_utc, tai_utc, tt_tai, ttcorr, tt2tb, ep0, ep1, ep2, ev0, ev1, ev2, tp0, tp1, tp2, tv0, tv1, tv2, Ttt, ) = (float(x) for x in line.split()) t2_epv = PosVel( numpy.asarray([ep0, ep1, ep2]) * ls, numpy.asarray([ev0, ev1, ev2]) * ls / u.s, ) t2_opv = PosVel( numpy.asarray([tp0, tp1, tp2]) * ls, numpy.asarray([tv0, tv1, tv2]) * ls / u.s, ) t2_ssb2obs = t2_epv + t2_opv # print time_to_mjd_string(TOA.mjd.tt), line.split()[-1] tempo_tt = Time(line.split()[-1], format="mjd_string", scale="tt") # Ensure that the clock corrections are accurate to better than 0.1 ns assert (math.fabs( (oclk * u.s + gps_utc * u.s - TOA["flags"]["clkcorr"]).to( u.ns).value) < 0.1) log.info("TOA in tt difference is: %.2f ns" % ((TOA["mjd"].tt - tempo_tt.tt).sec * u.s).to(u.ns).value) pint_opv = erfautils.gcrs_posvel_from_itrf(Observatory.get( TOA["obs"]).earth_location_itrf(), TOA, obsname=TOA["obs"]) pint_opv = PosVel(pint_opv.pos, pint_opv.vel) # print " obs T2:", t2_opv.pos.to(u.m).value, t2_opv.vel.to(u.m/u.s) # print " obs PINT:", pint_opv.pos.to(u.m), pint_opv.vel.to(u.m/u.s) dopv = pint_opv - t2_opv dpos = numpy.sqrt(numpy.dot(dopv.pos.to(u.m), dopv.pos.to(u.m))) dvel = numpy.sqrt( numpy.dot(dopv.vel.to(u.mm / u.s), dopv.vel.to(u.mm / u.s))) log.info(" obs diff: %.2f m, %.3f mm/s" % (dpos, dvel)) assert dpos < 2.0 and dvel < 0.02 pint_ssb2obs = PosVel( numpy.asarray(TOA["ssb_obs_pos"]) * u.km, numpy.asarray(TOA["ssb_obs_vel"]) * u.km / u.s, origin="SSB", obj="OBS", ) # print " topo T2:", t2_ssb2obs.pos.to(u.km), t2_ssb2obs.vel.to(u.km/u.s) # print " topo PINT:", pint_ssb2obs.pos.to(u.km), pint_ssb2obs.vel.to(u.km/u.s) dtopo = pint_ssb2obs - t2_ssb2obs dpos = numpy.sqrt(numpy.dot(dtopo.pos.to(u.m), dtopo.pos.to(u.m))) dvel = numpy.sqrt( numpy.dot(dtopo.vel.to(u.mm / u.s), dtopo.vel.to(u.mm / u.s))) log.info(" topo diff: %.2f m, %.3f m/s" % (dpos, dvel)) assert dpos < 2.0 and dvel < 0.02
def test_posvel_rejects_misshapen_quantity(): with pytest.raises((ValueError, TypeError)): PosVel(1 * u.m, 1 * u.m / u.s)