1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.gnss.attitude;
18
19 import org.hipparchus.analysis.UnivariateFunction;
20 import org.hipparchus.analysis.differentiation.DSFactory;
21 import org.hipparchus.analysis.differentiation.DerivativeStructure;
22 import org.hipparchus.analysis.solvers.BracketingNthOrderBrentSolver;
23 import org.hipparchus.analysis.solvers.UnivariateSolverUtils;
24 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
25 import org.hipparchus.geometry.euclidean.threed.Vector3D;
26 import org.hipparchus.util.FastMath;
27 import org.hipparchus.util.FieldSinCos;
28 import org.hipparchus.util.SinCos;
29 import org.orekit.frames.Frame;
30 import org.orekit.frames.LOFType;
31 import org.orekit.frames.Transform;
32 import org.orekit.time.AbsoluteDate;
33 import org.orekit.time.FieldAbsoluteDate;
34 import org.orekit.time.TimeStamped;
35 import org.orekit.utils.ExtendedPVCoordinatesProvider;
36 import org.orekit.utils.FieldPVCoordinates;
37 import org.orekit.utils.PVCoordinates;
38 import org.orekit.utils.PVCoordinatesProvider;
39 import org.orekit.utils.TimeStampedAngularCoordinates;
40 import org.orekit.utils.TimeStampedPVCoordinates;
41
42
43
44
45
46
47
48
49
50
51
52
53
54 class GNSSAttitudeContext implements TimeStamped {
55
56
57 private static final int ORDER = 2;
58
59
60 private static final DSFactory FACTORY = new DSFactory(1, ORDER);
61
62
63 private static final PVCoordinates PLUS_Y_PV =
64 new PVCoordinates(Vector3D.PLUS_J, Vector3D.ZERO, Vector3D.ZERO);
65
66
67 private static final PVCoordinates MINUS_Z_PV =
68 new PVCoordinates(Vector3D.MINUS_K, Vector3D.ZERO, Vector3D.ZERO);
69
70
71 private static final double BETA_SIGN_CHANGE_PROTECTION = FastMath.toRadians(0.07);
72
73
74 private final AbsoluteDate date;
75
76
77 private final FieldAbsoluteDate<DerivativeStructure> dateDS;
78
79
80 private final ExtendedPVCoordinatesProvider sun;
81
82
83 private final PVCoordinatesProvider pvProv;
84
85
86 private final Frame inertialFrame;
87
88
89 private final DerivativeStructure svbCos;
90
91
92 private final boolean morning;
93
94
95 private final DerivativeStructure delta;
96
97
98 private double muRate;
99
100
101 private double cNight;
102
103
104 private double cNoon;
105
106
107 private TurnSpan turnSpan;
108
109
110
111
112
113
114
115
116
117 GNSSAttitudeContext(final AbsoluteDate date,
118 final ExtendedPVCoordinatesProvider sun, final PVCoordinatesProvider pvProv,
119 final Frame inertialFrame, final TurnSpan turnSpan) {
120
121 this.date = date;
122 this.dateDS = addDerivatives(date);
123 this.sun = sun;
124 this.pvProv = pvProv;
125 this.inertialFrame = inertialFrame;
126 final FieldPVCoordinates<DerivativeStructure> sunPVDS = sun.getPVCoordinates(dateDS, inertialFrame);
127
128 final TimeStampedPVCoordinates svPV = pvProv.getPVCoordinates(date, inertialFrame);
129 final FieldPVCoordinates<DerivativeStructure> svPVDS = svPV.toDerivativeStructurePV(FACTORY.getCompiler().getOrder());
130 this.svbCos = FieldVector3D.dotProduct(sunPVDS.getPosition(), svPVDS.getPosition()).
131 divide(sunPVDS.getPosition().getNorm().multiply(svPVDS.getPosition().getNorm()));
132 this.morning = Vector3D.dotProduct(svPV.getVelocity(), sunPVDS.getPosition().toVector3D()) >= 0.0;
133
134 this.muRate = svPV.getAngularVelocity().getNorm();
135
136 this.turnSpan = turnSpan;
137
138 final DerivativeStructure absDelta;
139 if (svbCos.getValue() <= 0) {
140
141 absDelta = inOrbitPlaneAbsoluteAngle(svbCos.acos().negate().add(FastMath.PI));
142 } else {
143
144 absDelta = inOrbitPlaneAbsoluteAngle(svbCos.acos());
145 }
146 delta = FastMath.copySign(absDelta, -absDelta.getPartialDerivative(1));
147
148 }
149
150
151
152
153
154 private AbsoluteDate removeDerivatives(final FieldAbsoluteDate<DerivativeStructure> d) {
155 return d.toAbsoluteDate();
156 }
157
158
159
160
161
162 private FieldAbsoluteDate<DerivativeStructure> addDerivatives(final AbsoluteDate d) {
163 return new FieldAbsoluteDate<>(FACTORY.getDerivativeField(), d).
164 shiftedBy(FACTORY.variable(0, 0.0));
165 }
166
167
168
169
170
171 public TimeStampedAngularCoordinates nominalYaw(final AbsoluteDate d) {
172 final PVCoordinates svPV = pvProv.getPVCoordinates(d, inertialFrame);
173 return new TimeStampedAngularCoordinates(d,
174 svPV.normalize(),
175 PVCoordinates.crossProduct(sun.getPVCoordinates(d, inertialFrame), svPV).normalize(),
176 MINUS_Z_PV,
177 PLUS_Y_PV,
178 1.0e-9);
179 }
180
181
182
183
184
185 public double beta(final AbsoluteDate d) {
186 final TimeStampedPVCoordinates svPV = pvProv.getPVCoordinates(d, inertialFrame);
187 return 0.5 * FastMath.PI - Vector3D.angle(sun.getPVCoordinates(d, inertialFrame).getPosition(), svPV.getMomentum());
188 }
189
190
191
192
193
194 private DerivativeStructure betaDS(final FieldAbsoluteDate<DerivativeStructure> d) {
195 final TimeStampedPVCoordinates svPV = pvProv.getPVCoordinates(removeDerivatives(d), inertialFrame);
196 final FieldPVCoordinates<DerivativeStructure> svPVDS = svPV.toDerivativeStructurePV(FACTORY.getCompiler().getOrder());
197 return FieldVector3D.angle(sun.getPVCoordinates(d, inertialFrame).getPosition(), svPVDS.getMomentum()).
198 negate().
199 add(0.5 * FastMath.PI);
200 }
201
202
203
204
205 public DerivativeStructure betaDS() {
206 return betaDS(dateDS);
207 }
208
209
210 @Override
211 public AbsoluteDate getDate() {
212 return date;
213 }
214
215
216
217
218 public TurnSpan getTurnSpan() {
219 return turnSpan;
220 }
221
222
223
224
225 public double getSVBcos() {
226 return svbCos.getValue();
227 }
228
229
230
231
232
233
234
235
236
237
238
239 public double getSecuredBeta() {
240 final double beta = beta(getDate());
241 return FastMath.abs(beta) < BETA_SIGN_CHANGE_PROTECTION ?
242 beta(turnSpan.getTurnStartDate()) :
243 beta;
244 }
245
246
247
248
249
250
251 public boolean linearModelStillActive(final double linearPhi, final double phiDot) {
252 final double dt0 = turnSpan.getTurnEndDate().durationFrom(date);
253 final UnivariateFunction yawReached = dt -> {
254 final AbsoluteDate t = date.shiftedBy(dt);
255 final Vector3D pSun = sun.getPVCoordinates(t, inertialFrame).getPosition();
256 final PVCoordinates pv = pvProv.getPVCoordinates(t, inertialFrame);
257 final Vector3D pSat = pv.getPosition();
258 final Vector3D targetX = Vector3D.crossProduct(pSat, Vector3D.crossProduct(pSun, pSat)).normalize();
259
260 final double phi = linearPhi + dt * phiDot;
261 final SinCos sc = FastMath.sinCos(phi);
262 final Vector3D pU = pv.getPosition().normalize();
263 final Vector3D mU = pv.getMomentum().normalize();
264 final Vector3D omega = new Vector3D(-phiDot, pU);
265 final Vector3D currentX = new Vector3D(-sc.sin(), mU, -sc.cos(), Vector3D.crossProduct(pU, mU));
266 final Vector3D currentXDot = Vector3D.crossProduct(omega, currentX);
267
268 return Vector3D.dotProduct(targetX, currentXDot);
269 };
270 final double fullTurn = 2 * FastMath.PI / FastMath.abs(phiDot);
271 final double dtMin = FastMath.min(turnSpan.getTurnStartDate().durationFrom(date), dt0 - 60.0);
272 final double dtMax = FastMath.max(dtMin + fullTurn, dt0 + 60.0);
273 double[] bracket = UnivariateSolverUtils.bracket(yawReached, dt0,
274 dtMin, dtMax, fullTurn / 100, 1.0, 100);
275 if (yawReached.value(bracket[0]) <= 0.0) {
276
277 bracket = UnivariateSolverUtils.bracket(yawReached, 0.5 * (bracket[0] + bracket[1] + fullTurn),
278 bracket[1], bracket[1] + fullTurn, fullTurn / 100, 1.0, 100);
279 }
280 final double dt = new BracketingNthOrderBrentSolver(1.0e-3, 5).
281 solve(100, yawReached, bracket[0], bracket[1]);
282 turnSpan.updateEnd(date.shiftedBy(dt), date);
283
284 return dt > 0.0;
285
286 }
287
288
289
290
291
292
293 public boolean setUpTurnRegion(final double cosNight, final double cosNoon) {
294 this.cNight = cosNight;
295 this.cNoon = cosNoon;
296
297 if (svbCos.getValue() < cNight || svbCos.getValue() > cNoon) {
298
299 return true;
300 } else {
301
302
303 return inTurnTimeRange();
304 }
305
306 }
307
308
309
310
311 public DerivativeStructure getDeltaDS() {
312 return delta;
313 }
314
315
316
317
318 public double getOrbitAngleSinceMidnight() {
319 final double absAngle = inOrbitPlaneAbsoluteAngle(FastMath.PI - FastMath.acos(svbCos.getValue()));
320 return morning ? absAngle : -absAngle;
321 }
322
323
324
325
326 public boolean inSunSide() {
327 return svbCos.getValue() > 0;
328 }
329
330
331
332
333
334
335
336 public double getYawStart(final double sunBeta) {
337 final double halfSpan = 0.5 * turnSpan.getTurnDuration() * muRate;
338 return computePhi(sunBeta, FastMath.copySign(halfSpan, svbCos.getValue()));
339 }
340
341
342
343
344
345
346
347 public double getYawEnd(final double sunBeta) {
348 final double halfSpan = 0.5 * turnSpan.getTurnDuration() * muRate;
349 return computePhi(sunBeta, FastMath.copySign(halfSpan, -svbCos.getValue()));
350 }
351
352
353
354
355
356
357
358 public double yawRate(final double sunBeta) {
359 return (getYawEnd(sunBeta) - getYawStart(sunBeta)) / turnSpan.getTurnDuration();
360 }
361
362
363
364
365 public double getMuRate() {
366 return muRate;
367 }
368
369
370
371
372
373
374
375
376
377
378 private DerivativeStructure inOrbitPlaneAbsoluteAngle(final DerivativeStructure angle) {
379 return FastMath.acos(FastMath.cos(angle).divide(FastMath.cos(beta(getDate()))));
380 }
381
382
383
384
385
386
387
388
389
390
391
392 public double inOrbitPlaneAbsoluteAngle(final double angle) {
393 return FastMath.acos(FastMath.cos(angle) / FastMath.cos(beta(getDate())));
394 }
395
396
397
398
399
400
401
402
403
404 public double computePhi(final double sunBeta, final double inOrbitPlaneAngle) {
405 return FastMath.atan2(-FastMath.tan(sunBeta), FastMath.sin(inOrbitPlaneAngle));
406 }
407
408
409
410
411
412 public void setHalfSpan(final double halfSpan, final double endMargin) {
413
414 final AbsoluteDate start = date.shiftedBy((delta.getValue() - halfSpan) / muRate);
415 final AbsoluteDate end = date.shiftedBy((delta.getValue() + halfSpan) / muRate);
416 final AbsoluteDate estimationDate = getDate();
417
418 if (turnSpan == null) {
419 turnSpan = new TurnSpan(start, end, estimationDate, endMargin);
420 } else {
421 turnSpan.updateStart(start, estimationDate);
422 turnSpan.updateEnd(end, estimationDate);
423 }
424 }
425
426
427
428
429 public boolean inTurnTimeRange() {
430 return turnSpan != null && turnSpan.inTurnTimeRange(getDate());
431 }
432
433
434
435
436 public double timeSinceTurnStart() {
437 return getDate().durationFrom(turnSpan.getTurnStartDate());
438 }
439
440
441
442
443
444
445 public TimeStampedAngularCoordinates turnCorrectedAttitude(final double yaw, final double yawDot) {
446 return turnCorrectedAttitude(FACTORY.build(yaw, yawDot, 0.0));
447 }
448
449
450
451
452
453 public TimeStampedAngularCoordinates turnCorrectedAttitude(final DerivativeStructure yaw) {
454
455
456 final PVCoordinates svPV = pvProv.getPVCoordinates(date, inertialFrame);
457 final Vector3D p = svPV.getPosition();
458 final Vector3D v = svPV.getVelocity();
459 final Vector3D a = svPV.getAcceleration();
460 final double r2 = p.getNormSq();
461 final double r = FastMath.sqrt(r2);
462 final Vector3D keplerianJerk = new Vector3D(-3 * Vector3D.dotProduct(p, v) / r2, a, -a.getNorm() / r, v);
463 final PVCoordinates velocity = new PVCoordinates(v, a, keplerianJerk);
464 final PVCoordinates momentum = PVCoordinates.crossProduct(svPV, velocity);
465
466 final FieldSinCos<DerivativeStructure> sc = FastMath.sinCos(yaw);
467 final DerivativeStructure c = sc.cos().negate();
468 final DerivativeStructure s = sc.sin().negate();
469 final Vector3D m0 = new Vector3D(s.getValue(), c.getValue(), 0.0);
470 final Vector3D m1 = new Vector3D(s.getPartialDerivative(1), c.getPartialDerivative(1), 0.0);
471 final Vector3D m2 = new Vector3D(s.getPartialDerivative(2), c.getPartialDerivative(2), 0.0);
472 return new TimeStampedAngularCoordinates(date,
473 svPV.normalize(), momentum.normalize(),
474 MINUS_Z_PV, new PVCoordinates(m0, m1, m2),
475 1.0e-9);
476
477 }
478
479
480
481
482 public TimeStampedAngularCoordinates orbitNormalYaw() {
483 final Transform t = LOFType.LVLH_CCSDS.transformFromInertial(date, pvProv.getPVCoordinates(date, inertialFrame));
484 return new TimeStampedAngularCoordinates(date,
485 t.getRotation(),
486 t.getRotationRate(),
487 t.getRotationAcceleration());
488 }
489
490 }