def test_dot_constant_for_linear_values(name, ipset_linear): """Test that the derivative is constant for linear values""" _, y_dot = interpolation.interpolate_with_derivative(*ipset_linear, kind=name, **IPARGS.get( name, {})) assert np.allclose(y_dot, y_dot.mean())
def calculate_initial_values(eph, rundate): """Computing initial values for position and velocity in GCRS system This is for later use in orbit integration, from tables in the prediction files. Use a lagrange polynomial in order to interpolate in the tables. Args: eph: Dict containing ephemeris information Returns: eph: Dict where the initial position and velocity is added """ pos_gcrs = np.empty((3, 0)) times = np.empty((0)) table_of_positions = sorted(eph["positions"].items()) mjd1, mjd2 = zip(*[t for t, d in table_of_positions]) for pos_time, (_, data) in zip(time.Time(val=mjd1, val2=mjd2, fmt="mjd", scale="utc"), table_of_positions): diffsec = (pos_time.utc.datetime - rundate).total_seconds() # Only look at points close to rundate (start of integration) # if abs(diffsec) > 4000: # continue # Table given in ITRF coordinate system. Convert to GCRS, where the integration of the satellite orbit will # be done pos_gcrs = np.hstack((pos_gcrs, np.transpose([rotation.trs2gcrs(pos_time) @ data["pos"]]))) times = np.hstack((times, diffsec)) log.info("Interpolating data from prediction file in order to get initial pos/vel") pos_gcrs_ip, vel_gcrs_ip = interpolation.interpolate_with_derivative( times, np.transpose(pos_gcrs), np.array([0.0]), kind="lagrange", window=10, bounds_error=False ) eph["initial_pos"] = pos_gcrs_ip[0] eph["initial_vel"] = vel_gcrs_ip[0] return eph
def calculate_initial_values(eph, rundate): """Computing initial values for position and velocity in GCRS system This is for later use in orbit integration, from tables in the prediction files. Use a lagrange polynomial in order to interpolate in the tables. Args: eph: Dict containing ephemeris information Returns: eph: Dict where the initial position and velocity is added """ data = sorted(eph["positions"].items()) pos_itrs = np.zeros((len(data), 3)) mjd1, mjd2 = zip(*[t for t, d in data]) rotation_mat = rotation.trs2gcrs( time.Time(val=mjd1, val2=mjd2, fmt="mjd", scale="utc")) tbl = time.Time(val=mjd1, val2=mjd2, fmt="mjd", scale="utc") for i in range(0, len(data)): pos_itrs[i] = data[i][1]["pos"] diffsec = np.array([(t - rundate).total_seconds() for t in tbl.utc.datetime]) # Table given in ITRF coordinate system. Convert to GCRS, where the integration of the satellite orbit will # be done pos_gcrs = np.sum(rotation_mat @ pos_itrs[:, :, None], axis=2) log.info( "Interpolating data from prediction file in order to get initial pos/vel" ) pos_gcrs_ip, vel_gcrs_ip = interpolation.interpolate_with_derivative( diffsec, pos_gcrs, np.array([0.0]), kind="lagrange", window=10, bounds_error=False) eph["initial_pos"] = pos_gcrs_ip[0] eph["initial_vel"] = vel_gcrs_ip[0] return eph
def calculate(stage, dset): """ Integrate differential equation of motion of the satellite Args: stage: Name of current stage dset: Dataset containing the data """ iterations = config.tech.iterations.int # Run models adjusting station positions site.calculate_site("site", dset) delta_pos = site.add("site", dset) dset.site_pos[:] = (dset.site_pos.gcrs + delta_pos[0].gcrs).trs dset.add_float("obs", val=dset.time_of_flight * constant.c / 2, unit="meter") dset.add_float("calc", np.zeros(dset.num_obs), unit="meter") dset.add_float("residual", np.zeros(dset.num_obs), unit="meter") dset.add_float("up_leg", np.zeros(dset.num_obs), unit="second") dset.add_posvel("sat_pos", np.zeros((dset.num_obs, 6)), system="gcrs", time=dset.time) arc_length = config.tech.arc_length.float dset.site_pos.other = dset.sat_pos # First guess for up_leg: dset.up_leg[:] = dset.time_of_flight / 2 for iter_num in itertools.count(start=1): log.blank() log.info(f"Calculating model corrections for iteration {iter_num}") sat_time_list = dset.obs_time + dset.time_bias + dset.up_leg apriori_orbit_provider = config.tech.apriori_orbit.str sat_name = dset.vars["sat_name"] rundate = dset.analysis["rundate"] if apriori_orbit_provider: version = config.tech.apriori_orbit_version.str log.info( f"Using external orbits from {apriori_orbit_provider}, version {version}" ) apriori_orbit = apriori.get( "orbit", rundate=rundate + timedelta(days=arc_length), time=None, day_offset=6, satellite=sat_name, apriori_orbit="slr", file_key="slr_external_orbits", ) dset_external = apriori_orbit._read(dset, apriori_orbit_provider, version) sat_pos = dset_external.sat_pos.gcrs_pos t_sec = TimeDelta( dset_external.time - Time(datetime(rundate.year, rundate.month, rundate.day), scale="utc", fmt="datetime"), fmt="seconds", ) t_sec = t_sec.value else: sat_pos, sat_vel, t_sec = orbit.calculate_orbit( datetime(rundate.year, rundate.month, rundate.day), sat_name, sat_time_list, return_full_table=True) sat_pos_ip, sat_vel_ip = interpolation.interpolate_with_derivative( np.array(t_sec), sat_pos, sat_time_list, kind="interpolated_univariate_spline") dset.sat_pos.gcrs[:] = np.concatenate((sat_pos_ip, sat_vel_ip), axis=1) delay.calculate_delay("kinematic_models", dset) # We observe the time when an observation is done, and the time of flight of the laser pulse. We estimate # the up-leg time with Newton's method applied to the equation (8.84) of :cite:'beutler2005' Gerhard Beutler: # Methods of Celestial Mechanics, Vol I., 2005. for j in range(0, 4): reflect_time = dset.time + TimeDelta( dset.time_bias + dset.up_leg, fmt="seconds", scale="utc") site_pos_reflect_time = (rotation.trs2gcrs(reflect_time) @ dset.site_pos.trs.val[:, :, None])[:, :, 0] sta_sat_vector = dset.sat_pos.gcrs.pos.val - site_pos_reflect_time unit_vector = sta_sat_vector / np.linalg.norm(sta_sat_vector, axis=1)[:, None] rho12 = (np.linalg.norm(sta_sat_vector, axis=1) + delay.add("kinematic_models", dset)) / constant.c correction = (-dset.up_leg + rho12) / ( np.ones(dset.num_obs) - np.sum( unit_vector / constant.c * dset.sat_pos.vel.val, axis=1)) dset.up_leg[:] += correction sat_time_list = dset.obs_time + dset.time_bias + dset.up_leg sat_pos_ip, sat_vel_ip = interpolation.interpolate_with_derivative( np.array(t_sec), sat_pos, sat_time_list, kind="interpolated_univariate_spline") dset.sat_pos.gcrs[:] = np.concatenate((sat_pos_ip, sat_vel_ip), axis=1) delay.calculate_delay("satellite_models", dset) dset.calc[:] = delay.add("satellite_models", dset) dset.residual[:] = dset.obs - dset.calc log.info( f"{dset.num_obs} observations, residual = {dset.rms('residual'):.4f}" ) if not apriori_orbit_provider: orbit.update_orbit(sat_name, dset.site_pos.gcrs, dset.sat_pos.pos, dset.sat_pos.vel, dset.residual, dset.bin_rms) dset.write_as(stage=stage, label=iter_num, sat_name=sat_name) if iter_num >= iterations: break
def _calculate(self, dset): """Calculate precise orbits and satellite clock correction for given observation epochs The satellite position is determined for each observation epoch by interpolating within the given SP3 orbit time entries. The satellite velocities are calculated based on satellite positions 0.5 second before and after the observation epoch. Args: dset (Dataset): Dataset representing calculated precise orbits with following fields: ======================== =============== ======= ======================================================== Field Type Unit Description ======================== =============== ======= ======================================================== gnss_satellite_clock numpy.ndarray m Satellite clock correction gnss_relativistic_clock numpy.ndarray m Relativistic clock correction due to orbit eccentricity sat_posvel PosVelTable m Satellite position and velocity satellite numpy.ndarray Satellite numbers system numpy.ndarray GNSS identifiers time TimeTable Observation epochs ======================= =============== ======= ======================================================== """ # Check if satellites are given in SP3 file # TODO: Another solution has to be found for satellites not given in SP3 file, e.g. use of broadcast # ephemeris. not_available_sat = set(self.satellite) - set(self.dset_edit.satellite) if not_available_sat: log.fatal( "Satellites {} are not given in precise orbit file {}.", ", ".join(sorted(not_available_sat)), self.dset_edit.meta["parser"]["file_path"], ) log.info( "Calculating satellite position/velocity (precise) based on SP3 precise orbit file {}.", ", ".join(self.dset_edit.meta["parser"]["file_path"]), ) dset.vars["orbit"] = self.name dset.num_obs = len(self.time) dset.add_time("time", val=self.time, scale=self.time.scale) dset.add_text("satellite", val=self.satellite) dset.add_text("system", val=self.system) sat_pos = np.zeros((dset.num_obs, 3)) sat_vel = np.zeros((dset.num_obs, 3)) ref_time = dset.time[0] # Reference epoch used for interpolation # Loop over all given satellites for sat in set(self.satellite): log.debug("Interpolation for satellite: {}", sat) # Get array with information about, when observation are available for the given satellite (indicated by # True) idx = dset.filter(satellite=sat) orb_idx = self.dset_edit.filter(satellite=sat) if np.min(dset.time[idx].gps.mjd) < np.min( self.dset_edit.time[orb_idx].mjd): log.fatal( "Interpolation range is exceeded by satellite {} ( {} [epoch] < {} [precise orbit])." "".format( sat, np.max(dset.time[idx].gps.datetime), np.max(self.dset_edit.time[orb_idx].gps.datetime))) if np.max(dset.time[idx].gps.mjd) > np.max( self.dset_edit.time[orb_idx].mjd): log.fatal( "Interpolation range is exceeded by satellite {} ({} [epoch] > {} [precise orbit])." "".format( sat, np.max(dset.time[idx].gps.datetime), np.max(self.dset_edit.time[orb_idx].gps.datetime))) # Interpolation for given observation epochs (transmission time) sat_pos[idx], sat_vel[ idx] = interpolation.interpolate_with_derivative( self.dset_edit.time[orb_idx].gps.sec_to_reference( ref_time), self.dset_edit.sat_pos.itrs[orb_idx], dset.time[idx].gps.sec_to_reference(ref_time), kind="lagrange", window=10, dx=0.5, ) if np.isnan(np.sum(sat_pos[idx])) or np.isnan(np.sum( sat_vel[idx])): log.fatal( "NaN occurred by determination of precise satellite position and velocity for satellite {}.", sat) # Add satellite clock correction to Dataset dset.add_float("gnss_satellite_clock", val=self.satellite_clock_correction(), unit="meter") # Add satellite position and velocity to Dataset dset.add_posvel("sat_posvel", time="time", itrs=np.hstack((sat_pos, sat_vel))) # Add relativistic clock correction to Dataset dset.add_float("gnss_relativistic_clock", val=self.relativistic_clock_correction(), unit="meter") # +DEBUG # for num_obs in range(0, dset.num_obs): # print('DEBUG: ', dset.satellite[num_obs], # dset.time.gps.datetime[num_obs], # dset.time.gps.mjd_frac[num_obs]*24*3600, # ' '.join([str(v) for v in dset.sat_posvel.itrs_pos[num_obs]]), # dset.gnss_satellite_clock[num_obs], # dset.gnss_relativistic_clock[num_obs]) # -DEBUG return dset
def _calculate(self, dset_out: "Dataset", dset_in: "Dataset", time: str = "time") -> None: """Calculate precise orbits and satellite clock correction for given observation epochs As a first step observations are removed from unavailable satellites and for exceeding the interpolation boundaries. The input Dataset contains observation epochs for which the broadcast ephemeris and satellite clock correction should be determined. The satellite position is determined for each observation epoch by interpolating within the given SP3 orbit time entries. The satellite velocities are calculated based on satellite positions 0.5 second before and after the observation epoch. Args: dset_out (Dataset): Dataset representing calculated precise orbits with following fields: ======================== =============== ======= ======================================================== Field Type Unit Description ======================== =============== ======= ======================================================== gnss_satellite_clock numpy.ndarray m Satellite clock correction gnss_relativistic_clock numpy.ndarray m Relativistic clock correction due to orbit eccentricity sat_posvel PosVelTable m Satellite position and velocity satellite numpy.ndarray Satellite numbers system numpy.ndarray GNSS identifiers time TimeTable Observation epochs ======================= =============== ======= ======================================================== dset_in: Input Dataset containing model data for which broadcast ephemeris should be determined. time: Define time fields to be used. It can be for example 'time' or 'sat_time'. 'time' is related to observation time and 'sat_time' to satellite transmission time. """ # Clean orbits by removing unavailable satellites, unhealthy satellites and checking interpolation boundaries cleaners.apply_remover("gnss_clean_orbit", dset_in, orbit_flag="precise") # TODO: Another solution has to be found for satellites not given in SP3 file, e.g. use of broadcast # ephemeris. log.info( f"Calculating satellite position/velocity (precise) based on SP3 precise orbit file " f"{', '.join(self.dset_edit.meta['parser']['file_path'])}") sat_pos = np.zeros((dset_in.num_obs, 3)) sat_vel = np.zeros((dset_in.num_obs, 3)) ref_time = dset_in[time][0] # Reference epoch used for interpolation # Loop over all given satellites for sat in set(dset_in.satellite): log.debug(f"Interpolation for satellite: {sat}") # Get array with information about, when observation are available for the given satellite (indicated by # True) idx = dset_in.filter(satellite=sat) orb_idx = self.dset_edit.filter(satellite=sat) if np.min(dset_in[time][idx].gps.mjd) < np.min( self.dset_edit.time[orb_idx].mjd): log.fatal( f"Interpolation range is exceeded by satellite {sat} ({np.max(dset_in[time][idx].gps.datetime)} " f"[epoch] < {np.max(self.dset_edit.time[orb_idx].gps.datetime)} [precise orbit])" ) if np.max(dset_in[time][idx].gps.mjd) > np.max( self.dset_edit.time[orb_idx].mjd): log.fatal( f"Interpolation range is exceeded by satellite {sat} ({np.max(dset_in[time][idx].gps.datetime)} " f"[epoch] > {np.max(self.dset_edit.time[orb_idx].gps.datetime)} [precise orbit])" ) # Interpolation for given observation epochs (transmission time) diff_time_points = ref_time.gps - self.dset_edit.time.gps[orb_idx] diff_time_obs = ref_time.gps - dset_in[time].gps[idx] sat_pos[idx], sat_vel[ idx] = interpolation.interpolate_with_derivative( # self.dset_edit.time[orb_idx].gps.sec_to_reference(ref_time), diff_time_points.seconds, self.dset_edit.sat_pos.trs[orb_idx], # dset_in[time][idx].gps.sec_to_reference(ref_time), diff_time_obs.seconds, kind="lagrange", window=10, dx=0.5, ) if np.isnan(np.sum(sat_pos[idx])) or np.isnan(np.sum( sat_vel[idx])): log.fatal( f"NaN occurred by determination of precise satellite position and velocity for satellite {sat}" ) # Copy fields from model data Dataset dset_out.num_obs = dset_in.num_obs dset_out.add_text("satellite", val=dset_in.satellite) dset_out.add_text("system", val=dset_in.system) dset_out.add_time("time", val=dset_in[time]) dset_out.vars["orbit"] = self.name # Add float fields dset_out.add_float("gnss_relativistic_clock", val=self.relativistic_clock_correction( sat_pos, sat_vel), unit="meter") dset_out.add_float("gnss_satellite_clock", val=self.satellite_clock_correction(dset_in, time=time), unit="meter") # Add satellite position and velocity to Dataset dset_out.add_posvel("sat_posvel", time=dset_out.time, system="trs", val=np.hstack((sat_pos, sat_vel)))