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.differentiation.DerivativeStructure;
20 import org.hipparchus.geometry.euclidean.threed.FieldRotation;
21 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
22 import org.hipparchus.geometry.euclidean.threed.RotationConvention;
23 import org.hipparchus.geometry.euclidean.threed.Vector3D;
24 import org.hipparchus.util.FastMath;
25 import org.orekit.errors.OrekitException;
26 import org.orekit.frames.LOFType;
27 import org.orekit.frames.Transform;
28 import org.orekit.time.AbsoluteDate;
29 import org.orekit.time.TimeStamped;
30 import org.orekit.utils.FieldPVCoordinates;
31 import org.orekit.utils.PVCoordinates;
32 import org.orekit.utils.TimeStampedAngularCoordinates;
33 import org.orekit.utils.TimeStampedPVCoordinates;
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 class GNSSAttitudeContext implements TimeStamped {
51
52
53 private static final PVCoordinates PLUS_Y =
54 new PVCoordinates(Vector3D.PLUS_J, Vector3D.ZERO, Vector3D.ZERO);
55
56
57 private static final PVCoordinates MINUS_Z =
58 new PVCoordinates(Vector3D.MINUS_K, Vector3D.ZERO, Vector3D.ZERO);
59
60
61 private static final double BETA_SIGN_CHANGE_PROTECTION = FastMath.toRadians(0.07);
62
63
64 private static final int ORDER = 2;
65
66
67 private final TimeStampedPVCoordinates svPV;
68
69
70 private final FieldPVCoordinates<DerivativeStructure> svPVDS;
71
72
73 private final DerivativeStructure beta;
74
75
76 private final DerivativeStructure svbCos;
77
78
79 private final TimeStampedAngularCoordinates nominalYaw;
80
81
82 private final FieldRotation<DerivativeStructure> nominalYawDS;
83
84
85 private double muRate;
86
87
88 private double cNight;
89
90
91 private double cNoon;
92
93
94 private DerivativeStructure delta;
95
96
97 private double halfSpan;
98
99
100 private AbsoluteDate turnStart;
101
102
103 private AbsoluteDate turnEnd;
104
105
106
107
108
109
110 GNSSAttitudeContext(final TimeStampedPVCoordinates sunPV, final TimeStampedPVCoordinates svPV)
111 throws OrekitException {
112
113 final FieldPVCoordinates<DerivativeStructure> sunPVDS = sunPV.toDerivativeStructurePV(ORDER);
114 this.svPV = svPV;
115 this.svPVDS = svPV.toDerivativeStructurePV(ORDER);
116 this.svbCos = FieldVector3D.dotProduct(sunPVDS.getPosition(), svPVDS.getPosition()).
117 divide(sunPVDS.getPosition().getNorm().
118 multiply(svPVDS.getPosition().getNorm()));
119 this.beta = FieldVector3D.angle(sunPVDS.getPosition(), svPVDS.getMomentum()).
120 negate().
121 add(0.5 * FastMath.PI);
122
123
124 this.nominalYaw =
125 new TimeStampedAngularCoordinates(svPV.getDate(),
126 svPV.normalize(),
127 PVCoordinates.crossProduct(sunPV, svPV).normalize(),
128 MINUS_Z,
129 PLUS_Y,
130 1.0e-9);
131 this.nominalYawDS = nominalYaw.toDerivativeStructureRotation(ORDER);
132
133 this.muRate = svPV.getAngularVelocity().getNorm();
134
135 }
136
137
138 @Override
139 public AbsoluteDate getDate() {
140 return svPV.getDate();
141 }
142
143
144
145
146 public double getSVBcos() {
147 return svbCos.getValue();
148 }
149
150
151
152
153
154
155 public double getBeta() {
156 return beta.getValue();
157 }
158
159
160
161
162
163
164 public DerivativeStructure getBetaDS() {
165 return beta;
166 }
167
168
169
170
171
172
173
174
175
176
177
178 public double getSecuredBeta() {
179 return FastMath.abs(beta.getReal()) < BETA_SIGN_CHANGE_PROTECTION ?
180 beta.taylor(-timeSinceTurnStart(getDate())) :
181 getBeta();
182 }
183
184
185
186
187 public TimeStampedAngularCoordinates getNominalYaw() {
188 return nominalYaw;
189 }
190
191
192
193
194 public double yawAngle() {
195 final Vector3D xSat = nominalYaw.getRotation().revert().applyTo(Vector3D.PLUS_I);
196 return FastMath.copySign(Vector3D.angle(svPV.getVelocity(), xSat), -beta.getReal());
197 }
198
199
200
201
202 public DerivativeStructure yawAngleDS() {
203 final FieldVector3D<DerivativeStructure> xSat = nominalYawDS.revert().applyTo(Vector3D.PLUS_I);
204 return FastMath.copySign(FieldVector3D.angle(svPV.getVelocity(), xSat), -beta.getReal());
205 }
206
207
208
209
210
211
212 public boolean setUpTurnRegion(final double cosNight, final double cosNoon) {
213 this.cNight = cosNight;
214 this.cNoon = cosNoon;
215 if (svbCos.getValue() < cNight) {
216
217 final DerivativeStructure absDelta = inOrbitPlaneAbsoluteAngle(svbCos.acos().negate().add(FastMath.PI));
218 delta = FastMath.copySign(absDelta, -absDelta.getPartialDerivative(1));
219 return true;
220 } else if (svbCos.getValue() > cNoon) {
221
222 final DerivativeStructure absDelta = inOrbitPlaneAbsoluteAngle(svbCos.acos());
223 delta = FastMath.copySign(absDelta, -absDelta.getPartialDerivative(1));
224 return true;
225 } else {
226 return false;
227 }
228 }
229
230
231
232
233 public double getDelta() {
234 return delta.getValue();
235 }
236
237
238
239
240 public DerivativeStructure getDeltaDS() {
241 return delta;
242 }
243
244
245
246
247 public boolean inSunSide() {
248 return svbCos.getValue() > 0;
249 }
250
251
252
253
254
255
256
257 public double getYawStart(final double sunBeta) {
258 return computePhi(sunBeta, FastMath.copySign(halfSpan, svbCos.getValue()));
259 }
260
261
262
263
264
265
266
267 public double getYawEnd(final double sunBeta) {
268 return computePhi(sunBeta, FastMath.copySign(halfSpan, -svbCos.getValue()));
269 }
270
271
272
273
274
275
276
277 public double yawRate(final double sunBeta) {
278 return (getYawEnd(sunBeta) - getYawStart(sunBeta)) / getTurnDuration();
279 }
280
281
282
283
284 public double getMuRate() {
285 return muRate;
286 }
287
288
289
290
291
292
293
294
295
296
297 private DerivativeStructure inOrbitPlaneAbsoluteAngle(final DerivativeStructure angle) {
298 return FastMath.acos(FastMath.cos(angle).divide(FastMath.cos(beta)));
299 }
300
301
302
303
304
305
306
307
308
309
310
311 public double inOrbitPlaneAbsoluteAngle(final double angle) {
312 return FastMath.acos(FastMath.cos(angle) / FastMath.cos(beta.getReal()));
313 }
314
315
316
317
318
319
320
321
322
323 public double computePhi(final double sunBeta, final double inOrbitPlaneAngle) {
324 return FastMath.atan2(-FastMath.tan(sunBeta), FastMath.sin(inOrbitPlaneAngle));
325 }
326
327
328
329
330 public void setHalfSpan(final double halfSpan) {
331 this.halfSpan = halfSpan;
332 this.turnStart = svPV.getDate().shiftedBy((delta.getValue() - halfSpan) / muRate);
333 this.turnEnd = svPV.getDate().shiftedBy((delta.getValue() + halfSpan) / muRate);
334 }
335
336
337
338
339
340
341 public boolean inTurnTimeRange(final AbsoluteDate date, final double endMargin) {
342 return date.durationFrom(turnStart) > 0 &&
343 date.durationFrom(turnEnd) < endMargin;
344 }
345
346
347
348
349 public double getTurnDuration() {
350 return 2 * halfSpan / muRate;
351 }
352
353
354
355
356
357 public double timeSinceTurnStart(final AbsoluteDate date) {
358 return date.durationFrom(turnStart);
359 }
360
361
362
363
364
365
366 public TimeStampedAngularCoordinates turnCorrectedAttitude(final double yaw, final double yawDot) {
367 return turnCorrectedAttitude(beta.getFactory().build(yaw, yawDot, 0.0));
368 }
369
370
371
372
373
374 public TimeStampedAngularCoordinates turnCorrectedAttitude(final DerivativeStructure yaw) {
375
376
377 final DerivativeStructure nominalAngle = yawAngleDS();
378 final TimeStampedAngularCoordinates correction =
379 new TimeStampedAngularCoordinates(nominalYaw.getDate(),
380 new FieldRotation<>(FieldVector3D.getPlusK(nominalAngle.getField()),
381 yaw.subtract(nominalAngle),
382 RotationConvention.VECTOR_OPERATOR));
383
384
385 return correction.addOffset(getNominalYaw());
386
387 }
388
389
390
391
392 public TimeStampedAngularCoordinates orbitNormalYaw() {
393 final Transform t = LOFType.VVLH.transformFromInertial(svPV.getDate(), svPV);
394 return new TimeStampedAngularCoordinates(svPV.getDate(),
395 t.getRotation(),
396 t.getRotationRate(),
397 t.getRotationAcceleration());
398 }
399
400 }