1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.estimation.measurements;
18
19 import java.util.Map;
20
21 import org.hipparchus.CalculusFieldElement;
22 import org.hipparchus.Field;
23 import org.hipparchus.analysis.differentiation.Gradient;
24 import org.hipparchus.geometry.euclidean.threed.FieldRotation;
25 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
26 import org.hipparchus.geometry.euclidean.threed.RotationConvention;
27 import org.hipparchus.geometry.euclidean.threed.Vector3D;
28 import org.hipparchus.util.FastMath;
29 import org.orekit.bodies.BodyShape;
30 import org.orekit.bodies.FieldGeodeticPoint;
31 import org.orekit.bodies.GeodeticPoint;
32 import org.orekit.frames.FieldStaticTransform;
33 import org.orekit.frames.FieldTransform;
34 import org.orekit.frames.Frame;
35 import org.orekit.frames.KinematicTransform;
36 import org.orekit.frames.StaticTransform;
37 import org.orekit.frames.TopocentricFrame;
38 import org.orekit.frames.TopocentricTransformProvider;
39 import org.orekit.frames.Transform;
40 import org.orekit.models.earth.displacement.StationDisplacement;
41 import org.orekit.time.AbsoluteDate;
42 import org.orekit.time.FieldAbsoluteDate;
43 import org.orekit.time.clocks.QuadraticClockModel;
44 import org.orekit.utils.AngularCoordinates;
45 import org.orekit.utils.FieldAngularCoordinates;
46 import org.orekit.utils.FieldPVCoordinates;
47 import org.orekit.utils.FieldPVCoordinatesProvider;
48 import org.orekit.utils.PVCoordinatesProvider;
49 import org.orekit.utils.ParameterDriver;
50 import org.orekit.utils.TimeStampedFieldPVCoordinates;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 public class GroundStation extends AbstractParticipant implements Observer {
72
73
74
75
76
77
78
79 private static final double POSITION_OFFSET_SCALE = FastMath.scalb(1.0, 0);
80
81
82 private final TopocentricFrame baseFrame;
83
84
85 private final ParameterDriver eastOffsetDriver;
86
87
88 private final ParameterDriver northOffsetDriver;
89
90
91 private final ParameterDriver zenithOffsetDriver;
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106 public GroundStation(final TopocentricFrame baseFrame) {
107 this(baseFrame, createEmptyQuadraticClock(baseFrame.getName()));
108 }
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124 public GroundStation(final TopocentricFrame baseFrame, final QuadraticClockModel clock) {
125 super(baseFrame.getName(), clock);
126 this.baseFrame = baseFrame;
127
128 this.eastOffsetDriver = new ParameterDriver(baseFrame.getName() + OFFSET_SUFFIX + "-East",
129 0.0, POSITION_OFFSET_SCALE,
130 Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
131
132 this.northOffsetDriver = new ParameterDriver(baseFrame.getName() + OFFSET_SUFFIX + "-North",
133 0.0, POSITION_OFFSET_SCALE,
134 Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
135
136 this.zenithOffsetDriver = new ParameterDriver(baseFrame.getName() + OFFSET_SUFFIX + "-Zenith",
137 0.0, POSITION_OFFSET_SCALE,
138 Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
139
140
141 addParameterDriver(this.eastOffsetDriver);
142 addParameterDriver(this.northOffsetDriver);
143 addParameterDriver(this.zenithOffsetDriver);
144
145 }
146
147
148 @Override
149 public final boolean isSpaceBased() {
150 return false;
151 }
152
153
154
155
156 public ParameterDriver getEastOffsetDriver() {
157 return eastOffsetDriver;
158 }
159
160
161
162
163 public ParameterDriver getNorthOffsetDriver() {
164 return northOffsetDriver;
165 }
166
167
168
169
170 public ParameterDriver getZenithOffsetDriver() {
171 return zenithOffsetDriver;
172 }
173
174
175
176
177
178
179
180
181 public TopocentricFrame getBaseFrame() {
182 return baseFrame;
183 }
184
185
186
187
188
189
190
191
192 protected Vector3D computeDisplacement(final AbsoluteDate date, final Vector3D position) {
193 return Vector3D.ZERO;
194 }
195
196
197
198
199
200
201 public GeodeticPoint getOffsetGeodeticPoint(final AbsoluteDate date) {
202
203
204 final double x = eastOffsetDriver.getValue();
205 final double y = northOffsetDriver.getValue();
206 final double z = zenithOffsetDriver.getValue();
207 final BodyShape baseShape = baseFrame.getParentShape();
208 final StaticTransform baseToBody = baseFrame.getStaticTransformTo(baseShape.getBodyFrame(), date);
209 Vector3D origin = baseToBody.transformPosition(new Vector3D(x, y, z));
210
211 if (date != null) {
212 origin = origin.add(computeDisplacement(date, origin));
213 }
214
215 return baseShape.transform(origin, baseShape.getBodyFrame(), date);
216
217 }
218
219
220
221
222
223
224
225
226 public <T extends CalculusFieldElement<T>> FieldGeodeticPoint<T> getOffsetGeodeticPoint(final FieldAbsoluteDate<T> date) {
227
228
229 final double x = eastOffsetDriver.getValue();
230 final double y = northOffsetDriver.getValue();
231 final double z = zenithOffsetDriver.getValue();
232 final BodyShape baseShape = baseFrame.getParentShape();
233 final FieldStaticTransform<T> baseToBody = baseFrame.getStaticTransformTo(baseShape.getBodyFrame(), date);
234 FieldVector3D<T> origin = baseToBody.transformPosition(new Vector3D(x, y, z));
235 origin = origin.add(computeDisplacement(date.toAbsoluteDate(), origin.toVector3D()));
236
237 return baseShape.transform(origin, baseShape.getBodyFrame(), date);
238
239 }
240
241
242 @Override
243 public PVCoordinatesProvider getPVCoordinatesProvider() {
244 final GeodeticPoint offsetPoint = getOffsetGeodeticPoint(AbsoluteDate.ARBITRARY_EPOCH);
245 return new TopocentricFrame(baseFrame.getParentShape(), offsetPoint, "offset");
246 }
247
248
249 @Override
250 public FieldPVCoordinatesProvider<Gradient> getFieldPVCoordinatesProvider(final int freeParameters,
251 final Map<String, Integer> parameterIndices) {
252 return new FieldPVCoordinatesProvider<>() {
253 @Override
254 public TimeStampedFieldPVCoordinates<Gradient> getPVCoordinates(final FieldAbsoluteDate<Gradient> date,
255 final Frame frame) {
256
257 final FieldVector3D<Gradient> origin = getOrigin(date, parameterIndices);
258
259
260 final Transform bodyToInertNonField = baseFrame.getParent().getTransformTo(frame, date.toAbsoluteDate());
261 final FieldTransform<Gradient> bodyToInert = new FieldTransform<>(date.getField(),
262 bodyToInertNonField).shiftedBy(date.durationFrom(date.toAbsoluteDate()));
263
264 final TimeStampedFieldPVCoordinates<Gradient> zeroPV = new TimeStampedFieldPVCoordinates<>(date,
265 new FieldPVCoordinates<>(origin, FieldVector3D.getZero(date.getField())));
266 return bodyToInert.transformPVCoordinates(zeroPV);
267 }
268
269 @Override
270 public FieldVector3D<Gradient> getPosition(final FieldAbsoluteDate<Gradient> date, final Frame frame) {
271
272 final FieldVector3D<Gradient> origin = getOrigin(date, parameterIndices);
273
274
275 final KinematicTransform bodyToInertNonField = baseFrame.getParent().getKinematicTransformTo(frame,
276 date.toAbsoluteDate());
277 final FieldStaticTransform<Gradient> bodyToInert = shiftKinematicTransform(bodyToInertNonField,
278 date.durationFrom(date.toAbsoluteDate()));
279
280
281 return bodyToInert.getRotation().applyTo(bodyToInert.getTranslation().add(origin));
282 }
283 };
284 }
285
286
287
288
289
290
291
292 protected FieldVector3D<Gradient> getOrigin(final FieldAbsoluteDate<Gradient> date,
293 final Map<String, Integer> indices) {
294
295 final int freeParameters = date.getField().getZero().getFreeParameters();
296 final AbsoluteDate absoluteDate = date.toAbsoluteDate();
297 final Gradient x = eastOffsetDriver.getValue(freeParameters, indices, absoluteDate);
298 final Gradient y = northOffsetDriver.getValue(freeParameters, indices, absoluteDate);
299 final Gradient z = zenithOffsetDriver.getValue(freeParameters, indices, absoluteDate);
300 final FieldVector3D<Gradient> position = new FieldVector3D<>(x, y, z);
301
302 final Frame bodyFrame = baseFrame.getParentShape().getBodyFrame();
303 final KinematicTransform kinematicTopoToBody = baseFrame.getKinematicTransformTo(bodyFrame, absoluteDate);
304 final FieldStaticTransform<Gradient> staticTopoToBody = shiftKinematicTransform(kinematicTopoToBody,
305 date.durationFrom(absoluteDate));
306
307 final FieldVector3D<Gradient> originBeforeDisplacement = staticTopoToBody.transformPosition(position);
308 return originBeforeDisplacement.add(computeDisplacement(absoluteDate, originBeforeDisplacement.toVector3D()));
309 }
310
311
312
313
314
315
316
317
318 protected FieldStaticTransform<Gradient> shiftKinematicTransform(final KinematicTransform kinematicTransform,
319 final Gradient dt) {
320
321 final Field<Gradient> field = dt.getField();
322 final AbsoluteDate date = kinematicTransform.getDate();
323 final FieldVector3D<Gradient> fieldVelocity = new FieldVector3D<>(field, kinematicTransform.getVelocity());
324 final FieldVector3D<Gradient> shiftedTranslation = fieldVelocity.scalarMultiply(dt).add(kinematicTransform.getTranslation());
325
326 final FieldAngularCoordinates<Gradient> fieldAngularCoordinates = new FieldAngularCoordinates<>(field,
327 new AngularCoordinates(kinematicTransform.getRotation(), kinematicTransform.getRotationRate()));
328 final FieldVector3D<Gradient> rotationRate = fieldAngularCoordinates.getRotationRate();
329 final Gradient rate = rotationRate.getNorm();
330 final FieldRotation<Gradient> shiftedRotation = (rate.getReal() == 0.0) ?
331 fieldAngularCoordinates.getRotation() :
332 new FieldRotation<>(rotationRate, rate.multiply(dt), RotationConvention.FRAME_TRANSFORM)
333 .compose(fieldAngularCoordinates.getRotation(), RotationConvention.VECTOR_OPERATOR);
334 return FieldStaticTransform.of(new FieldAbsoluteDate<>(field, date).shiftedBy(dt), shiftedTranslation,
335 shiftedRotation);
336 }
337
338
339 @Override
340 public Transform getOffsetToInertial(final Frame inertial, final AbsoluteDate date,
341 final boolean clockOffsetAlreadyApplied) {
342
343
344 final AbsoluteDate offsetCompensatedDate = clockOffsetAlreadyApplied ?
345 date :
346 new AbsoluteDate(date, -getOffsetValue(date));
347
348 final TopocentricFrame topocentricFrame = (TopocentricFrame) getPVCoordinatesProvider();
349 return topocentricFrame.getTransformTo(inertial, offsetCompensatedDate);
350 }
351
352
353 @Override
354 public FieldTransform<Gradient> getOffsetToInertial(final Frame inertial,
355 final FieldAbsoluteDate<Gradient> offsetCompensatedDate,
356 final int freeParameters,
357 final Map<String, Integer> indices) {
358
359 final FieldVector3D<Gradient> origin = getOrigin(offsetCompensatedDate, indices);
360 final FieldGeodeticPoint<Gradient> originGP = baseFrame.getParentShape().transform(origin, baseFrame.getParent(),
361 offsetCompensatedDate);
362 final FieldStaticTransform<Gradient> staticOffsetToBody = TopocentricTransformProvider.getTransform(baseFrame.getParentShape(),
363 offsetCompensatedDate, originGP).getStaticInverse();
364 final FieldTransform<Gradient> offsetToBody = new FieldTransform<>(offsetCompensatedDate,
365 staticOffsetToBody.getTranslation(), staticOffsetToBody.getRotation());
366
367
368 final FieldTransform<Gradient> bodyToInert = baseFrame.getParent().getTransformTo(inertial, offsetCompensatedDate);
369
370
371 return new FieldTransform<>(offsetCompensatedDate, offsetToBody, bodyToInert);
372 }
373
374 }