def main(): ts = load.timescale() t = ts.tt(2018, 1, 22, 9, 9, 20) trial_angles = 10, 20, 30, 40 # the error peaks around 20 degrees trial_elevations = 0, 6000, AU_M print(__doc__) for n in range(1, 5): print('=== {} iterations ==='.format(n)) print('') for elevation_m in trial_elevations: for degrees in trial_angles: top = Topos(latitude_degrees=degrees, longitude_degrees=123, elevation_m=elevation_m) xyz_au = top.at(t).position.au xyz_au = einsum('ij...,j...->i...', t.M, xyz_au) lat, lon, elev = reverse_terra(xyz_au, t.gast, n) lat = lat / DEG2RAD error_mas = 60.0 * 60.0 * 1000.0 * abs(degrees - lat) print('latitude {} degrees, elevation {} m' ' -> error of {:.2f} mas'.format(degrees, elevation_m, error_mas)) print('') print("""\ Given that iterations=3 pushes the maximum error from tens of mas ("mas" means " milli-arcsecond") down to hundredths of a mas, it is the value we have chosen as a default. A fourth iteration, if we ever chose to perform one, pushes the error down to "0.00 mas". """)
def main(): ts = load.timescale() t = ts.tt(2018, 1, 22, 9, 9, 20) trial_angles = 10, 20, 30, 40 # the error peaks around 20 degrees trial_elevations = 0, 6000, AU_M print(__doc__) for n in range(1, 5): print('=== {} iterations ==='.format(n)) print('') for elevation_m in trial_elevations: for degrees in trial_angles: top = Topos(latitude_degrees=degrees, longitude_degrees=123, elevation_m=elevation_m) xyz_au = top.at(t).position.au xyz_au = einsum('ij...,j...->i...', t.M, xyz_au) lat, lon, elev = reverse_terra(xyz_au, t.gast, n) lat = lat / DEG2RAD error_mas = 60.0 * 60.0 * 1000.0 * abs(degrees - lat) print('latitude {} degrees, elevation {} m' ' -> error of {:.2f} mas' .format(degrees, elevation_m, error_mas)) print('') print("""\ Given that iterations=3 pushes the maximum error from tens of mas ("mas" means " milli-arcsecond") down to hundredths of a mas, it is the value we have chosen as a default. A fourth iteration, if we ever chose to perform one, pushes the error down to "0.00 mas". """)
def test_itrf_vector(): ts = load.timescale(builtin=True) t = ts.utc(2019, 11, 2, 3, 53) top = Topos(latitude_degrees=45, longitude_degrees=0, elevation_m=constants.AU_M - constants.ERAD) x, y, z = top.at(t).itrf_xyz().au assert abs(x - 0.7071) < 1e-4 assert abs(y - 0.0) < 1e-14 assert abs(z - 0.7071) < 1e-4
def test_negation(): ts = load.timescale() t = ts.utc(2020, 8, 30, 16, 5) usno = Topos('38.9215 N', '77.0669 W', elevation_m=92.0) neg = -usno p1 = usno.at(t) p2 = neg.at(t) assert (p1.position.au == -p2.position.au).all() assert (p1.velocity.au_per_d == -p2.velocity.au_per_d).all() # A second negation should return the unwrapped original. neg = -neg assert neg is usno
def test_from_altaz_parameters(ts): usno = Topos('38.9215 N', '77.0669 W', elevation_m=92.0) t = ts.tt(jd=api.T0) p = usno.at(t) a = api.Angle(degrees=10.0) d = api.Distance(au=0.234) with assert_raises(ValueError, 'the alt= parameter with an Angle'): p.from_altaz(alt='Bad value', alt_degrees=0, az_degrees=0) with assert_raises(ValueError, 'the az= parameter with an Angle'): p.from_altaz(az='Bad value', alt_degrees=0, az_degrees=0) p.from_altaz(alt=a, alt_degrees='bad', az_degrees=0) p.from_altaz(az=a, alt_degrees=0, az_degrees='bad') assert str(p.from_altaz(alt=a, az=a).distance()) == '0.1 au' assert str(p.from_altaz(alt=a, az=a, distance=d).distance()) == '0.234 au'
def angle_2d(lat, lon, y, m, d, h): # given surface lat lon and time, calculate the its angles involving satellite and Sun # information about satellites (chose GOES 16) sats = api.load.tle('https://celestrak.com/NORAD/elements/goes.txt') satellite = sats['GOES 16 [+]'] # planets planets = load('de421.bsp') sun = planets['sun'] earth = planets['earth'] # create array to hold angles angles = np.zeros((len(lat), len(lon), 5)) for i in range(len(lat)): for j in range(len(lon)): #time ts = api.load.timescale() tm = ts.tt(y, m, d, h, 0, 0) # call the surface station "boston" boston = Topos(str(lat[i]) + ' N', str(lon[j]) + ' W', elevation_m=0.0) # the angle between the two vectors: earth center to satellite and earth center to observer theta = satellite.at(tm).separation_from(boston.at(tm)) # geometry difference = satellite - boston geometry = difference.at(tm).altaz() # angles involving satellite scan = np.round(180 - (90 + geometry[0].degrees + theta.degrees), 2) zenith = np.round(geometry[0].degrees, 2) azimuth = np.round(geometry[1].degrees, 2) angles[i, j, 0] = zenith angles[i, j, 1] = azimuth angles[i, j, 2] = scan # angles involving the Sun observer = earth + boston geo2 = observer.at(tm).observe(sun).apparent().altaz() zenithsun = np.round(geo2[0].degrees, 2) azimuthsun = np.round(geo2[1].degrees, 2) angles[i, j, 3] = zenithsun angles[i, j, 4] = azimuthsun return angles
def test_frame_rotation(): # Does a frame's rotation and twist get applied in the right # directions? Let's test whether the position and velocity of an # ITRS vector (ERAD,0,0) are restored to the proper orientation. top = Topos(latitude_degrees=0, longitude_degrees=0) ts = load.timescale() t = ts.utc(2020, 11, 27, 15, 34) # Arbitrary time; LST ~= 20.03. p = top.at(t) r = p.frame_xyz(itrs) assert max(abs(r.m - [ERAD, 0, 0])) < 4e-8 # meters r, v = p.frame_xyz_and_velocity(itrs) assert max(abs(r.m - [ERAD, 0, 0])) < 4e-8 # meters assert max(abs(v.km_per_s)) < 3e-15 # km/s
def test_itrf_vector(): top = Topos(latitude_degrees=45, longitude_degrees=0, elevation_m=constants.AU_M - constants.ERAD) x, y, z = top.itrs_position.au assert abs(x - sqrt(0.5)) < 2e-7 assert abs(y - 0.0) < 1e-14 assert abs(z - sqrt(0.5)) < 2e-7 ts = load.timescale() t = ts.utc(2019, 11, 2, 3, 53) x, y, z = top.at(t).itrf_xyz().au assert abs(x - sqrt(0.5)) < 1e-4 assert abs(y - 0.0) < 1e-14 assert abs(z - sqrt(0.5)) < 1e-4
def test_velocity(): # It looks like this is a sweet spot for accuracy: presumably a # short enough fraction of a second that the vector does not time to # change direction much, but long enough that the direction does not # get lost down in the noise. factor = 300.0 ts = load.timescale() t = ts.utc(2019, 11, 2, 3, 53, [0, 1.0 / factor]) jacob = Topos(latitude_degrees=36.7138, longitude_degrees=-112.2169) p = jacob.at(t) velocity1 = p.position.km[:,1] - p.position.km[:,0] velocity2 = p.velocity.km_per_s[:,0] print(length_of(velocity2 - factor * velocity1)) assert length_of(velocity2 - factor * velocity1) < 0.0007
def test_beneath(ts, angle): t = ts.utc(2018, 1, 19, 14, 37, 55) # An elevation of 0 is more difficult for the routine's accuracy # than a very large elevation. top = Topos(latitude_degrees=angle, longitude_degrees=angle, elevation_m=0) p = top.at(t) b = p.subpoint() error_degrees = abs(b.latitude.degrees - angle) error_mas = 60.0 * 60.0 * 1000.0 * error_degrees assert error_mas < 0.1 error_degrees = abs(b.longitude.degrees - angle) error_mas = 60.0 * 60.0 * 1000.0 * error_degrees assert error_mas < 0.1
def test_polar_motion_when_computing_topos_position(ts): xp_arcseconds = 11.0 yp_arcseconds = 22.0 ts.polar_motion_table = [0.0], [xp_arcseconds], [yp_arcseconds] top = Topos(latitude=(42, 21, 24.1), longitude=(-71, 3, 24.8), elevation_m=43.0) t = ts.utc(2020, 11, 12, 22, 2) # "expected" comes from: # from novas.compat import ter2cel # print(ter2cel(t.whole, t.ut1_fraction, t.delta_t, xp_arcseconds, # yp_arcseconds, top.itrs_position.km, method=1)) expected = (3146.221313017412, -3525.955228249315, 4269.301880718039) assert max(abs(top.at(t).position.km - expected)) < 6e-11
def _calculateGroundTrack(earth, satellite, timeset): topoZero = Topos(latitude_degrees=0.0, longitude_degrees=0.0) satAbs = earth + satellite earthPosition = earth.at(timeset).position.km zeroPosition = topoZero.at(timeset).position.km satPosition = satAbs.at(timeset).position.km - earthPosition earthRotation = np.arctan2(zeroPosition[1], zeroPosition[2]) sinRot = np.sin(-earthRotation) cosRot = np.cos(-earthRotation) xAdj = satPosition[0] * cosRot - satPosition[1] * sinRot yAdj = satPosition[0] * sinRot + satPosition[1] * cosRot rxy = np.sqrt(xAdj**2 + yAdj**2) satLat = np.arctan2(satPosition[2], rxy) satLong = np.arctan2(yAdj, xAdj) plt.plot(np.rad2deg(satLong), np.rad2deg(satLat), 'o')
loc = Topos(38.8892771, -77.0353628) satellite = EarthSatellite(line1, line2) # Geocentric geometry = satellite.at(t) # Geographic point beneath satellite subpoint = geometry.subpoint() latitude = subpoint.latitude longitude = subpoint.longitude elevation = subpoint.elevation # Topocentric difference = satellite - loc geometry = difference.at(t) topoc= loc.at(t) # topocentric = difference.at(t) geocentric = satellite.at(t) # ------ Start outputs ----------- print ('\n Ephemeris time:', timestring) print (' JD time: ',t) print ('',loc) print ('\n Subpoint Longitude= ', longitude ) print (' Subpoint Latitude = ', latitude ) print (' Subpoint Elevation= {0:.3f}'.format(elevation.km),'km') # ------ Step 1: compute sat horizontal coords ------ alt, az, distance = topocentric.altaz() if alt.degrees > 0: print('\n',satellite, '\n is above the horizon') print ('\n Altitude= ', alt.degrees )
def read_obs(iod_lines): """ decodes the iod_line data """ global ssn global epoch_datetime # FIXME get rid of these globals Sites = CosparSite("data/stations.in") csi = .0055878713278878 zet = .0055888307019922 the = .0048580335354883 nobs = len( iod_lines) # Number of iod-compliant formatted lines in the input file ll = np.zeros((nobs, 3)) odata = np.zeros((nobs, 4)) rd = np.zeros((nobs, 3)) i = 0 for iod_line in iod_lines: # Grab the most recent version of these variables for writing eventual TLE # FIXME Scott's original code grabs the 2nd or "middle" datetime for epoch epoch_datetime = iod_line.DateTime ssn = iod_line.ObjectNumber ts = load.timescale() (year, month, day, hour, minute, second) = iod_line.DateTime.timetuple()[:6] t_skyfield = ts.utc(year, month, day, hour, minute, second) t1_jd = t_skyfield.tt ra = radians(iod_line.RA) dc = radians(iod_line.DEC) if (iod_line.Epoch == 4): # precess from B1950 to J2000 a = cos(dc) * sin(ra + csi) b = cos(the) * cos(dc) * cos(ra + csi) \ - sin(the) * sin(dc) c = sin(the) * cos(dc) * cos(ra + csi) \ + cos(the) * sin(dc) ra = atan(a / b) # ra - zet if (b < 0): ra += pi ra += zet # right ascension, radians ra += 1 / 30000 dc = asin(c) if (abs(dc) > 1.4): dc = c / abs(c) * acos(sqrt(a * a + b * b)) # precession from J2000 t = (t1_jd - 2451545) / 36525 csi = (2306.2181 + .30188 * t + .017998 * t * t) * t * de2ra / 3600 zet = (2306.2181 + 1.09468 * t + .018203 * t * t) * t * de2ra / 3600 the = (2004.3109 - .42665 * t - .041833 * t * t) * t * de2ra / 3600 a = cos(dc) * sin(ra + csi) b = cos(the) * cos(dc) * cos(ra + csi) \ - sin(the) * sin(dc) c = sin(the) * cos(dc) * cos(ra + csi) \ + cos(the) * sin(dc) ra = atan(a / b) # ra - zet if (b < 0): ra += pi ra += zet # right ascension, radians dc = asin(c) if (abs(dc) > 1.4): dc = c / abs(c) * acos(sqrt(a * a + b * b)) # line-of-sight vectors ll[i][0] = cos(dc) * cos(ra) ll[i][1] = cos(dc) * sin(ra) ll[i][2] = sin(dc) odata[i][0] = t1_jd # julian date odata[i][1] = ra # ra radians (observed) odata[i][2] = dc # dc radians (observed) odata[i][3] = iod_line.Station # station (la, lo, hh) = Sites.topos(iod_line.Station) observer_location = Topos(latitude_degrees=la, longitude_degrees=lo, elevation_m=hh) topocentric = observer_location.at(t_skyfield) rd[i] = topocentric.position.km / _XKMPER # elfind works in units of Earth radii i += 1 # end for return odata, ll, rd