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 " ] )