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