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