def generateEphemeris(name, tle1, tle2, time): """ Generate ephemeris for the given satellite at given epoch Parameters ---------- name : str Name of the satellite (line 0 of 3le) tle1 : str First line of the satellite's tle (line 1 of 3le) tle2 : str Second line of the satellite's tle (line 2 of 3le) date_str : str Desired epoch in format YYYY-mm-ddTHH:MM:SS Returns ------- ra : Raises ------ None """ observer = Topos(SITE_LATITUDE, SITE_LONGITUDE, elevation_m=SITE_ELEVATION) target = EarthSatellite(tle1, tle2, name) ra, dec, distance = (target - observer).at(time).radec() return [name, ra.hours, dec.degrees]
def __init__(self, line1, line2, name=None): """ Initiates the TLE Parameters ---------- line1, line2 : str First and second lines of the TLE name : str, optional Name of the object to which the TLE is attributed """ self.line1 = line1 self.line2 = line2 if name is not None: self.name = name[2:] else: self.name = 'UNKNOWN' # ephemeris info self.obs = TOPOS_LOCATION self.obj = EarthSatellite(line1, line2, name) self.ts = TS # book-keeping self.norad_id = int(self.line1[2:7]) self.yday = float(self.line1[20:32]) # orbital properties self.inclination = float(self.line2[8:16]) self.eccentricity = float(self.line2[26:33]) self.raan = float(self.line2[17:25]) self.argperigree = float(self.line2[34:42]) self.mean_anomaly = float(self.line2[43:51]) self.mean_motion = float(self.line2[52:63])
def generate_ephemeris(name, tle1, tle2, date_str): date = datetime.datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%S') date = date.replace(tzinfo=utc) lst = Time(date, scale='utc', location=SITE_LOCATION).sidereal_time('apparent') print('name', name) print('tle1', tle1) print('tle2', tle2) print('date', date) observer = Topos( latitude_degrees=SITE_LOCATION.lat.to(u.deg).value, longitude_degrees=SITE_LOCATION.lon.to(u.deg).value, elevation_m=SITE_LOCATION.height.to(u.m).value) time = load.timescale().utc(date) target = EarthSatellite(tle1, tle2, name) ra, dec, distance = (target - observer).at(time).radec() alt, az, distance = (target - observer).at(time).altaz() subpos = target.at(time).subpoint() lat = subpos.latitude.degrees lon = subpos.longitude.degrees if lon > 180: lon -= 360 field = SkyCoord(ra.hours, dec.degrees, unit=(u.hourangle, u.deg)) time2 = load.timescale().utc(date + datetime.timedelta(seconds=5)) ra2, dec2, distance2 = (target - observer).at(time2).radec() dra = (ra2._degrees - ra._degrees) * 3600 / 5 ddec = (dec2.degrees - dec.degrees) * 3600 / 5 return { 'name': name, 'date': date.strftime('%Y-%m-%dT%H:%M:%S'), 'ra': Angle(ra.to(u.deg)).to_string(unit=u.hourangle, sep=':'), 'ha': (lst - field.icrs.ra).wrap_at(12 * u.hourangle).to_string(sep=':', unit=u.hourangle, precision=2), 'dec': Angle(dec.to(u.deg)).to_string(unit=u.deg, sep=':'), 'dra': round(dra, 3), 'ddec': round(ddec, 3), 'alt': round(alt.degrees, 6), 'az': round(az.degrees, 6), 'latitude': round(lat, 3), 'longitude': round(lon, 3) }
def predict_doppler_from_tle(tle1, tle2, start_time, gs, base_freq): end_time = ts.tt_jd(start_time.tt + 1) #predict for 1 day in future tle_sat = EarthSatellite(tle1, tle2) times, events = tle_sat.find_events(gs, start_time, end_time, altitude_degrees=0.0) #Find events where the satellite sets below 10 degrees, and use this to split the times array up into individual passes sat_passes = [] start = -1 end = -1 for i, event in enumerate(events): if (event == 0): start = i if (event == 2) and (start > end): end = i sat_passes.append([times[start], times[end]]) for sat_pass in sat_passes: p_start = sat_pass[0] p_end = sat_pass[-1] #Split pass timeframe into 1000 time_points = ts.tt_jd(np.linspace(p_start.tt, p_end.tt, 1000)) sat_range_rate = tle_sat.at(time_points).velocity.km_per_s - gs.at( time_points).velocity.km_per_s sat_range = tle_sat.at(time_points).position.km - gs.at( time_points).position.km #Take the range rate's component in the direction of the range radial_velocity = np.array([np.dot([a[b] for a in sat_range], [a[b] for a in sat_range_rate]) \ / np.linalg.norm([a[b] for a in sat_range]) for b in range(1000)]) shifted_freqs = base_freq / (1 + radial_velocity / c) plt.plot(time_points.tt, shifted_freqs, markersize=1) plt.xlabel("Time (Julian Days)") plt.ylabel("Shifted frequency (Hz)") plt.show()
def test_appendix_c_satellite(): lines = appendix_c_example.splitlines() sat = EarthSatellite(lines, earth) jd_epoch = sat._sgp4_satellite.jdsatepoch three_days_later = jd_epoch + 3.0 offset = JulianDate(tt=three_days_later)._utc_float() - three_days_later jd = JulianDate(tt=three_days_later - offset) # First, a crucial sanity check (which is, technically, a test of # the `sgp4` package and not of Skyfield): are the right coordinates # being produced by our Python SGP4 propagator for this satellite? rTEME, vTEME, error = sat._position_and_velocity_TEME_km(jd) assert abs(-9060.47373569 - rTEME[0]) < 1e-8 assert abs(4658.70952502 - rTEME[1]) < 1e-8 assert abs(813.68673153 - rTEME[2]) < 1e-8 assert abs(-2.232832783 - vTEME[0]) < 1e-9 assert abs(-4.110453490 - vTEME[1]) < 1e-9 assert abs(-3.157345433 - vTEME[2]) < 1e-9
def test_appendix_c_satellite(): lines = appendix_c_example.splitlines() sat = EarthSatellite(lines, earth) jd_epoch = sat._sgp4_satellite.jdsatepoch three_days_later = jd_epoch + 3.0 jd = JulianDate(tt=three_days_later) jd.ut1 = array(three_days_later) # First, a crucial sanity check (which is, technically, a test of # the `sgp4` package and not of Skyfield): are the right coordinates # being produced by our Python SGP4 propagator for this satellite? rTEME, vTEME = sat._position_and_velocity_TEME_km(jd) assert abs(-9060.47373569 - rTEME[0]) < 1e-8 assert abs(4658.70952502 - rTEME[1]) < 1e-8 assert abs(813.68673153 - rTEME[2]) < 1e-8 assert abs(-2.232832783 - vTEME[0]) < 1e-9 assert abs(-4.110453490 - vTEME[1]) < 1e-9 assert abs(-3.157345433 - vTEME[2]) < 1e-9
def test_iss_altitude_computed_with_gcrs(iss_transit): dt, their_altitude = iss_transit cst = timedelta(hours=-6) #, minutes=1) dt = dt - cst t = api.load.timescale(delta_t=67.2091).utc(dt) lines = iss_tle.splitlines() s = EarthSatellite(lines, None) lake_zurich = api.Topos(latitude_degrees=42.2, longitude_degrees=-88.1) alt, az, d = lake_zurich.at(t).observe(s).altaz() print(dt, their_altitude, alt.degrees, their_altitude - alt.degrees) assert abs(alt.degrees - their_altitude) < 2.5 # TODO: tighten this up?
def test_appendix_c_satellite(): lines = appendix_c_example.splitlines() sat = EarthSatellite(lines, None) ts = api.load.timescale() jd_epoch = sat._sgp4_satellite.jdsatepoch three_days_later = jd_epoch + 3.0 offset = ts.tt(jd=three_days_later)._utc_float() - three_days_later t = ts.tt(jd=three_days_later - offset) # First, a crucial sanity check (which is, technically, a test of # the `sgp4` package and not of Skyfield): are the right coordinates # being produced by our Python SGP4 propagator for this satellite? rTEME, vTEME, error = sat._position_and_velocity_TEME_km(t) assert abs(-9060.47373569 - rTEME[0]) < 1e-8 assert abs(4658.70952502 - rTEME[1]) < 1e-8 assert abs(813.68673153 - rTEME[2]) < 1e-8 assert abs(-2.232832783 - vTEME[0]) < 1e-9 assert abs(-4.110453490 - vTEME[1]) < 1e-9 assert abs(-3.157345433 - vTEME[2]) < 1e-9
def test_iss_altitude_computed_with_gcrs(iss_transit): dt, their_altitude = iss_transit cst = timedelta(hours=-6) #, minutes=1) dt = dt - cst jd = JulianDate(utc=dt, delta_t=67.2091) lines = iss_tle.splitlines() s = EarthSatellite(lines, earth) lake_zurich = earth.topos(latitude_degrees=42.2, longitude_degrees=-88.1) alt, az, d = lake_zurich.gcrs(jd).observe(s).altaz() print(dt, their_altitude, alt.degrees, their_altitude - alt.degrees) assert abs(alt.degrees - their_altitude) < 2.5 # TODO: tighten this up?
def create_ground_track(out_path, sat, tle=None, start=None, delta_min=1, num_steps=1440, min_sun=None, sensor_angle=None, check_swath=False): """ Compute the ground track and sensor swatch for Globals and then write to GeoJSON """ # get globals tles from Celestrak if TLE not passed in if tle is None: tle = get_globals_tle(sat) timescale = load.timescale(builtin=True) times_dt = generate_time_array(start, delta_min, num_steps) times = timescale.utc(times_dt) sat_obj = EarthSatellite(line1=tle[0], line2=tle[1]) subsat = sat_obj.at(times).subpoint() lat = subsat.latitude.degrees lon = subsat.longitude.degrees track = np.concatenate([lon.reshape(-1, 1), lat.reshape(-1, 1)], axis=1) track_list = filter_sun_elevation(track, times_dt, min_sun) track_list = correct_rollover(track_list) properties = {"sat": sat, "start_time": str(times_dt[0]), "stop_time": str(times_dt[-1]), "time_step_minutes": delta_min} write_track_geojson(out_path, track_list, properties) if sensor_angle is not None: swath_list = get_sensor_swath(track_list, sensor_angle, GLOBALS_ALTITUDE_KM[sat]) swath_out_path = os.path.splitext(out_path)[0] + "_swath" + os.path.splitext(out_path)[1] write_track_geojson(swath_out_path, swath_list, properties) if check_swath: swath_check_out_path = os.path.splitext(out_path)[0] + "_swath_test" + os.path.splitext(out_path)[1] check_swath(swath_check_out_path, track_list, sensor_angle, 1000*GLOBALS_ALTITUDE_KM[sat])
def test_iss_altitude_computed_with_bcrs(iss_transit): dt, their_altitude = iss_transit cst = timedelta(hours=-6) #, minutes=1) dt = dt - cst jd = Timescale(delta_t=67.2091).utc(dt) lines = iss_tle.splitlines() s = EarthSatellite(lines, None) earth = api.load('de421.bsp')['earth'] lake_zurich = earth.topos(latitude_degrees=42.2, longitude_degrees=-88.1) # Compute using Solar System coordinates: alt, az, d = lake_zurich.at(jd).observe(s).altaz() print(dt, their_altitude, alt.degrees, their_altitude - alt.degrees) assert abs(alt.degrees - their_altitude) < 2.5 # TODO: tighten this up?
def __init__(self, line1, line2, name=None): self.line1 = line1 self.line2 = line2 if name is not None: self.name = name[2:] self.obs = TOPOS_LOCATION self.obj = EarthSatellite(line1, line2, name) self.ts = TS self.norad_id = int(self.line1[2:7]) self.yday = float(self.line1[20:32]) self.inclination = float(self.line2[8:16]) self.eccentricity = float(self.line2[26:33]) self.raan = float(self.line2[17:25]) self.argperigree = float(self.line2[34:42]) self.mean_anomaly = float(self.line2[43:51]) self.mean_motion = float(self.line2[52:63])
def __init__(self, line1, line2, name=None): """ Initiates the TLE Parameters ---------- line1, line2 : str First and second lines of the TLE name : str, optional Name of the object to which the TLE is attributed """ self.line1 = line1 self.line2 = line2 if name is not None: self.name = name[2:] else: self.name = 'UNKNOWN' # ephemeris info self.obs = TOPOS_LOCATION self.obj = EarthSatellite(line1, line2, name) self.ts = TS
from datetime import datetime from astropy.table import Table import numpy as np import time from st.site import Site EPHEM_PATH = '/Users/jblake95/GitHub/tlemcee/ephem_astra_1m_20180618_LaPalma.csv' TS = load.timescale() ephem = Table.read(EPHEM_PATH) line1 = '1 33436U 08057A 18170.03521762 .00000104 00000-0 00000+0 0 9996' line2 = '2 33436 0.0136 355.3865 0004990 118.0808 185.6030 1.00271245 35197' obj = EarthSatellite(line1, line2) site = Site('LaPalma') # test 1 - without arrays times = [] for t in ephem['UTC']: times.append(datetime.strptime(t, '%Y-%m-%dT%H:%M:%S.%f')) times = np.array(times) print('Computing for {} positions'.format(len(times))) from astropy.coordinates import SkyCoord from astropy import units as u t0 = time.time() ra, dec = np.zeros((2, len(times))) for t, dt in enumerate(times):
def test_epoch_date(): # Example from https://celestrak.com/columns/v04n03/ s = appendix_c_example.replace('00179.78495062', '98001.00000000') sat = EarthSatellite(s.splitlines(), None) assert sat.epoch.utc_jpl() == 'A.D. 1998-Jan-01 00:00:00.0000 UT'
def unpackElements(tle): """ Unpack TLE input Parameters ---------- tle : st.tle.TLE TLE object to unpack Returns ------- None """ tle.checksums = [] # line 1 elements if tle.line1 is not None: tle.norad_id = Norad_ID(tle.line1) tle.designator = Designator(tle.line1) tle.epoch = Epoch(tle.line1) tle.mmdot = Mmdot(tle.line1) tle.mmdot2 = Mmdot2(tle.line1) tle.drag = Drag(tle.line1) tle.setnumber = SetNumber(tle.line1) tle.checksums.append(CheckSum(tle.line1)) else: tle.norad_id = Norad_ID() tle.designator = Designator() tle.epoch = Epoch() tle.mmdot = Mmdot() tle.mmdot2 = Mmdot2() tle.drag = Drag() tle.setnumber = SetNumber() tle.line1 = EmptyTLE.line1.format(tle.norad_id.entry, tle.designator.entry, tle.epoch.entry, tle.mmdot.entry, tle.mmdot2.entry, tle.drag.entry, tle.setnumber.entry) # compute checksum tle.checksums.append(CheckSum(tle.line1)) tle.line1 = '{}{}'.format(tle.line1[:-1], tle.checksums[0].entry) # line 2 elements if tle.line2 is not None: tle.inclination = Inclination(tle.line2) tle.raan = RAAN(tle.line2) tle.eccentricity = Eccentricity(tle.line2) tle.argperigee = ArgPerigee(tle.line2) tle.meananomaly = MeanAnomaly(tle.line2) tle.mm = Mm(tle.line2) tle.revnumber = RevNumber(tle.line2) tle.checksums.append(CheckSum(tle.line2)) else: tle.inclination = Inclination() tle.raan = RAAN() tle.eccentricity = Eccentricity() tle.argperigee = ArgPerigee() tle.meananomaly = MeanAnomaly() tle.mm = Mm() tle.revnumber = RevNumber() tle.line2 = EmptyTLE.line2.format( tle.norad_id.entry, tle.inclination.entry, tle.raan.entry, tle.eccentricity.entry, tle.argperigee.entry, tle.meananomaly.entry, tle.mm.entry, tle.revnumber.entry) # compute checksum tle.checksums.append(CheckSum(tle.line2)) tle.line2 = '{}{}'.format(tle.line2[:-1], tle.checksums[1].entry) # name if tle.name is not None: # remove line number if from 3le if tle.name[0] == '0': tle.name = tle.name[2:] else: tle.name = 'UNKNOWN' tle._object = EarthSatellite(tle.line1, tle.line2, tle.name) return None
def modifyElements(tle, checksum, norad_id, designator, epoch, mmdot, mmdot2, drag, setnumber, inclination, raan, eccentricity, argperigee, meananomaly, mm, revnumber): """ Modify elements of a TLE object Parameters ---------- tle : st.tle.TLE TLE object in need of modification checksum : bool Toggle to recalculate checksum See descriptions above for suitable inputs for args (set to None for no modification) Returns ------- None """ if norad_id is not None: tle.norad_id = Norad_ID(norad_id) tle.line1 = '{}{}{}'.format(tle.line1[:2], tle.norad_id.entry, tle.line1[7:]) tle.line2 = '{}{}{}'.format(tle.line2[:2], tle.norad_id.entry, tle.line2[7:]) if designator is not None: tle.designator = Designator(designator) tle.line1 = '{}{}{}'.format(tle.line1[:9], tle.designator.entry, tle.line1[17:]) if epoch is not None: tle.epoch = Epoch(epoch) tle.line1 = '{}{}{}'.format(tle.line1[:18], tle.epoch.entry, tle.line1[32:]) if mmdot is not None: tle.mmdot = Mmdot(mmdot) tle.line1 = '{}{}{}'.format(tle.line1[:33], tle.mmdot.entry, tle.line1[43:]) if mmdot2 is not None: tle.mmdot2 = Mmdot2(mmdot2) tle.line1 = '{}{}{}'.format(tle.line1[:44], tle.mmdot2.entry, tle.line1[52:]) if drag is not None: tle.drag = Drag(drag) tle.line1 = '{}{}{}'.format(tle.line1[:53], tle.drag.entry, tle.line1[61:]) if setnumber is not None: tle.setnumber = SetNumber(setnumber) tle.line1 = '{}{}{}'.format(tle.line1[:64], tle.setnumber.entry, tle.line1[68:]) if inclination is not None: tle.inclination = Inclination(inclination) tle.line2 = '{}{}{}'.format(tle.line2[:8], tle.inclination.entry, tle.line2[16:]) if raan is not None: tle.raan = RAAN(raan) tle.line2 = '{}{}{}'.format(tle.line2[:17], tle.raan.entry, tle.line2[25:]) if eccentricity is not None: tle.eccentricity = Eccentricity(eccentricity) tle.line2 = '{}{}{}'.format(tle.line2[:26], tle.eccentricity.entry, tle.line2[33:]) if argperigee is not None: tle.argperigee = ArgPerigee(argperigee) tle.line2 = '{}{}{}'.format(tle.line2[:34], tle.argperigee.entry, tle.line2[42:]) if meananomaly is not None: tle.meananomaly = MeanAnomaly(meananomaly) tle.line2 = '{}{}{}'.format(tle.line2[:43], tle.meananomaly.entry, tle.line2[51:]) if mm is not None: tle.mm = Mm(mm) tle.line2 = '{}{}{}'.format(tle.line2[:52], tle.mm.entry, tle.line2[63:]) if revnumber is not None: tle.revnumber = RevNumber(revnumber) tle.line2 = '{}{}{}'.format(tle.line2[:63], tle.revnumber.entry, tle.line2[68:]) if checksum: calculateCheckSums(tle) tle._object = EarthSatellite(tle.line1, tle.line2, tle.name) return None