1414import numpy as np
1515
1616from gnss_lib_py .parsers .navdata import NavData
17+ from gnss_lib_py .utils import constants as consts
1718from gnss_lib_py .utils .coordinates import ecef_to_geodetic
1819
1920def solve_wls (measurements , weight_type = None , only_bias = False ,
@@ -29,13 +30,7 @@ def solve_wls(measurements, weight_type = None, only_bias = False,
2930 in rx_est_m will be updated if only_bias is set to True.
3031
3132 If only_bias is set to True, then the receiver position must also
32- be passed in as the receiver_state
33-
34- receiver_state : gnss_lib_py.parsers.navdata.NavData
35- Either estimated or ground truth receiver position in ECEF frame
36- in meters as an instance of the NavData class with the
37- following rows: ``x_rx*_m``, `y_rx*_m``, ``z_rx*_m``,
38- ``gps_millis``.
33+ be passed in as the receiver_state.
3934
4035 Parameters
4136 ----------
@@ -170,7 +165,7 @@ def wls(rx_est_m, pos_sv_m, corr_pr_m, weights = None,
170165 array with shape (4 x 1) and the following order:
171166 x_rx_m, y_rx_m, z_rx_m, b_rx_m.
172167 pos_sv_m : np.ndarray
173- Satellite positions as an array of shape [# svs x 3] where
168+ Satellite ECEF positions as an array of shape [# svs x 3] where
174169 the columns contain in order x_sv_m, y_sv_m, and z_sv_m.
175170 corr_pr_m : np.ndarray
176171 Corrected pseudoranges for all satellites with shape of
@@ -195,11 +190,35 @@ def wls(rx_est_m, pos_sv_m, corr_pr_m, weights = None,
195190 array with shape (4 x 1) and the following order:
196191 x_rx_m, y_rx_m, z_rx_m, b_rx_m.
197192
193+ Notes
194+ -----
195+ This function internally updates the used SV position to account for
196+ the time taken for the signal to travel to the Earth from the GNSS
197+ satellites.
198+ Since the SV and receiver positions are calculated in an ECEF frame
199+ of reference, which is moving with the Earth's rotation, the reference
200+ frame is slightly (about 30 m along longitude) different when the
201+ signals are received than when the signals were transmitted. Given
202+ the receiver's position is estimated when the signal is received,
203+ the SV positions need to be updated to reflect the change in the
204+ frame of reference in which their position is calculated.
205+
206+ This update happens after every Gauss-Newton update step and is
207+ adapted from [1]__.
208+
209+ References
210+ ----------
211+ .. [1] https://github.com/google/gps-measurement-tools/blob/master/opensource/FlightTimeCorrection.m
212+
198213 """
199214
200215 rx_est_m = rx_est_m .copy () # don't change referenced value
201216
202217 count = 0
218+ # Store the SV position at the original receiver time.
219+ # This position will be modified by the time taken by the signal to
220+ # travel to the receiver.
221+ rx_time_pos_sv_m = pos_sv_m .copy ()
203222 num_svs = pos_sv_m .shape [0 ]
204223 if num_svs < 4 and not only_bias :
205224 raise RuntimeError ("Need at least four satellites for WLS." )
@@ -245,6 +264,14 @@ def wls(rx_est_m, pos_sv_m, corr_pr_m, weights = None,
245264 else :
246265 rx_est_m += pos_x_delta
247266
267+
268+ # Update the satellite positions based on the time taken for
269+ # the signal to reach the Earth and the satellite clock bias.
270+ tx_time = (corr_pr_m .reshape (- 1 ) - rx_est_m [3 ,0 ])/ consts .C
271+ dtheta = consts .OMEGA_E_DOT * tx_time
272+ pos_sv_m [:, 0 ] = np .cos (dtheta )* rx_time_pos_sv_m [:,0 ] + np .sin (dtheta )* rx_time_pos_sv_m [:,1 ]
273+ pos_sv_m [:, 1 ] = - np .sin (dtheta )* rx_time_pos_sv_m [:,0 ] + np .cos (dtheta )* rx_time_pos_sv_m [:,1 ]
274+
248275 count += 1
249276
250277 if count >= max_count :
0 commit comments