1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.propagation.analytical.gnss;
18
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.List;
22
23 import org.hipparchus.analysis.differentiation.Gradient;
24 import org.hipparchus.analysis.differentiation.GradientField;
25 import org.hipparchus.analysis.differentiation.UnivariateDerivative2;
26 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
27 import org.hipparchus.geometry.euclidean.threed.Vector3D;
28 import org.hipparchus.linear.MatrixUtils;
29 import org.hipparchus.linear.QRDecomposition;
30 import org.hipparchus.linear.RealMatrix;
31 import org.hipparchus.linear.RealVector;
32 import org.hipparchus.util.FastMath;
33 import org.hipparchus.util.FieldSinCos;
34 import org.hipparchus.util.SinCos;
35 import org.orekit.attitudes.Attitude;
36 import org.orekit.attitudes.AttitudeProvider;
37 import org.orekit.attitudes.FrameAlignedProvider;
38 import org.orekit.frames.Frame;
39 import org.orekit.orbits.FieldKeplerianAnomalyUtility;
40 import org.orekit.orbits.FieldKeplerianOrbit;
41 import org.orekit.orbits.FieldKeplerianParameters;
42 import org.orekit.orbits.KeplerianAnomalyUtility;
43 import org.orekit.orbits.KeplerianOrbit;
44 import org.orekit.orbits.Orbit;
45 import org.orekit.orbits.PositionAngleType;
46 import org.orekit.propagation.AbstractMatricesHarvester;
47 import org.orekit.propagation.Propagator;
48 import org.orekit.propagation.SpacecraftState;
49 import org.orekit.propagation.analytical.AbstractAnalyticalPropagator;
50 import org.orekit.propagation.analytical.gnss.data.FieldGnssOrbitalElements;
51 import org.orekit.propagation.analytical.gnss.data.GNSSOrbitalElements;
52 import org.orekit.propagation.analytical.gnss.data.GNSSOrbitalElementsFactory;
53 import org.orekit.propagation.analytical.gnss.data.NonKeplerianDriversFactory;
54 import org.orekit.time.AbsoluteDate;
55 import org.orekit.time.FieldAbsoluteDate;
56 import org.orekit.time.GNSSDate;
57 import org.orekit.utils.DoubleArrayDictionary;
58 import org.orekit.utils.FieldPVCoordinates;
59 import org.orekit.utils.PVCoordinates;
60 import org.orekit.utils.ParameterDriver;
61 import org.orekit.utils.ParameterDriversProvider;
62 import org.orekit.utils.TimeSpanMap.Span;
63
64
65
66
67
68
69
70
71
72 public class GNSSPropagator<O extends GNSSOrbitalElements<O>>
73 extends AbstractAnalyticalPropagator implements ParameterDriversProvider {
74
75
76
77
78 private static final int MAX_ITER = 100;
79
80
81
82
83 private static final double TOL_P = 1.0e-6;
84
85
86
87
88 private static final double TOL_V = 1.0e-9;
89
90
91
92
93 private static final int FREE_PARAMETERS = 6;
94
95
96
97
98 private static final double EPS = 1.0e-12;
99
100
101 private O orbitalElements;
102
103
104
105
106 private final NonKeplerianDriversFactory driversFactory;
107
108
109 private final Frame eci;
110
111
112 private final Frame ecef;
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135 public GNSSPropagator(final GNSSOrbitalElementsFactory<O> factory) {
136 this(factory.createFromDrivers(), factory.getInertial(), factory.getBodyFixed(),
137 FrameAlignedProvider.of(factory.getInertial()),
138 Propagator.DEFAULT_MASS);
139 }
140
141
142
143
144
145
146
147
148 public GNSSPropagator(final O orbitalElements, final Frame eci, final Frame ecef,
149 final AttitudeProvider provider, final double mass) {
150 super(provider);
151
152 this.orbitalElements = orbitalElements;
153 this.driversFactory = new NonKeplerianDriversFactory();
154 driversFactory.reset(orbitalElements);
155
156 this.eci = eci;
157
158 this.ecef = ecef;
159
160
161 final Orbit orbit = propagateOrbit(orbitalElements.getDate());
162 final Attitude attitude = provider.getAttitude(orbit, orbit.getDate(), orbit.getFrame());
163
164
165 super.resetInitialState(new SpacecraftState(orbit, attitude).withMass(mass));
166
167 }
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183 public GNSSPropagator(final SpacecraftState initialState, final O nonKeplerianElements,
184 final Frame ecef, final AttitudeProvider provider, final double mass) {
185 this(buildOrbitalElements(initialState, nonKeplerianElements, new NonKeplerianDriversFactory(),
186 ecef, provider, mass),
187 initialState.getFrame(), ecef, provider, initialState.getMass());
188 }
189
190
191 @Override
192 public List<ParameterDriver> getParametersDrivers() {
193 return driversFactory.getParametersDrivers();
194 }
195
196
197
198
199 public NonKeplerianDriversFactory getDriversFactory() {
200 return driversFactory;
201 }
202
203
204
205
206
207
208 public Frame getECI() {
209 return eci;
210 }
211
212
213
214
215
216
217
218 public Frame getECEF() {
219 return ecef;
220 }
221
222
223
224
225
226
227 public double getMU() {
228 return orbitalElements.getOrbit().getMu();
229 }
230
231
232
233
234
235 public O getOrbitalElements() {
236 return orbitalElements;
237 }
238
239
240
241
242 @Override
243 protected AbstractMatricesHarvester createHarvester(final String stmName, final RealMatrix initialStm,
244 final DoubleArrayDictionary initialJacobianColumns) {
245
246
247 final GnssHarvester<O> harvester = new GnssHarvester<>(this, stmName, initialStm, initialJacobianColumns);
248
249
250 addAdditionalDataProvider(harvester);
251
252
253 return harvester;
254
255 }
256
257
258
259
260 @Override
261 protected List<String> getJacobiansColumnsNames() {
262 final List<String> columnsNames = new ArrayList<>();
263 for (final ParameterDriver driver : getParametersDrivers()) {
264 if (driver.isSelected() && !columnsNames.contains(driver.getNamesSpanMap().getFirstSpan().getData())) {
265
266
267 for (Span<String> span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {
268 columnsNames.add(span.getData());
269 }
270 }
271 }
272 Collections.sort(columnsNames);
273 return columnsNames;
274 }
275
276
277 @Override
278 public Orbit propagateOrbit(final AbsoluteDate date) {
279
280 final PVCoordinates pvaInECEF = propagateInEcef(date);
281
282 final PVCoordinates pvaInECI = ecef.getTransformTo(eci, date).transformPVCoordinates(pvaInECEF);
283
284 return new KeplerianOrbit(pvaInECI, eci, date, getMU());
285 }
286
287
288
289
290
291
292
293
294
295
296 public PVCoordinates propagateInEcef(final AbsoluteDate date) {
297
298 final KeplerianOrbit orbit = orbitalElements.getOrbit();
299
300
301 final UnivariateDerivative2 tk = new UnivariateDerivative2(getTk(date), 1.0, 0.0);
302
303 final UnivariateDerivative2 ak = tk.multiply(orbitalElements.getADot()).add(orbit.getA());
304
305 final UnivariateDerivative2 nA = tk.multiply(orbitalElements.getDeltaN0Dot() * 0.5).
306 add(orbitalElements.getDeltaN0()).
307 add(orbit.getKeplerianMeanMotion());
308
309 final UnivariateDerivative2 mk = tk.multiply(nA).add(orbit.getMeanAnomaly());
310
311 final UnivariateDerivative2 e = tk.newInstance(orbit.getE());
312 final UnivariateDerivative2 ek = FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(e, mk);
313
314 final UnivariateDerivative2 vk = FieldKeplerianAnomalyUtility.ellipticEccentricToTrue(e, ek);
315
316 final UnivariateDerivative2 phik = vk.add(orbit.getPeriapsisArgument());
317 final FieldSinCos<UnivariateDerivative2> cs2phi = FastMath.sinCos(phik.multiply(2));
318
319 final UnivariateDerivative2 dphik = cs2phi.cos().multiply(orbitalElements.getCuc()).add(cs2phi.sin().multiply(orbitalElements.getCus()));
320
321 final UnivariateDerivative2 drk = cs2phi.cos().multiply(orbitalElements.getCrc()).add(cs2phi.sin().multiply(orbitalElements.getCrs()));
322
323 final UnivariateDerivative2 dik = cs2phi.cos().multiply(orbitalElements.getCic()).add(cs2phi.sin().multiply(orbitalElements.getCis()));
324
325 final FieldSinCos<UnivariateDerivative2> csuk = FastMath.sinCos(phik.add(dphik));
326
327 final UnivariateDerivative2 rk = ek.cos().multiply(e.negate()).add(1).multiply(ak).add(drk);
328
329 final UnivariateDerivative2 ik = tk.multiply(orbitalElements.getIDot()).add(orbit.getI()).add(dik);
330 final FieldSinCos<UnivariateDerivative2> csik = FastMath.sinCos(ik);
331
332 final UnivariateDerivative2 xk = csuk.cos().multiply(rk);
333 final UnivariateDerivative2 yk = csuk.sin().multiply(rk);
334
335 final double thetaDot = orbitalElements.getAngularVelocity();
336 final double toe = orbitalElements.getTimeOfEphemeris().getSecondsInWeek();
337 final FieldSinCos<UnivariateDerivative2> csomk =
338 FastMath.sinCos(tk.multiply(orbitalElements.getOmegaDot() - thetaDot).
339 add(orbit.getRightAscensionOfAscendingNode() - thetaDot * toe));
340
341 final FieldVector3D<UnivariateDerivative2> positionWithDerivatives =
342 new FieldVector3D<>(xk.multiply(csomk.cos()).subtract(yk.multiply(csomk.sin()).multiply(csik.cos())),
343 xk.multiply(csomk.sin()).add(yk.multiply(csomk.cos()).multiply(csik.cos())),
344 yk.multiply(csik.sin()));
345 return new PVCoordinates(positionWithDerivatives);
346 }
347
348
349
350
351
352
353
354 private double getTk(final AbsoluteDate date) {
355 final double cycleDuration = orbitalElements.getCycleDuration();
356
357 double tk = date.durationFrom(orbitalElements.getDate());
358
359 while (tk > 0.5 * cycleDuration) {
360 tk -= cycleDuration;
361 }
362 while (tk < -0.5 * cycleDuration) {
363 tk += cycleDuration;
364 }
365
366 return tk;
367 }
368
369
370 @Override
371 public Frame getFrame() {
372 return eci;
373 }
374
375
376 @Override
377 protected double getMass(final AbsoluteDate date) {
378 return getInitialState().getMass();
379 }
380
381
382 @Override
383 public void resetInitialState(final SpacecraftState state) {
384 orbitalElements = buildOrbitalElements(state, orbitalElements, driversFactory,
385 ecef, getAttitudeProvider(), state.getMass());
386 final Orbit orbit = propagateOrbit(orbitalElements.getDate());
387 final Attitude attitude = getAttitudeProvider().getAttitude(orbit, orbit.getDate(), orbit.getFrame());
388 super.resetInitialState(new SpacecraftState(orbit, attitude).withMass(state.getMass()));
389 }
390
391
392 @Override
393 protected void resetIntermediateState(final SpacecraftState state, final boolean forward) {
394 resetInitialState(state);
395 }
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414 public static <O extends GNSSOrbitalElements<O>>
415 O buildOrbitalElements(final SpacecraftState initialState,
416 final O nonKeplerianElements,
417 final NonKeplerianDriversFactory driversFactory,
418 final Frame ecef, final AttitudeProvider provider,
419 final double mass) {
420
421
422 final Frame frozenEcef = ecef.getFrozenFrame(initialState.getFrame(), initialState.getDate(),
423 GNSSOrbitalElementsFactory.FROZEN + ecef.getName());
424 final KeplerianOrbit orbit = approximateInitialOrbit(initialState, nonKeplerianElements, frozenEcef);
425 driversFactory.reset(nonKeplerianElements);
426
427
428 final PVCoordinates targetPV = initialState.getPVCoordinates(frozenEcef);
429 FieldGnssOrbitalElements<Gradient, O> gElements = toGradient(nonKeplerianElements, orbit, driversFactory);
430 for (int i = 0; i < MAX_ITER; ++i) {
431
432
433 final FieldGnssPropagator<Gradient, O> gPropagator =
434 new FieldGnssPropagator<>(gElements, frozenEcef, ecef, provider,
435 gElements.getTgd().newInstance(mass));
436 final FieldPVCoordinates<Gradient> gPV = gPropagator.getInitialState().getPVCoordinates();
437
438
439 final RealMatrix jacobian = MatrixUtils.createRealMatrix(FREE_PARAMETERS, FREE_PARAMETERS);
440 jacobian.setRow(0, gPV.getPosition().getX().getGradient());
441 jacobian.setRow(1, gPV.getPosition().getY().getGradient());
442 jacobian.setRow(2, gPV.getPosition().getZ().getGradient());
443 jacobian.setRow(3, gPV.getVelocity().getX().getGradient());
444 jacobian.setRow(4, gPV.getVelocity().getY().getGradient());
445 jacobian.setRow(5, gPV.getVelocity().getZ().getGradient());
446
447
448 final RealVector residuals = MatrixUtils.createRealVector(FREE_PARAMETERS);
449 residuals.setEntry(0, targetPV.getPosition().getX() - gPV.getPosition().getX().getValue());
450 residuals.setEntry(1, targetPV.getPosition().getY() - gPV.getPosition().getY().getValue());
451 residuals.setEntry(2, targetPV.getPosition().getZ() - gPV.getPosition().getZ().getValue());
452 residuals.setEntry(3, targetPV.getVelocity().getX() - gPV.getVelocity().getX().getValue());
453 residuals.setEntry(4, targetPV.getVelocity().getY() - gPV.getVelocity().getY().getValue());
454 residuals.setEntry(5, targetPV.getVelocity().getZ() - gPV.getVelocity().getZ().getValue());
455
456
457 final double deltaP = FastMath.sqrt(residuals.getEntry(0) * residuals.getEntry(0) +
458 residuals.getEntry(1) * residuals.getEntry(1) +
459 residuals.getEntry(2) * residuals.getEntry(2));
460 final double deltaV = FastMath.sqrt(residuals.getEntry(3) * residuals.getEntry(3) +
461 residuals.getEntry(4) * residuals.getEntry(4) +
462 residuals.getEntry(5) * residuals.getEntry(5));
463 if (deltaP < TOL_P && deltaV < TOL_V) {
464 break;
465 }
466
467
468 final RealVector correction = new QRDecomposition(jacobian, EPS).getSolver().solve(residuals);
469
470
471 final FieldKeplerianOrbit<Gradient> previous = gElements.getOrbit();
472 Gradient updatedA;
473 Gradient updatedE;
474 double factor = 2;
475 do {
476
477 factor *= 0.5;
478 updatedA = previous.getA().add(correction.getEntry(0) * factor);
479 updatedE = previous.getE().add(correction.getEntry(1) * factor);
480 } while (updatedA.getValue() < 0 || updatedE.getValue() < 0 || updatedE.getValue() >= 1);
481
482
483 final FieldKeplerianOrbit<Gradient> updated =
484 new FieldKeplerianOrbit<>(new FieldKeplerianParameters<>(updatedA,
485 updatedE,
486 previous.getI().add(correction.getEntry(2) * factor),
487 previous.getPeriapsisArgument().add(correction.getEntry(3) * factor),
488 previous.getRightAscensionOfAscendingNode().add(correction.getEntry(4) * factor),
489 previous.getMeanAnomaly().add(correction.getEntry(5) * factor),
490 PositionAngleType.MEAN),
491 previous.getFrame(), previous.getDate(), previous.getMu());
492 gElements = toGradient(nonKeplerianElements, updated.toOrbit(), driversFactory);
493
494 }
495
496 return gElements.toNonField();
497
498 }
499
500
501
502
503
504
505
506
507 private static KeplerianOrbit approximateInitialOrbit(final SpacecraftState initialState,
508 final GNSSOrbitalElements<?> nonKeplerianElements,
509 final Frame frozenEcef) {
510
511
512
513 final PVCoordinates pv = initialState.getPVCoordinates(frozenEcef);
514 final Vector3D p = pv.getPosition();
515 final Vector3D v = pv.getVelocity();
516
517
518 final double rk = p.getNorm();
519
520
521 final Vector3D normal = pv.getMomentum().normalize();
522 final double cosIk = normal.getZ();
523 final double ik = Vector3D.angle(normal, Vector3D.PLUS_K);
524
525
526 final double q = FastMath.hypot(normal.getX(), normal.getY());
527 final double cos = -normal.getY() / q;
528 final double sin = normal.getX() / q;
529 final double xk = p.getX() * cos + p.getY() * sin;
530 final double yk = (p.getY() * cos - p.getX() * sin) / cosIk;
531
532
533 final double uk = FastMath.atan2(yk, xk);
534
535
536 double phi = uk;
537 for (int i = 0; i < MAX_ITER; ++i) {
538 final double previous = phi;
539 final SinCos cs2Phi = FastMath.sinCos(2 * phi);
540 phi = uk - (cs2Phi.cos() * nonKeplerianElements.getCuc() + cs2Phi.sin() * nonKeplerianElements.getCus());
541 if (FastMath.abs(phi - previous) <= EPS) {
542 break;
543 }
544 }
545 final SinCos cs2phi = FastMath.sinCos(2 * phi);
546
547
548
549 final double i0 = ik - (cs2phi.cos() * nonKeplerianElements.getCic() + cs2phi.sin() * nonKeplerianElements.getCis());
550 final double om0 = FastMath.atan2(sin, cos) +
551 nonKeplerianElements.getAngularVelocity() *
552 nonKeplerianElements.getTimeOfEphemeris().getSecondsInWeek();
553
554
555 final double mu = initialState.getOrbit().getMu();
556 final double rV2OMu = rk * v.getNorm2Sq() / mu;
557 final double sma = rk / (2 - rV2OMu);
558 final double eCosE = rV2OMu - 1;
559 final double eSinE = Vector3D.dotProduct(p, v) / FastMath.sqrt(mu * sma);
560 final double e = FastMath.hypot(eCosE, eSinE);
561 final double eccentricAnomaly = FastMath.atan2(eSinE, eCosE);
562 final double aop = phi - eccentricAnomaly;
563 final double meanAnomaly = KeplerianAnomalyUtility.ellipticEccentricToMean(e, eccentricAnomaly);
564
565 return new KeplerianOrbit(sma, e, i0, aop, om0, meanAnomaly, PositionAngleType.MEAN, frozenEcef,
566 initialState.getDate(), mu);
567
568 }
569
570
571
572
573
574
575
576
577
578 private static <O extends GNSSOrbitalElements<O>>
579 FieldGnssOrbitalElements<Gradient, O> toGradient(final O elements,
580 final KeplerianOrbit orbit,
581 final NonKeplerianDriversFactory driversFactory) {
582
583
584 final Gradient aG = Gradient.variable(FREE_PARAMETERS, 0, orbit.getA());
585 final Gradient eG = Gradient.variable(FREE_PARAMETERS, 1, orbit.getE());
586 final Gradient iG = Gradient.variable(FREE_PARAMETERS, 2, orbit.getI());
587 final Gradient paG = Gradient.variable(FREE_PARAMETERS, 3, orbit.getPeriapsisArgument());
588 final Gradient raanG = Gradient.variable(FREE_PARAMETERS, 4, orbit.getRightAscensionOfAscendingNode());
589 final Gradient mG = Gradient.variable(FREE_PARAMETERS, 5, orbit.getMeanAnomaly());
590 final FieldKeplerianOrbit<Gradient> orbitG =
591 new FieldKeplerianOrbit<>(new FieldKeplerianParameters<>(aG, eG, iG, paG, raanG, mG,
592 PositionAngleType.MEAN),
593 orbit.getFrame(),
594 new FieldAbsoluteDate<>(GradientField.getField(FREE_PARAMETERS),
595 orbit.getDate()),
596 Gradient.constant(FREE_PARAMETERS, orbit.getMu()));
597
598
599 return elements.toField(orbitG,
600 driversFactory.toGradients(FREE_PARAMETERS),
601 d -> Gradient.constant(FREE_PARAMETERS, d));
602
603 }
604
605 }