def interpolate(self, step_size: float, num_points: int = 5) -> OrbitEphemerisMessage: """Interpolates the ephemeris data to the desired time step size. :param step_size: The interpolated step size in seconds. :param num_points: The number of states to use for interpolation. """ epochs, state_vects = [], [] for emph_line in self.ephemeris_lines: epochs.append(emph_line.epoch) # Convert components from m and m\s to km and km\s state_vect = [i * 1000.0 for i in emph_line.state_vector] state_vects.append(state_vect) offset_epochs = conv.get_J2000_epoch_offset(epochs) interpolation = interpolate_ephemeris(self.ref_frame, offset_epochs, state_vects, num_points, self.ref_frame, offset_epochs[0], offset_epochs[-1], step_size) interp_ephem_lines = [] for proto_buf in interpolation: epoch = conv.get_UTC_string(proto_buf.time) state_vector = [i / 1000.0 for i in list(proto_buf.true_state)] interp_ephem_line = EphemerisLine(epoch=epoch, state_vector=state_vector) interp_ephem_lines.append(interp_ephem_line) interp_oem_data = self.copy() interp_oem_data.ephemeris_lines = interp_ephem_lines return interp_oem_data
def predict(self): self.predict["state"] = "disabled" start = get_J2000_epoch_offset(self.start_time.get()) end = get_J2000_epoch_offset(self.end_time.get()) data, tle = {}, [ l for l in self.tle.get("0.0", "end-1c").splitlines() if l.startswith("1") or l.startswith("2") ] for f in [ "latitude", "longitude", "altitude", "fov_azimuth", "fov_elevation", "fov_aperture", "step_size" ]: data[f] = [ float(t.strip()) for t in getattr(self, f).get().split(",") ] if (f not in ["altitude", "step_size"]): data[f] = [d * Constant.DEGREE_TO_RAD for d in data[f]] cfg_list = [] sim_meas = len(data["latitude"]) <= 2 or len(data["latitude"]) != len( data["longitude"]) for i in range(0, len(tle), 2): cfg_list.append( configure(prop_start=start, prop_initial_TLE=tle[i:i + 2], prop_end=end, prop_step=data["step_size"][0], sim_measurements=sim_meas, gravity_degree=-1, gravity_order=-1, ocean_tides_degree=-1, ocean_tides_order=-1, third_body_sun=False, third_body_moon=False, solid_tides_sun=False, solid_tides_moon=False, drag_model=DragModel.UNDEFINED, rp_sun=False)) cfg_list[-1].output_flags |= OutputFlag.OUTPUT_ECLIPSE if (sim_meas): add_station(cfg_list[-1], "Sensor", data["latitude"][0], data["longitude"][0], data["altitude"][0], data["fov_azimuth"][0], data["fov_elevation"][0], data["fov_aperture"][0]) cfg_list[-1].measurements[MeasurementType.AZIMUTH].error[:] = [ 0.0 ] cfg_list[-1].measurements[ MeasurementType.ELEVATION].error[:] = [0.0] else: cfg_list[-1].geo_zone_lat_lon[:] = [ l for ll in zip(data["latitude"], data["longitude"]) for l in ll ] if (len(cfg_list)): i = 0 lookup = {0.0: "Sunlit", 0.5: "Penumbra", 1.0: "Umbra"} self.output.delete("0.0", tk.END) if (sim_meas): self.output_label[ "text"] = "UTC, Azimuth [deg], Elevation [deg]" else: self.output_label[ "text"] = "UTC, Latitude [deg], Longitude [deg], Altitude [m]" for o in propagate_orbits(cfg_list): self.output.insert(tk.END, f"\nObject {tle[i][2:7]}:\n") i += 2 for m in o.array: if (sim_meas): self.output.insert( tk.END, "{}: {:.5f}, {:.5f} ({})\n".format( get_UTC_string(m.time), (m.values[0] / Constant.DEGREE_TO_RAD + 360) % 360, m.values[1] / Constant.DEGREE_TO_RAD, lookup[m.true_state[-1]])) else: lla = pos_to_lla(Frame.GCRF, m.time, m.true_state) self.output.insert( tk.END, "{}: {:.5f}, {:.5f}, {:.2f} ({})\n".format( get_UTC_string(m.time), lla[0] / Constant.DEGREE_TO_RAD, lla[1] / Constant.DEGREE_TO_RAD, lla[2], lookup[m.true_state[-1]])) self.predict["state"] = "normal"
] for i in range(0, len(elements), 2): config.append( configure(prop_start=start, prop_initial_TLE=elements[i:i + 2], prop_end=end, prop_step=args.step_size, gravity_degree=-1, gravity_order=-1, ocean_tides_degree=-1, ocean_tides_order=-1, third_body_sun=False, third_body_moon=False, solid_tides_sun=False, solid_tides_moon=False, drag_model=DragModel.UNDEFINED, rp_sun=False)) try: for idx, obj in enumerate(propagate_orbits(config)): obj_id = elements[idx * 2][2:7] print(f"\nObject {obj_id}:") for m in obj.array: print(get_UTC_string(m.time), m.true_state) if (args.export_oem): with open(obj_id + ".oem", "w") as fp: fp.write(export_OEM(config[idx], obj.array, obj_id, obj_id)) except Exception as exc: print(exc)
config.measurements[MeasurementType.RIGHT_ASCENSION].error[:] = [ 2.0 * Constant.ARC_SECOND_TO_RAD ] config.measurements[MeasurementType.DECLINATION].error[:] = [ 2.0 * Constant.ARC_SECOND_TO_RAD ] # Use Filter.EXTENDED_KALMAN for the EKF config.estm_filter = Filter.UNSCENTED_KALMAN # Build a list of Measurement objects, one for each RA/dec pair meas_obj = [] for o in real_obs: meas_obj.append( build_measurement(get_J2000_epoch_offset(o[0]), "UTA-ASTRIA-NMSkies", o[1:])) # Run OD. Fitting single object and hence the [0] fit = determine_orbit([config], [meas_obj])[0] # Check for estimation errors if (isinstance(fit, str)): print(fit) exit(1) for f in fit: # print(f) to dump pre-fits/post-fits/covariances print(get_UTC_string(f.time), f.station, f.estimated_state[:6]) # Plot OD results plot(config, meas_obj, fit, interactive=True, estim_param=False)
def export_TDM(cfg: Settings, obs, obj_id: str, station_list: List[str]=None)->str: """Export tracking data in CCSDS TDM format. Parameters ---------- cfg : Settings object. obs : Measurements to export. obj_id : Object identifier. station_list : List of ground stations to include; None to include all. Returns ------- Tracking data in TDM format. """ miter = cfg.measurements.keys() utc_list = get_UTC_string([o.time for o in obs]) if (MeasurementType.RIGHT_ASCENSION in miter and MeasurementType.DECLINATION in miter): obstype = f"ANGLE_TYPE = RADEC\nREFERENCE_FRAME = {cfg.prop_inertial_frame}" obspath = "1,2" if (MeasurementType.AZIMUTH in miter and MeasurementType.ELEVATION in miter): obstype = "ANGLE_TYPE = AZEL" obspath = "1,2" if (MeasurementType.RANGE in miter or MeasurementType.RANGE_RATE in miter): obspath = "2,1,2" if (MeasurementType.AZIMUTH in miter and MeasurementType.ELEVATION in miter): obstype = "RANGE_UNITS = km\nANGLE_TYPE = AZEL" else: obstype = "RANGE_UNITS = km" blocks = [] tdm_header = f"""CCSDS_TDM_VERS = 1.0 CREATION_DATE = {datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S")} ORIGINATOR = UT-Austin """ for sname, sinfo in cfg.stations.items(): if (station_list is not None and sname not in station_list): continue lat, lon, alt = sinfo.latitude/Constant.DEGREE_TO_RAD, sinfo.longitude/Constant.DEGREE_TO_RAD, sinfo.altitude/1000.0 blocks.append(f"""META_START TIME_SYSTEM = UTC PARTICIPANT_1 = {obj_id} PARTICIPANT_2 = {sname} (WGS-84 Latitude: {lat} deg, Longitude: {lon} deg, Altitude: {alt} km) MODE = SEQUENTIAL PATH = {obspath} {obstype} META_STOP """) blocks.append("DATA_START") for utc, o in zip(utc_list, obs): utc = utc[:-1] if (o.station != sname): continue if (MeasurementType.RANGE in miter or MeasurementType.RANGE_RATE in miter): if (MeasurementType.RANGE in miter): blocks.append(f"RANGE = {utc} {o.values[0]/1000.0}") if (MeasurementType.RANGE_RATE in miter): blocks.append(f"DOPPLER_INSTANTANEOUS = {utc} {o.values[-1]/1000.0}") if ((MeasurementType.AZIMUTH in miter and MeasurementType.ELEVATION in miter) or (MeasurementType.RIGHT_ASCENSION in miter and MeasurementType.DECLINATION in miter)): blocks.append((f"""ANGLE_1 = {utc} {o.values[0]/Constant.DEGREE_TO_RAD}\n""" f"""ANGLE_2 = {utc} {o.values[1]/Constant.DEGREE_TO_RAD}""")) blocks.append(f"DATA_STOP\n") return(tdm_header + "\n".join(blocks))
def export_OEM(cfg: Settings, obs, obj_id: str, obj_name: str, time_list: List[str]=None, add_prop_cov: bool=False)->str: """Export ephemerides in CCSDS OEM format. Parameters ---------- cfg : Settings object. obs : Measurements or estimation results to export. obj_id : Object identifier. obj_name : Object name. time_list : Limit output to UTC times specified; default is to output everything. add_prop_cov : Include propagated covariances if True; default is False. Returns ------- Ephemerides in OEM format. """ utc_list = get_UTC_string([o.time for o in obs]) oem_header = f"""CCSDS_OEM_VERS = 2.0 CREATION_DATE = {datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S")} ORIGINATOR = UT-Austin META_START OBJECT_NAME = {obj_name} OBJECT_ID = {obj_id} CENTER_NAME = EARTH REF_FRAME = {cfg.prop_inertial_frame} TIME_SYSTEM = UTC START_TIME = {time_list[0] if (time_list) else utc_list[0][:-1]} STOP_TIME = {time_list[-1] if (time_list) else utc_list[-1][:-1]} META_STOP """ eph_data, estm_cov, prop_cov, added = [], [], [], set() is_estm = (hasattr(obs[0], "estimated_state") and hasattr(obs[0], "estimated_covariance") and hasattr(obs[0], "propagated_covariance")) eph_key = "estimated_state" if (is_estm) else "true_state" for utc, o in zip(utc_list, obs): utc = utc[:-1] if (o.time in added): continue added.add(o.time) if (time_list is not None and utc not in time_list): continue X = [x/1000.0 for x in getattr(o, eph_key)[:6]] eph_data.append(f"{utc} {X[0]} {X[1]} {X[2]} {X[3]} {X[4]} {X[5]}") if (is_estm and len(o.estimated_covariance) >= 21): estm_cov.append(f"\nEPOCH = {utc}") for m in range(6): n = (m**2 + m)//2 estm_cov.append(" ".join([str(x/1E6) for x in o.estimated_covariance[n:m+n+1]])) if (is_estm and add_prop_cov and len(o.propagated_covariance) >= 21): prop_cov.append(f"\nEPOCH = {utc}") for m in range(6): n = (m**2 + m)//2 prop_cov.append(" ".join([str(x/1E6) for x in o.propagated_covariance[n:m+n+1]])) oem_data = oem_header + "\n".join(eph_data) if (len(estm_cov) > 0): oem_data += "\n\nCOMMENT Updated covariance\nCOVARIANCE_START" + "\n".join(estm_cov) + "\nCOVARIANCE_STOP" if (len(prop_cov) > 0): oem_data += "\n\nCOMMENT Propagated covariance\nCOVARIANCE_START" + "\n".join(prop_cov) + "\nCOVARIANCE_STOP" return(oem_data)
def predict(self): self.predict["state"] = "disabled" start = get_J2000_epoch_offset(self.start_time.get()) end = get_J2000_epoch_offset(self.end_time.get()) data, tle = {}, [ l for l in self.tle.get("0.0", "end-1c").splitlines() if l.startswith("1") or l.startswith("2") ] for f in [ "latitude", "longitude", "altitude", "fov_azimuth", "fov_elevation", "fov_aperture", "step_size" ]: data[f] = [ float(t.strip()) for t in getattr(self, f).get().split(",") ] if (f not in ["altitude", "step_size"]): data[f] = [d * Constant.DEGREE_TO_RAD for d in data[f]] cfg_list = [] sim_meas = len(data["latitude"]) <= 2 or len(data["latitude"]) != len( data["longitude"]) for i in range(0, len(tle), 2): cfg_list.append( configure(prop_start=start, prop_initial_TLE=tle[i:i + 2], prop_end=end, prop_step=data["step_size"][0], sim_measurements=sim_meas)) if (not sim_meas): cfg_list[-1].geo_zone_lat_lon[:] = [ l for ll in zip(data["latitude"], data["longitude"]) for l in ll ] continue add_station(cfg_list[-1], "Sensor", data["latitude"][0], data["longitude"][0], data["altitude"][0], data["fov_azimuth"][0], data["fov_elevation"][0], data["fov_aperture"][0]) cfg_list[-1].measurements[MeasurementType.AZIMUTH].error[:] = [0.0] cfg_list[-1].measurements[MeasurementType.ELEVATION].error[:] = [ 0.0 ] if (len(cfg_list)): i = 0 self.output.delete("0.0", tk.END) if (sim_meas): self.output_label[ "text"] = "UTC, Azimuth [deg], Elevation [deg]" else: self.output_label[ "text"] = "UTC, Latitude [deg], Longitude [deg], Altitude [m]" for o in propagate_orbits(cfg_list): self.output.insert(tk.END, "\nObject {}:\n".format(tle[i][2:7])) i += 2 for m in o.array: if (sim_meas): self.output.insert( tk.END, "{}: {:.5f}, {:.5f}\n".format( get_UTC_string(m.time), (m.values[0] / Constant.DEGREE_TO_RAD + 360) % 360, m.values[1] / Constant.DEGREE_TO_RAD)) else: lla = pos_to_lla(Frame.GCRF, m.time, m.true_state) self.output.insert( tk.END, "{}: {:.5f}, {:.5f}, {:.2f}\n".format( get_UTC_string(m.time), lla[0] / Constant.DEGREE_TO_RAD, lla[1] / Constant.DEGREE_TO_RAD, lla[2])) self.predict["state"] = "normal"
# but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from orbdetpy import configure, Frame from orbdetpy.conversion import get_J2000_epoch_offset, get_UTC_string from orbdetpy.propagation import propagate_orbits from orbdetpy.utilities import interpolate_ephemeris # Propagate for 1 hour at 5 minute intervals with default settings cfg = configure(prop_start=get_J2000_epoch_offset("2020-03-09T22:00:02.000Z"), prop_initial_state=[ -152408.166, -958234.785, 6908448.381, -7545.691, 285.553, -126.766 ], prop_end=get_J2000_epoch_offset("2020-03-09T23:00:02.000Z"), prop_step=300.0) times, states = [], [] for o in propagate_orbits([cfg])[0].array: times.append(o.time) states.append(o.true_state) # Interpolate over the same period at 1 minute intervals for i in interpolate_ephemeris(Frame.GCRF, times, states, 5, Frame.GCRF, cfg.prop_start, cfg.prop_end, 60.0): print(get_UTC_string(i.time), i.true_state)