Skip to content
This repository was archived by the owner on Apr 13, 2021. It is now read-only.

Commit 86a4670

Browse files
committed
Merge pull request #323 from dt-exafore/i106-glo-orb-comp
WIP: Add GLO orbits computation
2 parents c0ad207 + 0a1f6b8 commit 86a4670

File tree

6 files changed

+350
-2
lines changed

6 files changed

+350
-2
lines changed

include/libswiftnav/constants.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
* \note This is actually not identical to the usual WGS84 definition. */
4242
#define GPS_OMEGAE_DOT 7.2921151467e-5
4343

44-
/** Earths Gravitational Constant as defined in the ICD in m^3 / s^2
44+
/** Earth's Gravitational Constant as defined in the ICD in m^3 / s^2
4545
* \note This is actually not identical to the usual WGS84 definition. */
4646
#define GPS_GM 3.986005e14
4747

@@ -70,6 +70,22 @@
7070
/** GPS C/A code chipping rate in Hz. */
7171
#define GPS_CA_CHIPPING_RATE 1.023e6
7272

73+
/** GLO semi-major axis of Earth
74+
* NOTE: there is define WGS84_A which is 6378137, differ than defined
75+
* in GLO ICD, refer A.3.1.2. */
76+
#define GLO_A_E 6378136.0
77+
78+
/** Second zonal harmonic of the geopotential */
79+
#define GLO_J02 1.0826257e-3
80+
81+
/** Earth's Gravitational Constant as defined in the GLO ICD in m^3 / s^2
82+
* \note This is actually not identical to the usual WGS84 definition. */
83+
#define GLO_GM 3.9860044e14
84+
85+
/** Earth's rotation rate as defined in the GLO ICD in rad / s
86+
* \note This is actually not identical to the usual WGS84 definition. */
87+
#define GLO_OMEGAE_DOT 7.292115e-5
88+
7389
/* \} */
7490

7591
/** \defgroup dgnss_constants DGNSS

python/swiftnav/ephemeris.pxd

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ cdef extern from "libswiftnav/ephemeris.h":
2929
double a_gf0
3030
double a_gf1
3131

32+
ctypedef struct ephemeris_glo_t:
33+
double gamma;
34+
double tau;
35+
double pos[3]
36+
double vel[3]
37+
double acc[3]
38+
3239
ctypedef struct ephemeris_t:
3340
gnss_signal_t sid
3441
gps_time_t toe
@@ -39,6 +46,7 @@ cdef extern from "libswiftnav/ephemeris.h":
3946
# HACK: Actually an anonymous union in libswiftnat!
4047
ephemeris_kepler_t kepler
4148
ephemeris_xyz_t xyz
49+
ephemeris_glo_t glo
4250

4351
s8 calc_sat_state(const ephemeris_t *e, const gps_time_t *t,
4452
double pos[3], double vel[3],

python/swiftnav/ephemeris.pyx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ cdef class Ephemeris:
3434
self._thisptr.kepler = kwargs.pop('kepler')
3535
elif 'xyz' in kwargs:
3636
self._thisptr.xyz = kwargs.pop('xyz')
37+
elif 'glo' in kwargs:
38+
self._thisptr.glo = kwargs.pop('glo')
3739

3840
def __getattr__(self, k):
3941
return self._thisptr.get(k)

python/tests/test_ephemeris.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def test_sat_state():
6666
assert eph.is_valid(t.GpsTime(**{ 'wn': 1867, 'tow': 518400.0 + 3600.*1}))
6767
assert eph.is_valid(t.GpsTime(**{ 'wn': 1867, 'tow': 518400.0 - 3600.*1}))
6868
assert not eph.is_valid(t.GpsTime(**{ 'wn': 1868, 'tow': 518400.0,}))
69-
assert len(eph.to_dict()) == 8
69+
assert len(eph.to_dict()) == 9
7070
assert repr(eph)
7171
eph = e.Ephemeris(**{'sid': {'sat': 17, 'code': 0},
7272
'toe': { 'wn': 1867, 'tow': 518400.0,},
@@ -193,3 +193,71 @@ def test_parameters():
193193
valid = 1
194194
fit_interval = 4 * 60 * 60
195195
assert not e.Ephemeris.is_params_valid(valid, fit_interval, toe, time)
196+
197+
def test_glo_sat_state():
198+
eph1 = e.Ephemeris(**{'sid': {'sat': 3, 'code': 3,},
199+
'toe': {'wn': 1892, 'tow': 303900.0,}, #from GPS
200+
'ura': 2.0,
201+
'fit_interval': 4 * 60 * 60,
202+
'valid': 1,
203+
'healthy': 1,
204+
'glo': { #data at 2016-04-13T12:25:00
205+
'gamma' : 9.09494701772928238e-13,
206+
'tau' : -6.72144815325737000e-05,
207+
'pos' : [-1.4379786621093750e+07,-7.6587543945312500e+06,1.9676794921875000e+07],
208+
'vel' : [2.6736078262329102e+03,-2.4776077270507813e+02,1.8579368591308594e+03],
209+
'acc' : [0.0,0.0,-2.79396772384643555e-06],
210+
}})
211+
eph2 = e.Ephemeris(**{'sid': {'sat': 3, 'code': 3,},
212+
'toe': {'wn': 1892, 'tow': 305700.0,}, #from GPS
213+
'ura': 2.0,
214+
'fit_interval': 4 * 60 * 60,
215+
'valid': 1,
216+
'healthy': 1,
217+
'glo': { #data at 2016-04-13T12:55:00
218+
'gamma' : 9.09494701772928238e-13,
219+
'tau' : -6.72172755002975464e-05,
220+
'pos' : [-9.2793393554687500e+06,-8.5263706054687500e+06,2.2220868652343750e+07],
221+
'vel' : [2.9441843032836914e+03,-7.2328472137451172e+02,9.5054054260253906e+02],
222+
'acc' : [0.0,0.0,-2.79396772384643555e-06],
223+
}})
224+
#check that both ephemeris are valid at 2016-04-13T12:40:00
225+
assert eph1.is_valid(t.GpsTime(**{ 'wn': 1892, 'tow': 304800.0,}))
226+
assert eph2.is_valid(t.GpsTime(**{ 'wn': 1892, 'tow': 304800.0,}))
227+
assert eph1.is_healthy()
228+
assert eph2.is_healthy()
229+
#set time in between 2 ephemeris
230+
time = t.GpsTime(**{ 'wn': 1892, 'tow': 304800.0,})
231+
#calculate SV orbits at the time
232+
pos1, vel1, clock_err1, clock_rate_err1 = eph1.calc_sat_state(time)
233+
pos2, vel2, clock_err2, clock_rate_err2 = eph2.calc_sat_state(time)
234+
#check if position difference of the SV in 2.5 m range
235+
assert np.allclose(pos1, pos2, 0, 2.5)
236+
#check if velocity difference of the SV in 0.005 m/s range
237+
assert np.allclose(vel1, vel2, 0, 0.005)
238+
assert abs(clock_err1 - clock_err2) < 1.0e-8
239+
assert abs(clock_rate_err1 - clock_rate_err2) < 1.0e-8
240+
# Now check GLO orbit computation (pos and vel) at particular time
241+
# set reference ephemeris at 2007-15-11T06.15.00
242+
eph = e.Ephemeris(**{'sid': {'sat': 3, 'code': 3,},
243+
'toe': {'wn': 1453, 'tow': 368100.0,}, #from GPS
244+
'ura': 2.0,
245+
'fit_interval': 4 * 60 * 60,
246+
'valid': 1,
247+
'healthy': 1,
248+
'glo': { #input data from GLO ICD, pg. 55
249+
'gamma' : 0, #not necessary for the test
250+
'tau' : 0, #not necessary for the test
251+
'pos' : [-14081752.701,18358958.252,10861302.124],
252+
'vel' : [-1025.76358,1086.72147,-3157.32343],
253+
'acc' : [1.7156e-6-9.2581e-7,1.0278e-6-1.0343e-6,-1.0368e-6-1.1260e-6],
254+
}})
255+
#set time to 6:30
256+
time = t.GpsTime(**{ 'wn': 1453, 'tow': 369000.0,})
257+
#calculate SV orbits at the time
258+
pos, vel, clock_err, clock_rate_err = eph.calc_sat_state(time)
259+
#check calculated vel and pos data at 2007-15-11T06:30:00
260+
#reference values from GLO ICD, pg 55. Note: it seems there is an typo in
261+
# result Vz in ICD example
262+
assert np.allclose(pos, [-14836563.872,19249935.476,7924017.196], 0, 2.5)
263+
assert np.allclose(vel, [-653.97782,882.62958,-3359.44444], 0, 0.005)

src/ephemeris.c

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
float decode_ura_index(const u8 index);
2626
u32 decode_fit_interval(u8 fit_interval_flag, u16 iodc);
27+
/* maximum step length in seconds for Runge-Kutta aglorithm */
28+
#define GLO_MAX_STEP_LENGTH 30
2729

2830
/** \defgroup ephemeris Ephemeris
2931
* Functions and calculations related to the GPS ephemeris.
@@ -74,6 +76,138 @@ static s8 calc_sat_state_xyz(const ephemeris_t *e, const gps_time_t *t,
7476
return 0;
7577
}
7678

79+
/** Re-calculation of ephemeris within the interval of measurement
80+
* ICD 5.1: A.3.1.2, with corrections from RTCM 3.2 p.186
81+
*
82+
* \param ydot Pointer to output array
83+
* \param pos Pointer to position input array
84+
* \param vel Pointer to velocity input array
85+
* \param acc Pointer to acceleration input array
86+
* \param e Pointer to GLO ephemeris
87+
*/
88+
static void calc_ydot(double ydot[6],
89+
const double pos[3],
90+
const double vel[3],
91+
const double acc[3])
92+
{
93+
94+
double r = sqrt(pow(pos[0], 2) +
95+
pow(pos[1], 2) +
96+
pow(pos[2], 2));
97+
98+
double m_r3 = GLO_GM / pow(r, 3);
99+
100+
double g_term = 3.0/2.0 * GLO_J02 * GLO_GM *
101+
pow(GLO_A_E, 2) / pow(r, 5);
102+
103+
double lg_term = (1.0 - 5.0 * pow(pos[2], 2) / pow(r, 2));
104+
105+
double omega_sqr = pow(GPS_OMEGAE_DOT, 2);
106+
107+
ydot[0] = vel[0];
108+
ydot[1] = vel[1];
109+
ydot[2] = vel[2];
110+
111+
ydot[3] = -m_r3 * pos[0]
112+
-g_term * pos[0] * lg_term
113+
+ omega_sqr * pos[0]
114+
+ 2.0 * GLO_OMEGAE_DOT * vel[1]
115+
+ acc[0];
116+
117+
ydot[4] = -m_r3 * pos[1]
118+
-g_term * pos[1] * lg_term
119+
+ omega_sqr * pos[1]
120+
- 2.0 * GLO_OMEGAE_DOT * vel[0]
121+
+ acc[1];
122+
123+
124+
ydot[5] = -m_r3 * pos[2]
125+
-g_term * pos[2] * (2.0 + lg_term)
126+
+ acc[2];
127+
}
128+
129+
/** Calculate satellite position, velocity and clock offset from GLO ephemeris.
130+
*
131+
* \param e Pointer to an ephemeris structure for the satellite of interest
132+
* \param t time at which to calculate the satellite state
133+
* \param pos Array into which to write calculated satellite position [m]
134+
* \param vel Array into which to write calculated satellite velocity [m/s]
135+
* \param clock_err Pointer to where to store the calculated satellite clock
136+
* error [s]
137+
* \param clock_rate_err Pointer to where to store the calculated satellite
138+
* clock error [s/s]
139+
*
140+
* \return 0 on success,
141+
* -1 if ephemeris is not valid or too old
142+
*/
143+
static s8 calc_sat_state_glo(const ephemeris_t *e, const gps_time_t *t,
144+
double pos[3], double vel[3],
145+
double *clock_err, double *clock_rate_err)
146+
{
147+
assert(e != NULL);
148+
assert(t != NULL);
149+
assert(pos != NULL);
150+
assert(vel != NULL);
151+
assert(clock_err != NULL);
152+
assert(clock_rate_err != NULL);
153+
154+
double dt = abs(gpsdifftime(t, &e->toe));
155+
156+
if (dt > 900) {
157+
log_error("GLO: Integration end point is not within 900 s of TOE");
158+
return 1;
159+
}
160+
161+
u32 num_steps = ceil(dt / GLO_MAX_STEP_LENGTH);
162+
double h = gpsdifftime(t, &e->toe) / num_steps;
163+
164+
if (num_steps) {
165+
double ydot[6], y[6];
166+
167+
calc_ydot(ydot, e->glo.pos, e->glo.vel, e->glo.acc);
168+
memcpy(&y[0], e->glo.pos, sizeof(double) * 3);
169+
memcpy(&y[3], e->glo.vel, sizeof(double) * 3);
170+
171+
/* Runge-Kutta integration algorithm */
172+
for (u8 i = 0; i < num_steps; i++) {
173+
double k1[6], k2[6], k3[6], k4[6], y_tmp[6];
174+
u8 j;
175+
176+
memcpy(k1, ydot, sizeof(k1));
177+
178+
for (j = 0; j < 6; j++)
179+
y_tmp[j] = y[j] + h/2 * k1[j];
180+
181+
calc_ydot(k2, &y_tmp[0], &y_tmp[3], e->glo.acc);
182+
183+
for (j = 0; j < 6; j++)
184+
y_tmp[j] = y[j] + h/2 * k2[j];
185+
186+
calc_ydot(k3, &y_tmp[0], &y_tmp[3], e->glo.acc);
187+
188+
for (j = 0; j < 6; j++)
189+
y_tmp[j] = y[j] + h * k3[j];
190+
191+
calc_ydot(k4, &y_tmp[0], &y_tmp[3], e->glo.acc);
192+
193+
for (j = 0; j < 6; j++)
194+
y[j] += h/6 * (k1[j] + 2 * k2[j] + 2 * k3[j] + k4[j]);
195+
196+
calc_ydot(ydot, &y[0], &y[3], e->glo.acc);
197+
}
198+
memcpy(pos, &y[0], sizeof(double) * 3);
199+
memcpy(vel, &y[3], sizeof(double) * 3);
200+
} else {
201+
memcpy(pos, e->glo.pos, sizeof(double) * 3);
202+
memcpy(vel, e->glo.vel, sizeof(double) * 3);
203+
}
204+
205+
*clock_err = e->glo.tau + e->glo.gamma * dt;
206+
*clock_rate_err = e->glo.gamma;
207+
208+
return 0;
209+
}
210+
77211
/** Calculate satellite position, velocity and clock offset from GPS ephemeris.
78212
*
79213
* References:
@@ -231,6 +365,8 @@ s8 calc_sat_state(const ephemeris_t *e, const gps_time_t *t,
231365
return calc_sat_state_kepler(e, t, pos, vel, clock_err, clock_rate_err);
232366
case CONSTELLATION_SBAS:
233367
return calc_sat_state_xyz(e, t, pos, vel, clock_err, clock_rate_err);
368+
case CONSTELLATION_GLO:
369+
return calc_sat_state_glo(e, t, pos, vel, clock_err, clock_rate_err);
234370
default:
235371
assert(!"Unsupported constellation");
236372
return -1;
@@ -666,6 +802,14 @@ static bool ephemeris_kepler_equal(const ephemeris_kepler_t *a,
666802
(a->toc.tow == b->toc.tow);
667803
}
668804

805+
static bool ephemeris_glo_equal(const ephemeris_glo_t *a,
806+
const ephemeris_glo_t *b)
807+
{
808+
return (memcmp(a->pos, b->pos, sizeof(a->pos)) == 0) &&
809+
(memcmp(a->vel, b->vel, sizeof(a->vel)) == 0) &&
810+
(memcmp(a->acc, b->acc, sizeof(a->acc)) == 0);
811+
}
812+
669813
/** Are the two ephemerides the same?
670814
*
671815
* \param a First ephemeris
@@ -688,6 +832,8 @@ bool ephemeris_equal(const ephemeris_t *a, const ephemeris_t *b)
688832
return ephemeris_kepler_equal(&a->kepler, &b->kepler);
689833
case CONSTELLATION_SBAS:
690834
return ephemeris_xyz_equal(&a->xyz, &b->xyz);
835+
case CONSTELLATION_GLO:
836+
return ephemeris_glo_equal(&a->glo, &b->glo);
691837
default:
692838
assert(!"Unsupported constellation");
693839
return false;

0 commit comments

Comments
 (0)