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
def posvel(self, t, ephem, maxextrap=2): """Return 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 ) log.error( "Orbit file goes {0} to {1}, Events go {2} to {3}".format( tmin, tmax, np.min(t.tt.mjd), np.max(t.tt.mjd) ) ) raise ValueError("Bad extrapolation of S/C file.") # Compute vector from SSB to Earth geo_posvel = objPosVel_wrt_SSB("earth", t, ephem) # 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 geo_posvel + nicer_posvel
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 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 test_posvel_rejects_misshapen_quantity(): with pytest.raises((ValueError, TypeError)): PosVel(1 * u.m, 1 * u.m / u.s)
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 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
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)