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.Field;
20 import org.hipparchus.CalculusFieldElement;
21 import org.hipparchus.util.FastMath;
22 import org.orekit.frames.Frame;
23 import org.orekit.time.AbsoluteDate;
24 import org.orekit.utils.ExtendedPositionProvider;
25 import org.orekit.utils.TimeStampedAngularCoordinates;
26 import org.orekit.utils.TimeStampedFieldAngularCoordinates;
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 public class GPSBlockIIA extends AbstractGNSSAttitudeProvider {
42
43
44 public static final double DEFAULT_YAW_BIAS = FastMath.toRadians(0.5);
45
46
47 private static final double NIGHT_TURN_LIMIT = FastMath.toRadians(180.0 - 13.25);
48
49
50 private static final double END_MARGIN = 1800.0;
51
52
53 private static final double[] DEFAULT_YAW_RATES = new double[] {
54 Double.NaN,
55 FastMath.toRadians(0.1211), FastMath.toRadians(0.1339), FastMath.toRadians(0.1230), FastMath.toRadians(0.1233),
56 FastMath.toRadians(0.1180), FastMath.toRadians(0.1266), FastMath.toRadians(0.1269), FastMath.toRadians(0.1033),
57 FastMath.toRadians(0.1278), FastMath.toRadians(0.0978), FastMath.toRadians(0.2000), FastMath.toRadians(0.1990),
58 FastMath.toRadians(0.2000), FastMath.toRadians(0.0815), FastMath.toRadians(0.1303), FastMath.toRadians(0.0838),
59 FastMath.toRadians(0.1401), FastMath.toRadians(0.1069), FastMath.toRadians(0.0980), FastMath.toRadians(0.1030),
60 FastMath.toRadians(0.1366), FastMath.toRadians(0.1025), FastMath.toRadians(0.1140), FastMath.toRadians(0.1089),
61 FastMath.toRadians(0.1001), FastMath.toRadians(0.1227), FastMath.toRadians(0.1194), FastMath.toRadians(0.1260),
62 FastMath.toRadians(0.1228), FastMath.toRadians(0.1165), FastMath.toRadians(0.0969), FastMath.toRadians(0.1140)
63 };
64
65
66 private final double yawRate;
67
68
69 private final double yawBias;
70
71
72
73
74
75
76
77
78
79
80 public GPSBlockIIA(final double yawRate, final double yawBias,
81 final AbsoluteDate validityStart, final AbsoluteDate validityEnd,
82 final ExtendedPositionProvider sun, final Frame inertialFrame) {
83 super(validityStart, validityEnd, sun, inertialFrame);
84 this.yawRate = yawRate;
85 this.yawBias = yawBias;
86 }
87
88
89
90
91
92
93 public static double getDefaultYawRate(final int prnNumber) {
94 return DEFAULT_YAW_RATES[prnNumber];
95 }
96
97
98 @Override
99 protected TimeStampedAngularCoordinates correctedYaw(final GNSSAttitudeContext context) {
100
101
102 final double aNoon = FastMath.atan(context.getMuRate() / yawRate);
103 final double aNight = NIGHT_TURN_LIMIT;
104 final double cNoon = FastMath.cos(aNoon);
105 final double cNight = FastMath.cos(aNight);
106
107 if (context.setUpTurnRegion(cNight, cNoon)) {
108
109 final double absBeta = FastMath.abs(context.beta(context.getDate()));
110 context.setHalfSpan(context.inSunSide() ?
111 absBeta * FastMath.sqrt(aNoon / absBeta - 1.0) :
112 context.inOrbitPlaneAbsoluteAngle(aNight - FastMath.PI),
113 END_MARGIN);
114 if (context.inTurnTimeRange()) {
115
116
117 final double beta = context.getSecuredBeta();
118 final double phiStart = context.getYawStart(beta);
119 final double dtStart = context.timeSinceTurnStart();
120 final double linearPhi;
121 final double phiDot;
122 if (context.inSunSide()) {
123
124 if (beta > 0 && beta < yawBias) {
125
126
127 phiDot = FastMath.copySign(yawRate, beta);
128 linearPhi = phiStart + phiDot * dtStart;
129 } else {
130
131 phiDot = -FastMath.copySign(yawRate, beta);
132 linearPhi = phiStart + phiDot * dtStart;
133 }
134 } else {
135
136 phiDot = yawRate;
137 linearPhi = phiStart + phiDot * dtStart;
138 }
139
140 if (context.linearModelStillActive(linearPhi, phiDot)) {
141
142 return context.turnCorrectedAttitude(linearPhi, phiDot);
143 }
144
145 }
146
147 }
148
149
150 return context.nominalYaw(context.getDate());
151
152 }
153
154
155 @Override
156 protected <T extends CalculusFieldElement<T>> TimeStampedFieldAngularCoordinates<T> correctedYaw(final GNSSFieldAttitudeContext<T> context) {
157
158 final Field<T> field = context.getDate().getField();
159
160
161 final T aNoon = FastMath.atan(context.getMuRate().divide(yawRate));
162 final T aNight = field.getZero().newInstance(NIGHT_TURN_LIMIT);
163 final double cNoon = FastMath.cos(aNoon.getReal());
164 final double cNight = FastMath.cos(aNight.getReal());
165
166 if (context.setUpTurnRegion(cNight, cNoon)) {
167
168 final T absBeta = FastMath.abs(context.beta(context.getDate()));
169 context.setHalfSpan(context.inSunSide() ?
170 absBeta.multiply(FastMath.sqrt(aNoon.divide(absBeta).subtract(1.0))) :
171 context.inOrbitPlaneAbsoluteAngle(aNight.subtract(aNoon.getPi())),
172 END_MARGIN);
173 if (context.inTurnTimeRange()) {
174
175
176 final T beta = context.getSecuredBeta();
177 final T phiStart = context.getYawStart(beta);
178 final T dtStart = context.timeSinceTurnStart();
179 final T linearPhi;
180 final T phiDot;
181 if (context.inSunSide()) {
182
183 if (beta.getReal() > 0 && beta.getReal() < yawBias) {
184
185
186 phiDot = field.getZero().newInstance(FastMath.copySign(yawRate, beta.getReal()));
187 linearPhi = phiStart.add(phiDot.multiply(dtStart));
188 } else {
189
190 phiDot = field.getZero().newInstance(-FastMath.copySign(yawRate, beta.getReal()));
191 linearPhi = phiStart.add(phiDot.multiply(dtStart));
192 }
193 } else {
194
195 phiDot = field.getZero().newInstance(yawRate);
196 linearPhi = phiStart.add(phiDot.multiply(dtStart));
197 }
198
199 if (context.linearModelStillActive(linearPhi, phiDot)) {
200
201 return context.turnCorrectedAttitude(linearPhi, phiDot);
202 }
203
204 }
205
206 }
207
208
209 return context.nominalYaw(context.getDate());
210
211 }
212
213 }