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