import numpy as np import sympy from laika.constants import EARTH_ROTATION_RATE, SPEED_OF_LIGHT from laika.helpers import ConstellationId def calc_pos_fix_gauss_newton(measurements, posfix_functions, x0=None, signal='C1C', min_measurements=6): ''' Calculates gps fix using gauss newton method To solve the problem a minimal of 4 measurements are required. If Glonass is included 5 are required to solve for the additional free variable. returns: 0 -> list with positions ''' if x0 is None: x0 = [0, 0, 0, 0, 0] n = len(measurements) if n < min_measurements: return [], [] Fx_pos = pr_residual(measurements, posfix_functions, signal=signal) x = gauss_newton(Fx_pos, x0) residual, _ = Fx_pos(x, weight=1.0) return x.tolist(), residual.tolist() def pr_residual(measurements, posfix_functions, signal='C1C'): def Fx_pos(inp, weight=None): vals, gradients = [], [] for meas in measurements: pr = meas.observables[signal] pr += meas.sat_clock_err * SPEED_OF_LIGHT w = (1 / meas.observables_std[signal]) if weight is None else weight val, *gradient = posfix_functions[meas.constellation_id](*inp, pr, *meas.sat_pos, w) vals.append(val) gradients.append(gradient) return np.asarray(vals), np.asarray(gradients) return Fx_pos def gauss_newton(fun, b, xtol=1e-8, max_n=25): for _ in range(max_n): # Compute function and jacobian on current estimate r, J = fun(b) # Update estimate delta = np.linalg.pinv(J) @ r b -= delta # Check step size for stopping condition if np.linalg.norm(delta) < xtol: break return b def get_posfix_sympy_fun(constellation): # Unknowns x, y, z = sympy.Symbol('x'), sympy.Symbol('y'), sympy.Symbol('z') bc = sympy.Symbol('bc') bg = sympy.Symbol('bg') var = [x, y, z, bc, bg] # Knowns pr = sympy.Symbol('pr') sat_x, sat_y, sat_z = sympy.Symbol('sat_x'), sympy.Symbol('sat_y'), sympy.Symbol('sat_z') weight = sympy.Symbol('weight') theta = EARTH_ROTATION_RATE * (pr - bc) / SPEED_OF_LIGHT val = sympy.sqrt( (sat_x * sympy.cos(theta) + sat_y * sympy.sin(theta) - x) ** 2 + (sat_y * sympy.cos(theta) - sat_x * sympy.sin(theta) - y) ** 2 + (sat_z - z) ** 2 ) if constellation == ConstellationId.GLONASS: res = weight * (val - (pr - bc - bg)) elif constellation == ConstellationId.GPS: res = weight * (val - (pr - bc)) else: raise NotImplementedError(f"Constellation {constellation} not supported") res = [res] + [sympy.diff(res, v) for v in var] return sympy.lambdify([x, y, z, bc, bg, pr, sat_x, sat_y, sat_z, weight], res, modules=["numpy"])