$~\color{blue}{Input:}$
- $\color{blue}{\text{Pseudoranges}} ~\color{blue}{\rho_j}$
Navigation message:
- $\color{blue}{\text{Satellite position}}$ when transmitting signal:$ ~\color{blue}{s_j =(x_{sat}^j, y_{sat}^j, z_{sat}^j)}$
- Satellite clock $\color{blue}{\text{offset}}:\color{blue}{dt^j}$ (j= 1,2,3...n) (n>=4)
$~\color{red}{Unknowns:}$
- $\color{red}{\text{Receiver position}}$:$~\color{red}{(r =x_{rec}, y_{rec}, z_{rec})}$
- $\color{red}{\text{Receiver clock offset}}$:$~\color{red}{\delta_{rec}}$
Input: Raw pseudoranges from Rinex, satellite ephemeris information from broadscast ephemeris.
Initialise user clock bias = 0, user position at earth center ECEF = 0,0,0, alternativly use previous user position or 'Bancrofts method'
Initialise estimated satellite clock bias and pseudoranges for each satellite:
- Calculate
satellite clock bias
at receiver time tag- Calculate
satellite signal emission time
using measured pseudorange, receiver time tag, satellite clock bias and speed of light.- Estimate ionospherec delay using
Ionosphere-free Combination
for dual frequency receivers.- Calculate
satellite position
at satellite signal emission time in two steps:
- Perifocal coordinate system
- Earth-fixed coordinate system (ECEF)
- Transform satellite coordinates from the system tied to the earth at signal emission time to the system tied to the earth at
signal reception time
. Correct the ECEF coordinates of satellite position for earth rotation duringsignal propagation time
.
Do until change in user user position and receiver clock < threshold
For each satellite:
- Update the
modeled psudorange
with previous estimate of user position- Calculate the
prefit residual
which is the difference between measured and modeled pseudorange, update troposheric slant correction from new receiver position.- Form the linearized GPS measurement matrix G linearising around 'a priori' receiver position $(X_0, Y_0, Z_0)$
- Use the
Least square method
orKalman filter method
to solve y = Gx for correction in receiver position and receiver clock bias.- Update user position.
End For
End Do
Output: User position in ECEF and user clock bias.
$PR_{rec}^{sat} = \rho + c (\delta t_{rec} - \delta t^{sat}) + \sum \delta_k + \epsilon$
$\sum \delta_k = Iono_{rec}^{sat} + Trop_{rec}^{sat} + TGD^{sat} $
The propagation speed of GNSS electromagnetic signals in the ionosphere depends on its electron density and frequncy of signal. Codes and phases propagate at different speeds in the ionosphere, resulting in a delay in the code and an advance in the carrier measurements. Phase measurements are advanced on crossing the ionosphere, that is a negative delay, while the code measurements undergo a positive delay. The difference between the measured range (with frequency f signal) and the euclidean distance between the satellite and receiver is given by:
Phase ionospheric refraction $\Delta_{phase, f}^{iono} =-\frac{\text{40.3}}{{f}^2}\int_{}^{} N_e dl$
Code ionospheric refraction $\Delta_{group, f}^{iono} =+\frac{\text{40.3}}{{f}^2}\int_{}^{} N_e dl$
Where $N_e$ is electron density.
The ionospheric effects on code $R$ and carrier phase $\Phi$ measurements depend (99.9%) on the inverse of squared signal frequency f. Therefore, the dual-frequency receivers can eliminate these effect through a linear combination of code or carrier measurements:
Ionosphere-Free Combination:
Phase $ \Phi_{_{\mbox{iono-free}}} = \frac{f_1^2\;\Phi_{_{L_1}}-f_2^2\;\Phi_{_{L_2}}}{f_1^2-f_2^2}$
Pseudorange $ R_{_{\mbox{iono-free}}}=\frac{f_1^2\;R_{_{P_1}}-f_2^2\;R_{_{P_2}}}{f_1^2-f_2^2}$
Timing Group Delay or Total Group Delay (TGD) of the associated codes cancel in this combination and are not needed (since the satellite clocks are referred to the ionospheric-free combination of such codes).
# GPS frequencies
F1 = 1575.42e6
F2 = 1227.60e6
F5 = 1176.60e6
# Galileo frequncies
E1 = 1575.420
E6 = 1278.750
E5 = 1191.795
E5a = 1176.450
E5b = 1207.140
C1C = 6
C2L = 9
C5Q =10
GPSTime = "2022 06 15 13 59 50.0000000"
SAT = "G01"
P1 = 22009709.740
P2 = 22009701.580
P5 = 22009698.680
ionosfreeP1P2=(F1**2*P1-F2**2*P2)/(F1**2-F2**2)
print('Ionosfree P1 P2: '+ str(ionosfreeP1P2))
Ionosfree P1 P2: 22009722.35313868
ionosfreeP1P5=(F1**2*P1-F5**2*P5)/(F1**2-F5**2)
print('Ionosfree P1 P5: '+ str(ionosfreeP1P5))
Ionosfree P1 P5: 22009723.690324184
TEC_P1P2 =1/40.3/(1/F1**2-1/F2**2)*(P1-P2)
print('TEC P1 P2: '+str(TEC_P1P2))
TEC P1 P2: -7.768028923398451e+17
P1_TECcoor = P1-(40.3/F1**2)* TEC_P1P2
print('P1 TEC coorection: '+str(P1_TECcoor))
P1 TEC coorection: 22009722.353138685
TEC_P1P5 =1/40.3/(1/F1**2-1/F5**2)*(P1-P5)
print('TEC P1 P5: '+str(TEC_P1P5))
TEC P1 P5: -8.591558727486689e+17
P2_TECcoor = P2-(40.3/F2**2)* TEC_P1P2
print('P2_TEC coorection: '+str(P2_TECcoor))
P2_TEC coorection: 22009722.353138685
P5_TECcoor = P5-(40.3/F5**2)* TEC_P1P5
print('P5 TEC coorection: '+str(P5_TECcoor))
P5 TEC coorection: 22009723.690324184
# External dependencies and constants
import math
import datetime
C = 2.99792458E+08 #Speed of light in vacuum
WE = 7.2921151467E-5 #Earth’s angular velocity rad/sec
A_WGS84 = 6378137 #WGS84 ellipsoid semi major axis
B_WGS84 = 6356752.31425 #WGS84 ellipsoid semi minor axis
class Eph():
""" class to define GPS/GAL/QZS/CMP ephemeris """ #Rinex NAV RECORD Cell
sat = 0 # Satellite system (G), sat number (PRN) #SV / EPOCH / SV CLK [0]
iode = 0 # Issue of data, ephemeris #BROADCAST ORBIT - 1 [10]
iodc = 0.0 # IODC issue of data, clock #BROADCAST ORBIT - 6 [33]
af0 = 0.0 # SV clock correction (seconds) #SV / EPOCH / SV CLK [7]
af1 = 0.0 # SV clock clock drift correction coefficient (sec/sec) #SV / EPOCH / SV CLK [8]
af2 = 0.0 # SV clock clock drift rate (sec/sec2) #SV / EPOCH / SV CLK [9]
toc = 0 # Epoch: Toc - Time of clock (GPS) year, month, day, hour, minute, second #SV / EPOCH / SV CLK [1-6]
toe = 0 # Toe Time of Ephemeris (sec of GPS week) #BROADCAST ORBIT - 3 [18]
tot = 0 # Transmission time of message #BROADCAST ORBIT - 7 [34]
week = 0 # GPS Week # (to go with TOE) continuous number, not mod(1024)! #BROADCAST ORBIT - 5 [28]
crs = 0.0 # radius sine harmonic correction (meters) #BROADCAST ORBIT - 1 [11]
crc = 0.0 # radius cosine harmonic correction (meters) #BROADCAST ORBIT - 4 [23]
cus = 0.0 # argument of latitude correction sine harmonic correction (radians) #BROADCAST ORBIT - 2 [16]
cuc = 0.0 # argument of latitude cosine harmonic correction (radians) #BROADCAST ORBIT - 2 [14]
cis = 0.0 # inclination sine harmonic correction (radians) #BROADCAST ORBIT - 3 [21]
cic = 0.0 # inclination cosine harmonic correction (radians) #BROADCAST ORBIT - 2 [19]
e = 0.0 # eccentrisity #BROADCAST ORBIT - 3 [15]
i0 = 0.0 # inclination angle at reference time (radians) #BROADCAST ORBIT - 4 [22]
sqrtA = 0.0 # sqrt of semi-major axis (sqrt(m)) #BROADCAST ORBIT - 2 [17]
deln = 0.0 # Delta n, mean motion difference from computed value #BROADCAST ORBIT - 1 [12]
M0 = 0.0 # M0 mean anomaly at reference time(radians) #BROADCAST ORBIT - 1 [13]
OMG0 = 0.0 # OMEGA0, longitude of ascending node (radians) #BROADCAST ORBIT - 3 [20]
OMGd = 0.0 # OMEGA DOT, rate of right ascension (radians/sec) #BROADCAST ORBIT - 3 [25]
omg = 0.0 # Argument of perigee (radians) #BROADCAST ORBIT - 4 [24]
idot = 0.0 # Rate of inclination angle T (radians/sec) #BROADCAST ORBIT - 5 [26]
tgd = 0.0 # Group delay differential (only to be used with single freq. recvr.) (sec) #BROADCAST ORBIT - 6 [32]
sva = 0 # SV accuracy (meters) #BROADCAST ORBIT - 6 [30]
health = 0 # SV health, URA Index #BROADCAST ORBIT - 6 [31]
fit = 0 # Fit Interval in hours see section 6.11.(BNK) #BROADCAST ORBIT - 7
toes = 0
def __init__(self, sat=0):
self.sat = sat
Example measurements TOC 2022-6-15 13:59:50 and 2022-6-15 14:00:30
XYZapprox = [1962040.2281, 844038.2429, 5989768.7110] # APPROX POSITION XYZ from Rinex
# prn, time og measurement, C1C, C2W/C2L
satobs1 = [['G01', 2022, 6, 15, 13, 59, 50, 22009709.740, 22009701.580],
['G08', 2022, 6, 15, 13, 59, 50, 21982005.060, 21981997.620],
['G10', 2022, 6, 15, 13, 59, 50, 21597421.060, 21597412.480],
['G14', 2022, 6, 15, 13, 59, 50, 22853592.900, 22853583.060],
['G21', 2022, 6, 15, 13, 59, 50, 21228529.140, 21228516.720],
['G22', 2022, 6, 15, 13, 59, 50, 24111518.280, 24111510.480],
['G24', 2022, 6, 15, 13, 59, 50, 23071483.120, 23071476.140],
['G27', 2022, 6, 15, 13, 59, 50, 24314409.040, 24314403.480]]
satobs = [['G01',2022,6,15,14,0,30,21985760.860,21985752.700],
['G08',2022,6,15,14,0,30,22000879.460,22000872.020],
['G10',2022,6,15,14,0,30,21611138.380,21611129.860],
['G14',2022,6,15,14,0,30,22855354.620,22855344.820],
['G21',2022,6,15,14,0,30,21222574.420,21222561.980],
['G22',2022,6,15,14,0,30,24085793.080,24085785.080],
['G24',2022,6,15,14,0,30,23063882.340,23063875.380],
['G27',2022,6,15,14,0,30,24344764.260,24344756.740]]
ephs1 = [['G01', 2022, 6, 15, 14, 0, 0, .340794678777E-03, -.784439180279E-11, .000000000000E+00,
.520000000000E+02, -.842500000000E+02, .416731644260E-08, .264235247830E-01,
-.429712235928E-05, .120090576820E-01, .735744833946E-06, .515366528702E+04,
.309600000000E+06, -.279396772385E-07, .224388442451E+01, .875443220139E-07,
.987856820887E+00, .374968750000E+03, .921269443462E+00, -.838463496799E-08,
-.380730144653E-09, .000000000000E+00, .221400000000E+04, .000000000000E+00,
.000000000000E+00, .000000000000E+00, .512227416039E-08, .520000000000E+02,
.309335000000E+06, .000000000000E+00, .000000000000E+00, .000000000000E+00],
['G08', 2022, 6, 15, 14, 0, 0, -.722706317902E-04, -.159161572810E-11, .000000000000E+00,
.820000000000E+02, .140875000000E+03, .459733435457E-08, .219640037703E+01,
.740587711334E-05, .744616868906E-02, .344030559063E-05, .515378232765E+04,
.309600000000E+06, -.184401869774E-06, .116075602777E+01, .195577740669E-06,
.963246002285E+00, .315625000000E+03, .173596929569E+00, -.824212903204E-08,
.228938107620E-09, .000000000000E+00, .221400000000E+04, .000000000000E+00,
.000000000000E+00, .000000000000E+00, .512227416039E-08, .820000000000E+02,
.309335000000E+06, .000000000000E+00, .000000000000E+00, .000000000000E+00],
['G10', 2022, 6, 15, 14, 0, 0, -.455882865936E-03, -.147792889038E-10, .000000000000E+00,
.580000000000E+02, -.136031250000E+03, .365872382910E-08, -.198612952341E+01,
-.702962279320E-05, .765492708888E-02, .103227794170E-04, .515368768883E+04,
.309600000000E+06, -.279396772385E-07, -.300850082132E+01, -.137835741043E-06,
.974342578239E+00, .189125000000E+03, -.249896124194E+01, -.737780731529E-08,
.143934566881E-09, .000000000000E+00, .221400000000E+04, .000000000000E+00,
.000000000000E+00, .000000000000E+00, .232830643654E-08, .580000000000E+02,
.309336000000E+06, .000000000000E+00, .000000000000E+00, .000000000000E+00],
['G14', 2022, 6, 15, 14, 0, 0, -.111524946988E-03, -.682121026330E-12, .000000000000E+00,
.150000000000E+02, .985625000000E+02, .447661504041E-08, -.130199759818E+01,
.498257577419E-05, .199141632766E-02, .110100954771E-04, .515366280174E+04,
.309600000000E+06, .707805156708E-07, .173366849128E+00, -.931322574615E-08,
.952816756894E+00, .163218750000E+03, -.313134934801E+01, -.778175271266E-08,
-.286440502825E-09, .000000000000E+00, .221400000000E+04, .000000000000E+00,
.000000000000E+00, .000000000000E+00, -.791624188423E-08, .783000000000E+03,
.309337000000E+06, .000000000000E+00, .000000000000E+00, .000000000000E+00],
['G21', 2022, 6, 15, 14, 0, 0, .161311123520E-03, .000000000000E+00, .000000000000E+00,
.480000000000E+02, -.690000000000E+02, .473019703169E-08, .235557719582E+01,
-.333972275257E-05, .242386998143E-01, -.119768083096E-05, .515374702072E+04,
.309600000000E+06, .441446900368E-06, .214969297667E+01, .117346644402E-06,
.960199874762E+00, .397781250000E+03, -.937445853228E+00, -.824212903204E-08,
-.492163357722E-09, .000000000000E+00, .221400000000E+04, .000000000000E+00,
.000000000000E+00, .000000000000E+00, -.102445483208E-07, .480000000000E+02,
.309338000000E+06, .000000000000E+00, .000000000000E+00, .000000000000E+00],
['G22', 2022, 6, 15, 14, 0, 0, .276219565421E-03, .636646291241E-11, .000000000000E+00,
.790000000000E+02, .637500000000E+01, .447268630534E-08, .209109460950E+01,
.281259417534E-06, .134111947846E-01, .758469104767E-05, .515369627380E+04,
.309600000000E+06, .134110450745E-06, -.187625357347E+01, -.329688191414E-06,
.961125351849E+00, .230781250000E+03, -.184820560104E+01, -.769603485646E-08,
.706100840506E-09, .000000000000E+00, .221400000000E+04, .000000000000E+00,
.000000000000E+00, .000000000000E+00, -.838190317154E-08, .790000000000E+02,
.309485000000E+06, .000000000000E+00, .000000000000E+00, .000000000000E+00],
['G24', 2022, 6, 15, 14, 0, 0, .220152083784E-03, -.841282599140E-11, .000000000000E+00,
.840000000000E+02, -.106875000000E+02, .558523264736E-08, .346157288052E+00,
-.622123479843E-06, .125489170896E-01, .565126538277E-05, .515367090797E+04,
.309600000000E+06, -.782310962677E-07, -.991197407488E+00, .219792127609E-06,
.933901572841E+00, .259656250000E+03, .817978952381E+00, -.853106963901E-08,
-.304655547269E-09, .000000000000E+00, .221400000000E+04, .000000000000E+00,
.000000000000E+00, .000000000000E+00, .279396772385E-08, .840000000000E+02,
.309339000000E+06, .000000000000E+00, .000000000000E+00, .000000000000E+00],
['G27', 2022, 6, 15, 14, 0, 0, .215651467443E-03, .329691829393E-11, .000000000000E+00,
.700000000000E+02, .148187500000E+03, .440161191623E-08, .225401738364E+01,
.754743814468E-05, .105409963289E-01, .368244946003E-05, .515365896797E+04,
.309600000000E+06, -.240281224251E-06, .118118244570E+01, -.150874257088E-06,
.971982136512E+00, .313812500000E+03, .688193783108E+00, -.803997775449E-08,
.273225666660E-09, .000000000000E+00, .221400000000E+04, .000000000000E+00,
.100000000000E+01, .000000000000E+00, .186264514923E-08, .700000000000E+02,
.309340000000E+06, .000000000000E+00, .000000000000E+00, .000000000000E+00]]
ephs =[['G01', 2022,6,15,16,0,0,.340738333762E-03,-.784439180279E-11,.000000000000E+00,
.540000000000E+02,-.773437500000E+02,.429303596504E-08,.107670373452E+01,
-.395812094212E-05,.120082932990E-01,.612810254097E-06,.515366287804E+04,
.316800000000E+06,-.912696123123E-07,.224382395479E+01,.147148966789E-06,
.987854126192E+00,.385250000000E+03,.921173225877E+00,-.836713423901E-08,
-.345014371233E-09,.000000000000E+00,.221400000000E+04,.000000000000E+00,
.000000000000E+00,.000000000000E+00,.512227416039E-08,.540000000000E+02,
.309628000000E+06,.000000000000E+00,.000000000000E+00,.000000000000E+00],
['G08',2022,6,15,16,0,0,-.722822733223E-04,-.159161572810E-11,.000000000000E+00,
.830000000000E+02,.150718750000E+03,.456054710795E-08,-.303652790620E+01,
.783987343311E-05,.744804507121E-02,.411830842495E-05,.515377832794E+04,
.316800000000E+06,-.152736902237E-06,.116069700634E+01,-.447034835815E-07,
.963248680888E+00,.303375000000E+03,.173456537710E+00,-.807605068564E-08,
.300012496725E-09,.000000000000E+00,.221400000000E+04,.000000000000E+00,
.000000000000E+00,.000000000000E+00,.512227416039E-08,.830000000000E+02,
.309618000000E+06,.000000000000E+00,.000000000000E+00,.000000000000E+00],
['G10',2022,6,15,16,0,0,-.455989036709E-03,-.147792889038E-10,.000000000000E+00,
.840000000000E+02,-.154406250000E+03,.376515683390E-08,-.935626010933E+00,
-.817887485027E-05,.765509542543E-02,.104885548353E-04,.515369258881E+04,
.316800000000E+06,.203028321266E-06,-.300855339128E+01,.912696123123E-07,
.974342123272E+00,.186187500000E+03,-.249930062284E+01,-.761103131572E-08,
-.496449250533E-10,.000000000000E+00,.221400000000E+04,.000000000000E+00,
.100000000000E+01,.000000000000E+00,.232830643654E-08,.840000000000E+02,
.309619000000E+06,.000000000000E+00,.000000000000E+00,.000000000000E+00],
['G14',2022,6,15,16,0,0,-.111529603601E-03,-.682121026330E-12,.000000000000E+00,
.160000000000E+02,.826562500000E+02,.437018203562E-08,-.251014454845E+00,
.437907874584E-05,.199296663050E-02,.110045075417E-04,.515366699409E+04,
.316800000000E+06,.391155481339E-07,.173310839846E+00,.372529029846E-07,
.952814682476E+00,.163093750000E+03,-.313214701434E+01,-.779961059937E-08,
-.283226083217E-09,.000000000000E+00,.221400000000E+04,.000000000000E+00,
.000000000000E+00,.000000000000E+00,-.791624188423E-08,.784000000000E+03,
.309620000000E+06,.000000000000E+00,.000000000000E+00,.000000000000E+00],
['G21',2022,6,15,16,0,0,.161311123520E-03,.000000000000E+00,.000000000000E+00,
.520000000000E+02,-.630625000000E+02,.467519474063E-08,-.287745316399E+01,
-.290945172310E-05,.242399923736E-01,-.149756669998E-05,.515374453163E+04,
.316800000000E+06,.204890966415E-07,.214963212220E+01,.389292836189E-06,
.960198189480E+00,.405531250000E+03,-.937461246052E+00,-.809105131048E-08,
-.133219834855E-09,.000000000000E+00,.221400000000E+04,.000000000000E+00,
.000000000000E+00,.000000000000E+00,-.102445483208E-07,.520000000000E+02,
.309621000000E+06,.000000000000E+00,.000000000000E+00,.000000000000E+00],
['G22',2022,6,15,16,0,0,.276264734566E-03,.636646291241E-11,.000000000000E+00,
.820000000000E+02,.171875000000E+01,.443554190098E-08,.314129354829E+01,
.184401869774E-06,.134117096895E-01,.768713653088E-05,.515369504356E+04,
.316800000000E+06,.145286321640E-06,-.187630757124E+01,-.875443220139E-07,
.961130650538E+00,.228937500000E+03,-.184823787448E+01,-.753888545341E-08,
.637526555540E-09,.000000000000E+00,.221400000000E+04,.000000000000E+00,
.000000000000E+00,.000000000000E+00,-.838190317154E-08,.820000000000E+02,
.309622000000E+06,.000000000000E+00,.000000000000E+00,.000000000000E+00],
['G24',2022,6,15,16,0,0,.220091082156E-03,-.841282599140E-11,.000000000000E+00,
.850000000000E+02,-.113750000000E+02,.552022993973E-08,.139631594392E+01,
-.678002834320E-06,.125492629595E-01,.570528209209E-05,.515367191124E+04,
.316800000000E+06,-.189989805222E-06,-.991258418486E+00,.838190317154E-07,
.933898960069E+00,.256125000000E+03,.818009805323E+00,-.849606818106E-08,
-.373229832235E-09,.000000000000E+00,.221400000000E+04,.000000000000E+00,
.000000000000E+00,.000000000000E+00,.279396772385E-08,.850000000000E+02,
.309623000000E+06,.000000000000E+00,.000000000000E+00,.000000000000E+00],
['G27',2022,6,15,16,0,0,.215674750507E-03,.329691829393E-11,.000000000000E+00,
.710000000000E+02,.162375000000E+03,.431732269096E-08,-.297898170629E+01,
.817701220512E-05,.105392733822E-01,.352598726749E-05,.515366197205E+04,
.316800000000E+06,-.726431608200E-07,.118112470578E+01,-.257045030594E-06,
.971984339666E+00,.317718750000E+03,.688196155961E+00,-.801533387083E-08,
.314655963827E-09,.000000000000E+00,.221400000000E+04,.000000000000E+00,
.100000000000E+01,.000000000000E+00,.186264514923E-08,.710000000000E+02,
.309624000000E+06,.000000000000E+00,.000000000000E+00,.000000000000E+00]]
- Calculate GPST second of week from
receiver time tag GPST
= "2022 06 15 13 59 50.0000000"- Select the last transmitted navigation message block before instant GPST of week = 309590 (wsec)
# Example calculating sec of GPS week from Time of clock (GPS) year, month, day, hour, minute, second
yy = 2022
M = 6
dd = 15
hh = 13
mm = 59
ss = 50
obstime = datetime.datetime(yy, M, dd, hh, mm, ss)
gps_day_of_week = obstime.weekday() + 1 #GPS week starts midnight between saturday and sunday
wsec = gps_day_of_week *86400 + hh*3600 + mm*60 + ss
print(wsec)
# Epoch: Toc - Time of clock (GPS) year, month, day, hour, minute, second #SV / EPOCH / SV CLK
yy = 2022
M = 6
dd = 15
hh = 14
mm = 0
ss = 0
obstime = datetime.datetime(yy, M, dd, hh, mm, ss)
gps_day_of_week = obstime.weekday() + 1 #GPS week starts midnight between saturday and sunday
wsec = gps_day_of_week *86400 + hh*3600 + mm*60 + ss
print(wsec) # Toe Time of Ephemeris (sec of GPS week)
309590 309600
def gps_sec_of_week(toc):
yy = toc[0]
M = toc[1]
dd = toc[2]
hh = toc[3]
mm = toc[4]
ss = toc[5]
obstime = datetime.datetime(yy, M, dd, hh, mm, ss)
gps_day_of_week = obstime.weekday() + 1 #GPS week starts midnight between saturday and sunday
gps_sec = gps_day_of_week *86400 + hh*3600 + mm*60 + ss
return gps_sec
The clock offsets are due to clock synchronisation errors referring to the GNSS time scale. The modelling of such offsets, as well as its effects on the navigation solution, are described as follows:
$\delta t_{rcv}$ is estimated together with the receiver coordinates, hence no modelling is needed in this case.
This can be split into two terms:
$\delta t_{sat} = \delta t_{clockdrift}+\Delta_{rel}$
The first term $\delta t_{sat}$ can be calculated from values broadcast in the navigation messages (for SPP) or from the precise products available from IGS centres or other providers (for PPP). The broadcast navigation message provides the clock information as the coefficients of a polynomial, in a given reference epoch (t0), to compute the satellite clock offset as
${\color{black}{\delta t_{clockdrift} = a_0 + a_1(t − t_0) + a_2(t − t_0)^2} }$
The relativistic effect is a function of Eccentic anomali:
$\Delta_{rel} = eph.e \cdot\sqrt{eph.A}\cdot sinE_k$
def calculate_clock_bias(eph, t):
F = -4.442807633E-10 # -2*sqrt(MU)/c**2
MU = 3.986005E+14 # value of Earth's universal gravitational parameters
A = eph.sqrtA**2 # semi-major axis
cmm = math.sqrt(MU/A**3) # computed mean motion
tk = t - eph.toe # time from ephemeris reference epoch
if (tk > 302400): # account for beginning or end of week crossover
tk = tk-604800
if (tk < -302400):
tk = tk+604800
# apply mean motion correction
n = cmm + eph.deln
# Mean anomaly
Mk = eph.M0 + n*tk
# Eccentric anaomaly
Ek = Mk # initial value
for x in range(3): # iterate 2 times
Ek=Ek+(Mk-Ek + eph.e*math.sin(Ek))/(1-eph.e*math.cos(Ek))
dt_rel = F*eph.e*eph.sqrtA*math.sin(Ek)
dt_sat = eph.af0 + eph.af1*(t-eph.toc) + eph.af2*(t-eph.toc)**2 + dt_rel
print(eph.sat," SV clock drift bias(m): ", dt_sat *C, " SV relativity clock bias(m): ", dt_rel * C)
return [dt_sat, dt_rel]
def make_eph_list():
eph_list = []
for eph in ephs:
sv = Eph(eph[0])
sv.iode = eph[10]
sv.iodc =eph[33]
sv.af0 = eph[7]
sv.af1 = eph[8]
sv.af2 = eph[9]
sv.toc = gps_sec_of_week(eph[1:7])
sv.toe = eph[18]
sv.tot = eph[34]
sv.week = eph[28]
sv.crs = eph[11]
sv.crc = eph[23]
sv.cus = eph[16]
sv.cuc = eph[14]
sv.cis = eph[21]
sv.cic = eph[19]
sv.e = eph[15]
sv.i0 = eph[22]
sv.sqrtA = eph[17]
sv.deln = eph[12]
sv.M0 = eph[13]
sv.OMG0 = eph[20]
sv.OMGd = eph[25]
sv.omg = eph[24]
sv.idot = eph[26]
sv.tdg = eph[32]
sv.sva = eph[30]
sv.health = eph[31]
eph_list.append(sv)
return eph_list
sv_ephs = make_eph_list()
satobs_eph = [] # list of observations:
# time tag of observation rec 0
# pseudorange measurement C1C, C2W/C2L record 1 and 2
# ephemeris of sv linked to observation record 3
# computed clock bias of sv record 4
# computed emmision time record 5
# computed Xsat at emmision time record 6
# computed Ysat at emmision time record 7
# computed Zsat at emmision time record 8
# computed Xsat at reception time record 9
# computed Ysat at reception time record 10
# computed Zsat at reception time record 11
# computed troposphere correction record 12
# computed sv relativistic clock bias record 13
for obs in satobs:
rcv = gps_sec_of_week(obs[1:7]) # Receiver reception time of signal
sv = obs[0]
for eph in sv_ephs:
if sv == eph.sat:
satobs_eph.append([rcv, obs[7], obs[8], eph ,0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
i = 0
for mea in satobs_eph:
dt_sat = calculate_clock_bias(mea[3], mea[0]) #
satobs_eph[i][4] = dt_sat[0] # dt sat including realtivistic bias
satobs_eph[i][13] = dt_sat[1] # dt sat realtivistic bias
i = i + 1
G01 SV clock drift bias(m): 102167.38652414233 SV relativity clock bias(m): -0.2577029075724685 G08 SV clock drift bias(m): -21670.371872938864 SV relativity clock bias(m): -4.1126808044774945 G10 SV clock drift bias(m): -136665.50278015406 SV relativity clock bias(m): 4.803098164396179 G14 SV clock drift bias(m): -33432.9500639449 SV relativity clock bias(m): 1.3177120849272912 G21 SV clock drift bias(m): 48348.336859136216 SV relativity clock bias(m): -11.521363666201287 G22 SV clock drift bias(m): 82800.48503129893 SV relativity clock bias(m): -7.914015016737841 G24 SV clock drift bias(m): 65996.73687962558 SV relativity clock bias(m): -2.993093583476232 G27 SV clock drift bias(m): 64645.02356264339 SV relativity clock bias(m): -5.553255202087061
We need to know the emission time of the satellite signal to calculate the position of the satellite in the reference frame. As the measurements are linked to the signal reception time, which is given by the receiver time tags (i.e. in the receiver clock), an algorithm is needed to obtain the signal emission time in the GNSS (GPS, Glonass, Galileo, etc.) time scale. The emission time can be directly obtained from the reception time by taking into account that the pseudorange R is a direct measurement of the time difference between both epochs, each one being measured in the corresponding clock.
Emission time computation from receiver time-tag and code pseudorange:
$T[emission]$ in this system time scale can be computed from the receiver measurement time tags $t_{rcv}$ receiver as
${\color{black}{T_{[emission]} = t_{sat[emission]} − \delta t_{sat} = t_{rcv[reception]} − R/C −\delta t_{sat}}}$
where $R$ is the pseudorange and $\delta t_{sat}$ satellite clock bias
def emission_time(r, t_rec, dt_sat):
tems = t_rec-(r/C+dt_sat)
return tems
i = 0
for mea in satobs_eph:
p1 = mea[1]
p2 = mea[2]
ionosfreeP1P2=(F1**2*p1-F2**2*p2)/(F1**2-F2**2) # Using inosphere free pseudrorange
reception_time_rec = mea[0]
dt_sv = mea[4]
satobs_eph[i][5] = emission_time(ionosfreeP1P2, reception_time_rec, dt_sv)
print('SV ', mea[3].sat)
print('Ionos-free pseudorange ', ionosfreeP1P2)
print('Computed emission time of SV signal: ',satobs_eph[i][5])
i = i + 1
SV G01 Ionos-free pseudorange 21985773.473138683 Computed emission time of SV signal: 309629.92632255994 SV G08 Ionos-free pseudorange 22000890.960214686 Computed emission time of SV signal: 309629.9266852117 SV G10 Ionos-free pseudorange 21611151.549600683 Computed emission time of SV signal: 309629.92836882494 SV G14 Ionos-free pseudorange 22855369.76813225 Computed emission time of SV signal: 309629.92387421295 SV G21 Ionos-free pseudorange 21222593.648853585 Computed emission time of SV signal: 309629.9290477748 SV G22 Ionos-free pseudorange 24085805.445822243 Computed emission time of SV signal: 309629.9193822083 SV G24 Ionos-free pseudorange 23063893.09826535 Computed emission time of SV signal: 309629.92284699227 SV G27 Ionos-free pseudorange 24344775.883872915 Computed emission time of SV signal: 309629.918578936
Orbital calculation of satellite coordinates in Conventional Terrestrial System - CTS, Earth Centered Earth Fixed
Compute the time $t_k$ (time of data) from the ephemerides reference epoch $toe$ ($t$ and $toe$ are expressed in seconds in the GPS week):
$toe$ - Time of Ephemeris (sec of GPS week)
$t$ - emission time
$t_k = t − toe$
If $t_k$ > 302 400 s, subtract 604 800 s from tk. If $tk$ < −302 400 s, add 604 800 s.( week rollover)
Compute the mean anomaly for tk:
$M_k = eph.M0 + eph.deln\cdot t_k$
Solve (iteratively) the Kepler equation for the eccentric anomaly Ek:
$M_k = E_k − e\cdot sinE_k$
$E_k = M_k$ initial value
Iterate:
$ E_k=E_k+ \frac{M_k-E_k-eph.e\cdot sinE_k}{1-eph.e\cdot cosE_k}$
Compute the true anomaly $v_k$ (use atan2):
$v_k = atan2(\frac{(1-eph.e^2)\cdot sinE_k}{1-eph.e \cdot cosE_k}, \frac{cosE_k-eph.e}{1-eph.e\cdot cosE_k})$
Compute the argument of latitude $u_k$ from the argument of perigee $\omega$, true anomaly $v_k$ and corrections $cuc$ and $cus$:
$u_k = \omega + v_k + eph.cuc \cdot cos2(\omega + v_k) + eph.cus \cdot sin2(\omega + v_k)$
Compute the radial distance $r_k$, considering corrections $crc$ and $crs$
$r_k = eph.sqrtA^2(1 − eph.e\cdot cosE_k) + eph.crc\cdot cos2(\omega + v_k) + eph.crs\cdot sin2(\omega + v_k)$
Compute the inclination $i_k$ of the orbital plane from the inclination $io$ at reference time $toe$, and corrections $cic$ and $cis$
$\begin{align*} i_k = eph.io+ eph.idot t_k + eph.cic\cdot cos2(\omega + v_k) + eph.cis\cdot sin2(\omega + v_k) \end{align*}$
Compute the longitude of the ascending node $\lambda_k$ (with respect to Greenwich). This calculation uses the right ascension at the beginning of the current week ($\Omega_0$), the correction from the apparent sidereal time variation in Greenwich between the beginning of the week and reference time $t_k = t − toe$, and the change in longitude of the ascending node from the reference time $toe$:
$\lambda_k = \Omega_0 + t_k(\dot\Omega − ωE) − ωE\cdot toe$
Compute the satellite coordinates in the orbital plane:
$ x_k = r_k\cdot cos(u_k)$
$ y_k = r_k\cdot sin(u_k)$
Compute the satellite coordinates in the CTS frame, applying three rotations (around $u_k, i_k$ and $\lambda_k$):
$ x_{CTS} = x_k\cdot cos(\omega) - y_k\cdot cos(i_k)\cdot sin(\omega)$
$ y_{CTS} = x_k\cdot sin(\omega) + yk\cdot cos(i_k)\cdot cos(\omega)$
$ z_{CTS} = y_k\cdot sin(i_k)$
def orbit_calculation(eph, t):
F = -4.442807633e-10 # -2*sqrt(MU)/c**2
MU = 3.986005e14 # value of Earth's universal gravitational parameters
A = eph.sqrtA**2 # semi-major axis
cmm = math.sqrt(MU/A**3) # computed mean motion
tk = t - eph.toe # time from ephemeris reference epoch
if (tk > 302400): # account for beginning or end of week crossover
tk = tk-604800
if (tk < -302400):
tk = tk+604800
# apply mean motion correction
n = cmm + eph.deln
# Mean anomaly
Mk = eph.M0 + n*tk
# Eccentric anomaly
Ek = Mk # initial value
for x in range(3): # iterate 2 times
Ek=Ek+(Mk-Ek+eph.e*math.sin(Ek))/(1-eph.e*math.cos(Ek))
# True anomaly
vk = math.atan2((math.sqrt(1-eph.e**2))*math.sin(Ek)/(1-eph.e*math.cos(Ek)), (math.cos(Ek)-eph.e)/(1-eph.e*math.cos(Ek)))
# vk = 2*math.atan((math.sqrt((1+eph.e)/(1-eph.e))*math.tan(Ek/2)))
# argument of latitude
phi = vk + eph.omg
# compute harmonic corrections
du = eph.cus*math.sin(2*phi) + eph.cuc*math.cos(2*phi); #argument of lattitude correction
dr = eph.crs*math.sin(2*phi) + eph.crc*math.cos(2*phi); #radius correction
di = eph.cis*math.sin(2*phi) + eph.cic*math.cos(2*phi); #inclination correction
# corrected argument of lattitude
uk = phi + du
# corrected radius
rk = A*(1-eph.e*math.cos(Ek)) + dr
# corrected inclination angle at reference time
ik = eph.i0 + eph.idot*tk + di;
# corrected longitude of ascending node
omega = eph.OMG0 + (eph.OMGd - WE)*tk - WE*eph.toe
xk = rk*math.cos(uk) #x posistion in orbital plane
yk = rk*math.sin(uk) #y posistion in orbital plane
x = xk*math.cos(omega) - yk*math.cos(ik)*math.sin(omega); #Earth-fixed coordinates ECEF
y = xk*math.sin(omega) + yk*math.cos(ik)*math.cos(omega);
z = yk*math.sin(ik);
print('SV',eph.sat)
return[x,y,z]
i = 0
print('Satellite coordinates in ECEF at signal emission time')
for mea in satobs_eph:
svXYZ = orbit_calculation(mea[3],mea[5]) #input eph and sv emission time, ouput sv coordinates at emmision time in CTS
satobs_eph[i][6] = svXYZ[0]
satobs_eph[i][7] = svXYZ[1]
satobs_eph[i][8] = svXYZ[2]
print('Emission time (s): ',mea[5])
print('X ', satobs_eph[i][6],'Y ', satobs_eph[i][7],'Z ', satobs_eph[i][8])
i = i + 1
Satellite coordinates in ECEF at signal emission time SV G01 Emission time (s): 309629.92632255994 X 13031293.310108224 Y -14140924.611738503 Z 17855617.049962882 SV G08 Emission time (s): 309629.9266852117 X 21981431.907177202 Y 1766152.6526481416 Z 15015223.581840554 SV G10 Emission time (s): 309629.92836882494 X 1242509.956379449 Y 15655321.7354242 Z 21522353.842483167 SV G14 Emission time (s): 309629.92387421295 X 758181.5523897447 Y -16481033.125235895 Z 20796258.11599192 SV G21 Emission time (s): 309629.9290477748 X 15365982.640044274 Y -3228753.5219289847 Z 21995975.30020505 SV G22 Emission time (s): 309629.9193822083 X 17509064.222957592 Y 19347881.04037465 Z 5853841.371709985 SV G24 Emission time (s): 309629.92284699227 X -14337055.003536966 Y 10177482.993712874 Z 19488564.12592963 SV G27 Emission time (s): 309629.918578936 X 23309804.967000924 Y 12499352.057187416 Z 3929408.615245241
Account for earth rotation $\alpha$ during signal propagation time from emission to reception $ \delta t$. Earth fixed reference system (Conventional Terrestrial System - CTS) at reception time is used to build the navigation equations.
Looking down to the North Pole from space:
$ \alpha = \omega E\cdot \delta t_{prop} = ~ 0.07 sec $
Methodology:
Neclecting atmospheric delay, the signal propagation time is given by:
Signal propagation time $\delta t_{prop} = \frac{|P_{sat} - P_{rec}|}{C} = \frac{p0_{sat -rec}}{C}$
$P_{sat}$ = satellite ECEF position vector
$P_{rec}$ = receiver ECEF position Vector
approximate value of $ p0_{sat-rec}$ can be used in this equation
$ [Xsat_R,Ysat_R,Zsat_R]_{CTS-reception} = R3(\alpha)\cdot [Xsat_E,Ysat_E,Zsat_E]_{CTS-emission}$
$\begin{bmatrix}x_R \\y_R \\z_R \end{bmatrix}=\begin{bmatrix}cos\alpha & \sin\alpha & 0 \\-sin\alpha & \cos\alpha & 0\\ 0 & 0 & 1 \end{bmatrix}\begin{bmatrix}x_E \\y_E\\z_E \end{bmatrix}$
The geometric range between satellite coordinates at emission time and the “approximate position of the receiver” at reception time (both coordinates given in the same reference system CTS system) is computed by:
$ p0_{sat-rec} = \sqrt{(x_{sat}-x_{0rec})^2+(y_{sat}-y_{0rec})^2+(z_{sat}-z_{0rec})^2}$
def earth_rotation_coor(Xsat, Ysat, Zsat, transmission_time):
rot=WE*transmission_time
rsin = math.sin(rot)
rcos = math.cos(rot)
#Rotating the Satellite Reference Frame
Xsat_reception = rcos*Xsat + rsin*Ysat
Ysat_reception = -rsin*Xsat + rcos*Ysat
Zsat_reception = Zsat
return [Xsat_reception, Ysat_reception, Zsat_reception]
def geometric_range(X0rec, Y0rec, Z0rec, Xsat, Ysat, Zsat):
p0sat_rec = math.sqrt((Xsat-X0rec)**2+(Ysat-Y0rec)**2+(Zsat-Z0rec)**2)
return p0sat_rec
def transmission_time(p0sat_rec):
transmission_time = p0sat_rec/C # negative time but positiv definition in rotation
return transmission_time
def reception_time_satcoords(X0rec, Y0rec, Z0rec, Xsat, Ysat, Zsat):
p0 = geometric_range(X0rec, Y0rec, Z0rec, Xsat, Ysat, Zsat)
dt = transmission_time(p0)
svCTS_reception = earth_rotation_coor(Xsat, Ysat, Zsat, dt)
return svCTS_reception
i = 0
for mea in satobs_eph:
print('SV',mea[3].sat, 'ECEF-coordinates of sattelite at signal reception time')
svCTS_reception = reception_time_satcoords(XYZapprox[0], XYZapprox[1], XYZapprox[2], mea[6], mea[7], mea[8])
for x in range(1): # check difference p0sat_rec
p0 = geometric_range(XYZapprox[0], XYZapprox[1], XYZapprox[2], svCTS_reception[0], svCTS_reception[1] ,svCTS_reception[2])
dt = transmission_time(p0)
print('Propagation time of signal:', dt)
svCTS_reception = earth_rotation_coor(mea[6], mea[7], mea[8], dt)
print('X ', svCTS_reception[0], 'Y ', svCTS_reception[1],'Z ', svCTS_reception[2]) # Reception time sat
satobs_eph[i][9] = svCTS_reception[0]
satobs_eph[i][10] = svCTS_reception[1]
satobs_eph[i][11] = svCTS_reception[2]
i = i + 1
for mea in satobs_eph:
print('SV ',mea[3].sat, ': Impact of earth rotaion on geometric range:')
print(math.sqrt((mea[6]-mea[9])**2 + (mea[7]-mea[10])**2 +(mea[8]-mea[11])**2)) # earth rotation impact (distance)
SV G01 ECEF-coordinates of sattelite at signal reception time Propagation time of signal: 0.0736773734213029 X 13031217.335838398 Y -14140994.623967856 Z 17855617.049962882 SV G08 ECEF-coordinates of sattelite at signal reception time Propagation time of signal: 0.07331472738948755 X 21981441.349058382 Y 1766035.135616039 Z 15015223.581840554 SV G10 ECEF-coordinates of sattelite at signal reception time Propagation time of signal: 0.07163111158659244 X 1242591.7307322798 Y 15655315.245055374 Z 21522353.842483167 SV G14 ECEF-coordinates of sattelite at signal reception time Propagation time of signal: 0.07612571895154371 X 758090.0632776495 Y -16481037.3337805 Z 20796258.11599192 SV G21 ECEF-coordinates of sattelite at signal reception time Propagation time of signal: 0.07095216096756103 X 15365965.93454789 Y -3228833.024147361 Z 21995975.30020505 SV G22 ECEF-coordinates of sattelite at signal reception time Propagation time of signal: 0.08061771194983812 X 17509177.963747263 Y 19347778.10886743 Z 5853841.371709985 SV G24 ECEF-coordinates of sattelite at signal reception time Propagation time of signal: 0.07715293631283941 X -14336997.743966822 Y 10177563.654983908 Z 19488564.12592963 SV G27 ECEF-coordinates of sattelite at signal reception time Propagation time of signal: 0.08142096809997065 X 23309879.179127373 Y 12499213.659411553 Z 3929408.615245241 SV G01 : Impact of earth rotaion on geometric range: 103.31409359130132 SV G08 : Impact of earth rotaion on geometric range: 117.89572492010201 SV G10 : Impact of earth rotaion on geometric range: 82.03151631174106 SV G14 : Impact of earth rotaion on geometric range: 91.585858513515 SV G21 : Impact of earth rotaion on geometric range: 81.23839200892466 SV G22 : Impact of earth rotaion on geometric range: 153.4009857006593 SV G24 : Impact of earth rotaion on geometric range: 98.91864848388583 SV G27 : Impact of earth rotaion on geometric range: 157.03943477895933
From ECEF(x,y,s) to Local Geodetic(e,n,u) coordinates:
$\begin{bmatrix}\Delta e\\\Delta n\\\Delta u\end{bmatrix}=\bf{R_1 }\begin{bmatrix}\pi/2-\phi \end{bmatrix}\bf{R_3}\begin{bmatrix}\pi/2+\lambda \end{bmatrix}\begin{bmatrix}\Delta x\\\Delta y\\\Delta z\end{bmatrix} =\begin{bmatrix}-sin\lambda & cos\lambda & 0 \\-cos\lambda sin\phi & -sin\lambda sin\phi & cos\phi\\ cos\lambda cos\phi & sin\lambda cos\phi & sin\phi \end{bmatrix}\begin{bmatrix}\Delta x \\\Delta y\\\Delta z \end{bmatrix}$
$~~Az = atan2(\frac{\Delta e}{\Delta n})$ $~~E = atan(\frac{\Delta u}{\sqrt{\Delta e^2 + \Delta n^2 }})$
def dXYZ_receiver_sat(xrec, yrec, zrec, xsat, ysat, zsat): #ECEF rec-sat vector
dxyz =[xsat-xrec, ysat-yrec, zsat-zrec]
return dxyz
def rad_deg(radians):
degrees = 180 * radians/math.pi
return degrees
def deg_rad(degrees):
radians = math.pi * degrees/180
return radians
def cartesian_geographic(da, db, dx, dy, dz):
# da major axis
# db minor axis
# d_la latitude of point *Changing arg
# d_lg longitude of point *Changing arg
# dh ellipsoidal height *Changing arg
# dx x-axis
# dy y-axis
# dz z-axis
de1 = (da ** 2 - db ** 2) / (da ** 2)
d_e2 = (da ** 2 - db ** 2) / (db ** 2)
dp = math.sqrt(dx ** 2 + dy ** 2)
d_t = (dz * da) / (dp * db)
d_teta = math.atan(d_t)
d_c2 = math.cos(d_teta)
d_s2 = math.sin(d_teta)
d_t1 = (dz + (d_e2 * db * d_s2 ** 3)) / (dp - de1 * da * d_c2 ** 3)
d_t2 = dy / dx
d_la = math.atan(d_t1)
d_lg = math.atan(d_t2)
d_c = math.cos(d_la)
d_s = math.sin(d_la)
d_n = da ** 2 / (math.sqrt((da ** 2 * d_c ** 2) + (db ** 2 * d_s ** 2)))
dh = (dp / math.cos(d_la)) - d_n
return d_lg, d_la, dh
# test cartesian to geodetic
point = cartesian_geographic(A_WGS84, B_WGS84, 2660520.2488, 589219.3944, 5747982.8939)
print('Longitude: ',rad_deg(point[0])) # longitude
print('Latitude: ',rad_deg(point[1])) # latitude
print('Ellipsoidal height: ',point[2]) # ellipsoidal height
Longitude: 12.48760652265354 Latitude: 64.78409006861396 Ellipsoidal height: 538.7430106811225
def dxyz_denu(long, lat, vector): # Transform ECEF rec-sat vector to local geodetic system enu
dx = vector[0]
dy = vector[1]
dz = vector[2]
sinl = math.sin(long)
sinb = math.sin(lat)
cosl = math.cos(long)
cosb = math.cos(lat)
de = -sinl*dx + cosl*dy
dn = -cosl*sinb*dx - sinl*sinb*dy + cosb*dz
du = cosl*cosb*dx + sinl*cosb*dy + sinb*dz
return de,dn,du
def azimut(de,dn):
azi = math.atan2(de, dn)
return azi
def elevation(de,dn,du):
elev = math.atan2(du, math.sqrt(de**2 + dn**2))
return elev
i = 0
xrec = XYZapprox[0]
yrec = XYZapprox[1]
zrec = XYZapprox[2]
point = cartesian_geographic(A_WGS84, B_WGS84, xrec, yrec, zrec)
print('Position of receiver: ')
print('Longitude of revceiver: ',rad_deg(point[0])) # longitude
print('Latitude of receiver: ',rad_deg(point[1])) # latitude
print('Ellipsoidal height of receiver: ',point[2]) # ellipsoidal height
for mea in satobs_eph:
print('SV',mea[3].sat)
dxyz = dXYZ_receiver_sat(xrec, yrec, zrec, mea[9], mea[10], mea[11])
print ('Geometric range SV-receiver: ', math.sqrt(dxyz[0]**2+dxyz[1]**2+dxyz[2]**2))
dneu = dxyz_denu(point[0], point[1], dxyz)
print('Azimut SV:',rad_deg(azimut(dneu[0], dneu[1])))
print('Elevation angle SV:' ,rad_deg(elevation(dneu[0], dneu[1], dneu[2])))
Position of receiver: Longitude of revceiver: 23.276599787253613 Latitude of receiver: 70.49576914993929 Ellipsoidal height of receiver: 38.70248999912292 SV G01 Geometric range SV-receiver: 22087920.87696028 Azimut SV: -90.13007647727463 Elevation angle SV: 34.79023770350955 SV G08 Geometric range SV-receiver: 21979202.331695005 Azimut SV: -154.27954135049987 Elevation angle SV: 42.218620172251455 SV G10 Geometric range SV-receiver: 21474467.011819255 Azimut SV: 88.79645013548918 Elevation angle SV: 49.687197212120154 SV G14 Geometric range SV-receiver: 22821916.40150329 Azimut SV: -51.14121857614808 Elevation angle SV: 29.683929402086285 SV G21 Geometric range SV-receiver: 21270922.736877814 Azimut SV: -117.70007173418381 Elevation angle SV: 61.320476707944806 SV G22 Geometric range SV-receiver: 24168582.02377926 Azimut SV: 151.98471741159088 Elevation angle SV: 17.038037029769498 SV G24 Geometric range SV-receiver: 23129868.419146217 Azimut SV: 44.755381600680195 Elevation angle SV: 22.782574605077336 SV G27 Geometric range SV-receiver: 24409392.159429844 Azimut SV: 174.4847310279382 Elevation angle SV: 14.576198068402974
# Import libraries
from mpl_toolkits import mplot3d
import numpy as np
import matplotlib.pyplot as plt
%matplotlib notebook
# Define Data
x = []
y = []
z = []
recx = []
recy = []
recz = []
sat = []
for mea in satobs_eph:
x.append(mea[9])
y.append(mea[10])
z.append(mea[11])
sat.append(mea[3].sat)
recx.append(XYZapprox[0])
recy.append(XYZapprox[1])
recz.append(XYZapprox[2])
# Create Figure
fig = plt.figure(figsize = (10, 7))
ax = plt.axes(projection ="3d")
# Create Plot and change color
ax.scatter3D(x, y, z, color='red')
ax.scatter3D(recx, recy, recz, color='green')
for mea in satobs_eph:
ax.text(mea[9], mea[10], mea[11], mea[3].sat)
ax.text(recx[0], recy[0], recz[0], 'REC')
for mea in satobs_eph:
x, y, z = [recx[0], mea[9]], [recy[0], mea[10]], [recz[0], mea[11]]
ax.plot(x, y, z, color='blue', linewidth=0.2)
u = np.linspace(0, 2 * np.pi, 25)
v = np.linspace(0, np.pi, 25)
x = 6371000 * np.outer(np.cos(u), np.sin(v))
y = 6371000 * np.outer(np.sin(u), np.sin(v))
z = 6371000 * np.outer(np.ones(np.size(u)), np.cos(v))
ax.plot_surface(x, y, z, color='grey' )
ax.view_init(10, -75)
# Show plot
plt.show()
The tropospheric delay does not depend on frequency and affects both the code and carrier phases in the same way. It can be modeled (about 90%) as:
A simple model is model presented here is from [Collins, 1999] and it is the model adopted by the SBAS (WAAS, EGNOS...)
$ Trop_{sat-rec} = (d_{dry} + d_{wet})\cdot M_{E_{sat}} $
$ d_{dry} = 2.3exp(-0.116 \cdot 10^{-3}\cdot H) m $
$ d_{wet} = 0.1m$
$ M_{E_{sat}} = \frac{\text{0.01}}{\sqrt{0.002001 + sin^2(elev)}} $
The obliquity factor $M(E)$ as function of elevation angle
def tropospheric_delay(h_sealevel, sat_elev):
d_dry = 2.3* math.exp(-0.116E-03 * h_sealevel)
d_wet = 0.1
m_elev = 1.001/math.sqrt(0.002001 + math.sin(sat_elev)**2)
troposd_sat_rec = (d_dry + d_wet) * m_elev
return troposd_sat_rec
print('Tropsheric delay at 42m and elev 14.865201084274346 :' ,tropospheric_delay(42, deg_rad(14.865201084274346)))
Tropsheric delay at 42m and elev 14.865201084274346 : 9.18225409265146
xrec = XYZapprox[0]
yrec = XYZapprox[1]
zrec = XYZapprox[2]
point = cartesian_geographic(A_WGS84, B_WGS84, xrec, yrec, zrec)
print('Receiver position:')
print('Longitude: ',rad_deg(point[0])) # longitude
print('Latitude: ',rad_deg(point[1])) # latitude
print('Height: ', point[2]) # ellipsoidal height
print("Tropospheric delay:")
i = 0
for mea in satobs_eph:
dxyz = dXYZ_receiver_sat(xrec, yrec, zrec, mea[9], mea[10], mea[11])
dneu = dxyz_denu(point[0], point[1], dxyz)
tropo_d = tropospheric_delay(point[2], elevation(dneu[0], dneu[1], dneu[2]))
satobs_eph[i][12] = tropo_d
print('SV',mea[3].sat, satobs_eph[i][12])
i = i + 1
Receiver position: Longitude: 23.276599787253613 Latitude: 70.49576914993929 Height: 38.70248999912292 Tropospheric delay: SV G01 4.179599477413308 SV G08 3.551996515901868 SV G10 3.1316813259152916 SV G14 4.810814002878507 SV G21 2.7230540901157143 SV G22 8.070429923387122 SV G24 6.136540694026958 SV G27 9.35827713799435
Prefit residual is the difference between measured and modeled pseudorange
$PR_{rec}^{sat} = \rho + c (\delta t_{rec} - \delta t^{sat}) + \sum \delta_k + \epsilon$
Prefit residual:
$ Prefit_{rec}^{sat}= PR_{rec}^{sat} - C_{modeled}$
Modeled range includes estimated "true range" $\rho0_{rec}^{sat}$:
$ C_{modeled} = \rho0_{rec}^{sat} - C(\delta t_{sat} + \delta_{rel}) + Trop_{rec}^{sat} + Iono_{rec}^{sat} + TGD$
xrec = XYZapprox[0]
yrec = XYZapprox[1]
zrec = XYZapprox[2]
for mea in satobs_eph:
print('SV: ', mea[3].sat)
p0_rs = geometric_range(xrec, yrec, zrec, mea[9], mea[10], mea[11])
print('P0 geometric range: ',p0_rs )
cdt_sat = C*mea[4]
print('Sv clock bias(s): ',mea[4])
print('Sv clock bias(m):', cdt_sat)
tropo = mea[12]
print('Tropospehric delay(m): ', tropo)
c_modeled = p0_rs-cdt_sat+tropo
print("C modeled :" ,c_modeled)
p1 = mea[1]
p2 = mea[2]
ionosfreeP1P2=(F1**2*p1-F2**2*p2)/(F1**2-F2**2) # Using inosphere free pseudrorange
prefit = ionosfreeP1P2 - c_modeled
prefitL1 = p1 - c_modeled
print('Prefit residual ionos: ',prefit)
print('Prefit L1: ',prefitL1)
SV: G01 P0 geometric range: 22087920.87696028 Sv clock bias(s): 0.0003407937184468541 Sv clock bias(m): 102167.38652414233 Tropospehric delay(m): 4.179599477413308 C modeled : 21985757.67003562 Prefit residual ionos: 15.803103063255548 Prefit L1: 3.1899643801152706 SV: G08 P0 geometric range: 21979202.331695005 Sv clock bias(s): -7.228457986404335e-05 Sv clock bias(m): -21670.371872938864 Tropospehric delay(m): 3.551996515901868 C modeled : 22000876.25556446 Prefit residual ionos: 14.704650226980448 Prefit L1: 3.2044355422258377 SV: G10 P0 geometric range: 21474467.011819255 Sv clock bias(s): -0.0004558670477966262 Sv clock bias(m): -136665.50278015406 Tropospehric delay(m): 3.1316813259152916 C modeled : 21611135.646280736 Prefit residual ionos: 15.90331994742155 Prefit L1: 2.733719263225794 SV: G14 P0 geometric range: 22821916.40150329 Sv clock bias(s): -0.00011152031737884782 Sv clock bias(m): -33432.9500639449 Tropospehric delay(m): 4.810814002878507 C modeled : 22855354.16238124 Prefit residual ionos: 15.605751011520624 Prefit L1: 0.45761876180768013 SV: G21 P0 geometric range: 21270922.736877814 Sv clock bias(s): 0.00016127269238753236 Sv clock bias(m): 48348.336859136216 Tropospehric delay(m): 2.7230540901157143 C modeled : 21222577.123072766 Prefit residual ionos: 16.52578081935644 Prefit L1: -2.703072763979435 SV: G22 P0 geometric range: 24168582.02377926 Sv clock bias(s): 0.0002761926887143336 Sv clock bias(m): 82800.48503129893 Tropospehric delay(m): 8.070429923387122 C modeled : 24085789.609177887 Prefit residual ionos: 15.836644355207682 Prefit L1: 3.470822110772133 SV: G24 P0 geometric range: 23129868.419146217 Sv clock bias(s): 0.00022014141823282818 Sv clock bias(m): 65996.73687962558 Tropospehric delay(m): 6.136540694026958 C modeled : 23063877.818807285 Prefit residual ionos: 15.279458064585924 Prefit L1: 4.521192714571953 SV: G27 P0 geometric range: 24409392.159429844 Sv clock bias(s): 0.00021563258793736364 Sv clock bias(m): 64645.02356264339 Tropospehric delay(m): 9.35827713799435 C modeled : 24344756.49414434 Prefit residual ionos: 19.3897285759449 Prefit L1: 7.7658556625247
Solving SPS position equation and receiver position
$\color{blue} {color~blue = measured}$
$\color{green} {color~green = computed}$
$\color{red} {color~red = estimated}$
$\color{blue}{PR_{rec}^{sat}} = \color{red}{\rho_{rec}^{sat}} + \color{green}c \cdot (\color{red}{\delta t_{rec}} - \color{green}{\delta t^{sat}}) + \color{green}{\sum \delta_k} + \epsilon$
Form the ionos-free combination
of L1 and L2 or L5 pseudoranges:
$\color{blue}{PR_{_{\mbox{iono-free}}}^{sat-rec}}=\color{blue}{\frac{f_1^2\;R_{_{P_1}}-f_2^2\;R_{_{P_2}}}{f_1^2-f_2^2}}$
Prefit residual:
$ ResPrefit_{rec}^{sat}= PR_{rec}^{sat} - C_{modeled}$
Modeled range includes estimated "true range" $\rho0_{rec}^{sat}$:
$\color{green} { C_{modeled} = \rho0_{rec}^{sat} - c(\delta t_{sat} + \delta_{rel}) + Trop_{rec}^{sat} + Iono_{rec}^{sat} + TGD}$
The following equation can be written for geometric range, neglecting the multipath and receiver noise terms:
$ PR^j- D^j\simeq\sqrt{(x^j-x)^2+(y^j-y)^2+(z^j-z)^2} +c(\delta t)~~$
$ D^j = - c(\delta t_{sat} + \delta_{rel}) + Trop_{rec}^{j} + Iono_{rec}^{j} + TGD$
$ j=1,2,...,n~~~~ (n \geq 4)~~satellite $
Linearising $\color{red}{\rho_{rec}^{sat}}$ around "an apriori" receiver position $(x_0, y_0, z_0)$
$ \color{blue}{PR_{rec}^{j}}=\color{red}{\rho_0^j+\frac{x_0-x^j}{\rho_0^j} dx +\frac{y_0-y^j}{\rho_0^j} dy+\frac{z_0-z^j}{\rho_0^j}dz} +c (\delta t_{rec} - \delta t^{sat}) + \sum \delta_k \qquad with \qquad dx=x-x_0; dy=y-y_0;dz=z-z_0 \qquad$
We can rearrange equation by separating the known and unknown terms of each side:
$\color{blue}{PR_{rec}^{j}}+\color{green}{{\rho_0^j + c\cdot\delta t^{sat}}-\sum \delta_k} =\color{green}{\frac{x_0-x^j}{\rho_0^j}} dx +\color{green}{\frac{y_0-y^j}{\rho_0^j}} dy+\color{green}{\frac{z_0-z^j}{\rho_0^j}}dz +\color{green}c\cdot\delta t_{rec})\qquad with \qquad dx=x-x_0; dy=y-y_0;dz=z-z_0 \qquad$
For all satellites in view calculate prefit residual:
$\color{green}{Prefit_{rec}^{j}}= \color{green}{PR_{rec}^{j} - C_{modeled}^j} = \color{green}{PR_{rec}^{j}-{\rho_0^j + c\cdot dt^{sat}}-\sum \delta_k} $
$\color{green}{Prefit_{rec}^{j}} = \color{green}{\frac{x_0-x^j}{\rho_0^j}} dx -\color{green}{\frac{y_0-y^j}{\rho_0^j}} dy+ \color{green}{\frac{z_0-z^j}{\rho_0^j}}dz + \color{green}c\cdot dt_{rec}$
We can simplify notation:
$ a_x^j = \frac{x_0-x^j}{\rho_0^j}~~~$ $ a_y^j = \frac{y_0-y^j}{\rho_0^j}~~~$ $ a_z^j = \frac{z_0-z^j}{\rho_0^j}$
$Y = Prefit_{rec}^{j}= {PR_{rec}^{sat} - C_{modeled}}$
Let us assume that we have n satellites visible simultaneously. We can form the design matrix of unit vectors
and the matrix of unknowns:
$\vec{Gx} = \begin{bmatrix}a_x^1 & a_y^1 & a_z^1 & c\\a_x^2 & a_y^2 & a_z^2 & c\\a_x^3 & a_y^3 & a_z^3 & c\\a_x^4 & a_y^4 & a_z^4 & c\\ \vdots&\vdots&\vdots&\vdots\\a_x^n & a_y^n & a_z^n & c\end{bmatrix}\begin{bmatrix}\Delta x \\\Delta y\\\Delta z\\\Delta t \end{bmatrix}$
Matrix of observations (Prefit residuals):
$\vec{F} = \begin{bmatrix}Prefit_{rec}^1\\Prefit_{rec}^2\\Prefit_{rec}^3\\Prefit_{rec}^4\\\vdots\\Prefit_{rec}^n \end{bmatrix}$
Estimating the unknowns by LS method:
$ F~= Gx$
$\hat x~= (G^TG)^{-1} \cdot G^T \cdot F$
Iterativ least square solution:
Update $(x_{rec},~y_{rec},~z_{rec})$ and use it to calculate geometric range $\rho0_{rec}^{sat}$ and fill in $(x0_{rec},~y0_{rec},~z0_{rec})$ in the next step of calculation of $C_{modeled}$.
$\begin{bmatrix}x_{rec}\\y_{rec}\\z_{rec}\end{bmatrix} = \begin{bmatrix}x0_{rec}\\y0_{rec}\\z0_{rec}\end{bmatrix} + \begin{bmatrix}dx\\dy\\dz\end{bmatrix}$
xrec = XYZapprox[0]
yrec = XYZapprox[1]
zrec = XYZapprox[2]
xrec = 0
yrec = 0
zrec = 0
cdt_rec = 0
residuals_prev = 0
i = 1
for x in range(11):
y = []
G = []
for mea in satobs_eph:
p0_rs = geometric_range(xrec, yrec, zrec, mea[9], mea[10], mea[11])
cdt_sat = C*mea[4]
tropo = mea[12]
c_modeled = p0_rs-cdt_sat+tropo # + cdt_rec
p1 = mea[1]
p2 = mea[2]
ionosfreeP1P2=(F1**2*p1-F2**2*p2)/(F1**2-F2**2)
prefit = ionosfreeP1P2 - c_modeled
y.append([prefit])
x1 = xrec-mea[9]/p0_rs
y1 = yrec-mea[10]/p0_rs
z1 = zrec-mea[11]/p0_rs
G.append([x1,y1,z1,C])
ymat = np.array(y)
Gmat = np.array(G)
res = np.linalg.inv(np.dot(Gmat.transpose(),Gmat))
res = np.dot(res,Gmat.transpose())
res = np.dot(res,ymat)
xrec = xrec + res[0,0]
yrec = yrec + res[1,0]
zrec = zrec + res[2,0]
cdt_rec = res[3,0] # + cdt_rec
residuals = math.sqrt(res[0,0]**2+res[1,0]**2+res[2,0]**2)
print('iter:', i, math.sqrt(res[0,0]**2+res[1,0]**2+res[2,0]**2) )
print('residual change: ', residuals-residuals_prev)
residuals_prev = residuals
i = i+1
print('Residuals:')
print('dx:', XYZapprox[0]-xrec)
print('dy:', XYZapprox[1]-yrec)
print('dz:', XYZapprox[2]-zrec)
print('Receiver clock bias: ', cdt_rec)
print('X_ECEF:', xrec)
print('Y_ECEF:', yrec)
print('Z_ECEF:', zrec)
point = cartesian_geographic(A_WGS84, B_WGS84, xrec, yrec, zrec)
print('Latitude:',rad_deg(point[1]))
print('Longitude:',rad_deg(point[0]))
print('Ellipsoidal height:',point[2])
iter: 1 7527002.431962806 residual change: 7527002.431962806 iter: 2 940255.84193763 residual change: -6586746.590025175 iter: 3 220827.24033477184 residual change: -719428.6016028582 iter: 4 6696.084249741599 residual change: -214131.15608503023 iter: 5 2677.3390988890087 residual change: -4018.74515085259 iter: 6 833.2741860400477 residual change: -1844.064912848961 iter: 7 6.985661685345771 residual change: -826.2885243547018 iter: 8 0.7385928389945531 residual change: -6.247068846351217 iter: 9 0.06951584022928747 residual change: -0.6690769987652656 iter: 10 0.01568315619266222 residual change: -0.05383268403662525 iter: 11 0.0019362998563241288 residual change: -0.013746856336338091 Residuals: dx: 0.30062739574350417 dy: -0.005998189211823046 dz: -2.135871988721192 Receiver clock bias: -4.097940710301451e-05 X_ECEF: 1962039.9274726042 Y_ECEF: 844038.2488981892 Z_ECEF: 5989770.846871989 Latitude: 70.49577785482565 Longitude: 23.276603121883067 Ellipsoidal height: 40.624387479387224