1   /* Copyright 2002-2022 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.estimation.measurements;
18  
19  import java.io.ByteArrayInputStream;
20  import java.io.ByteArrayOutputStream;
21  import java.io.IOException;
22  import java.io.ObjectInputStream;
23  import java.io.ObjectOutputStream;
24  import java.util.Arrays;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Locale;
28  import java.util.Map;
29  
30  import org.hipparchus.analysis.UnivariateVectorFunction;
31  import org.hipparchus.analysis.differentiation.FiniteDifferencesDifferentiator;
32  import org.hipparchus.analysis.differentiation.Gradient;
33  import org.hipparchus.analysis.differentiation.GradientField;
34  import org.hipparchus.analysis.differentiation.UnivariateDifferentiableVectorFunction;
35  import org.hipparchus.geometry.euclidean.threed.Rotation;
36  import org.hipparchus.geometry.euclidean.threed.Vector3D;
37  import org.hipparchus.linear.RealMatrix;
38  import org.hipparchus.optim.nonlinear.vector.leastsquares.LevenbergMarquardtOptimizer;
39  import org.hipparchus.random.RandomGenerator;
40  import org.hipparchus.random.Well19937a;
41  import org.hipparchus.util.FastMath;
42  import org.hipparchus.util.Precision;
43  import org.junit.Assert;
44  import org.junit.Test;
45  import org.orekit.Utils;
46  import org.orekit.bodies.BodyShape;
47  import org.orekit.bodies.GeodeticPoint;
48  import org.orekit.bodies.OneAxisEllipsoid;
49  import org.orekit.errors.OrekitException;
50  import org.orekit.errors.OrekitMessages;
51  import org.orekit.estimation.Context;
52  import org.orekit.estimation.EstimationTestUtils;
53  import org.orekit.estimation.leastsquares.BatchLSEstimator;
54  import org.orekit.frames.FieldTransform;
55  import org.orekit.frames.Frame;
56  import org.orekit.frames.FramesFactory;
57  import org.orekit.frames.TopocentricFrame;
58  import org.orekit.frames.Transform;
59  import org.orekit.orbits.OrbitType;
60  import org.orekit.orbits.PositionAngle;
61  import org.orekit.propagation.Propagator;
62  import org.orekit.propagation.conversion.NumericalPropagatorBuilder;
63  import org.orekit.time.AbsoluteDate;
64  import org.orekit.utils.Constants;
65  import org.orekit.utils.FieldPVCoordinates;
66  import org.orekit.utils.IERSConventions;
67  import org.orekit.utils.PVCoordinates;
68  import org.orekit.utils.ParameterDriver;
69  
70  public class GroundStationTest {
71  
72      @Test
73      public void testEstimateClockOffset() throws IOException, ClassNotFoundException {
74  
75          Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
76  
77          final NumericalPropagatorBuilder propagatorBuilder =
78                          context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
79                                                1.0e-6, 60.0, 0.001);
80  
81          // create perfect range measurements
82          final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
83                                                                             propagatorBuilder);
84          final List<ObservedMeasurement<?>> measurements =
85                          EstimationTestUtils.createMeasurements(propagator,
86                                                                 new RangeMeasurementCreator(context),
87                                                                 1.0, 3.0, 300.0);
88  
89          // change one station clock
90          final TopocentricFrame base  = context.stations.get(0).getBaseFrame();
91          final BodyShape parent       = base.getParentShape();
92          final double deltaClock      = 0.00084532;
93          final String changedSuffix   = "-changed";
94          final GroundStation changed  = new GroundStation(new TopocentricFrame(parent, base.getPoint(),
95                                                                                base.getName() + changedSuffix),
96                                                           context.ut1.getEOPHistory(),
97                                                           context.stations.get(0).getDisplacements());
98  
99          // create orbit estimator
100         final BatchLSEstimator estimator = new BatchLSEstimator(new LevenbergMarquardtOptimizer(),
101                                                                 propagatorBuilder);
102         for (final ObservedMeasurement<?> measurement : measurements) {
103             final Range range = (Range) measurement;
104             final String name = range.getStation().getBaseFrame().getName() + changedSuffix;
105                 if (changed.getBaseFrame().getName().equals(name)) {
106                     estimator.addMeasurement(new Range(changed, range.isTwoWay(),
107                                                        range.getDate().shiftedBy(deltaClock),
108                                                        range.getObservedValue()[0],
109                                                        range.getTheoreticalStandardDeviation()[0],
110                                                        range.getBaseWeight()[0],
111                                                        range.getSatellites().get(0)));
112                 } else {
113                     estimator.addMeasurement(range);
114                 }
115         }
116         estimator.setParametersConvergenceThreshold(1.0e-3);
117         estimator.setMaxIterations(100);
118         estimator.setMaxEvaluations(200);
119 
120         // we want to estimate station clock offset
121         changed.getClockOffsetDriver().setSelected(true);
122         changed.getEastOffsetDriver().setSelected(false);
123         changed.getNorthOffsetDriver().setSelected(false);
124         changed.getZenithOffsetDriver().setSelected(false);
125 
126         EstimationTestUtils.checkFit(context, estimator, 2, 3,
127                                      0.0, 6.7e-7,
128                                      0.0, 1.8e-6,
129                                      0.0, 1.3e-7,
130                                      0.0, 5.9e-11);
131         Assert.assertEquals(deltaClock, changed.getClockOffsetDriver().getValue(), 8.2e-11);
132 
133         RealMatrix normalizedCovariances = estimator.getOptimum().getCovariances(1.0e-10);
134         RealMatrix physicalCovariances   = estimator.getPhysicalCovariances(1.0e-10);
135         Assert.assertEquals(7,        normalizedCovariances.getRowDimension());
136         Assert.assertEquals(7,        normalizedCovariances.getColumnDimension());
137         Assert.assertEquals(7,        physicalCovariances.getRowDimension());
138         Assert.assertEquals(7,        physicalCovariances.getColumnDimension());
139         Assert.assertEquals(4.185e-9, physicalCovariances.getEntry(6, 6), 3.0e-13);
140 
141     }
142 
143     @Test
144     public void testEstimateStationPosition() throws IOException, ClassNotFoundException {
145 
146         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
147 
148         final NumericalPropagatorBuilder propagatorBuilder =
149                         context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
150                                               1.0e-6, 60.0, 0.001);
151 
152         // create perfect range measurements
153         final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
154                                                                            propagatorBuilder);
155         final List<ObservedMeasurement<?>> measurements =
156                         EstimationTestUtils.createMeasurements(propagator,
157                                                                new RangeMeasurementCreator(context),
158                                                                1.0, 3.0, 300.0);
159 
160         // move one station
161         final RandomGenerator random = new Well19937a(0x4adbecfc743bda60l);
162         final TopocentricFrame base = context.stations.get(0).getBaseFrame();
163         final BodyShape parent = base.getParentShape();
164         final Vector3D baseOrigin = parent.transform(base.getPoint());
165         final Vector3D deltaTopo = new Vector3D(2 * random.nextDouble() - 1,
166                                                 2 * random.nextDouble() - 1,
167                                                 2 * random.nextDouble() - 1);
168         final Transform topoToParent = base.getTransformTo(parent.getBodyFrame(), (AbsoluteDate) null);
169         final Vector3D deltaParent   = topoToParent.transformVector(deltaTopo);
170         final String movedSuffix     = "-moved";
171         final GroundStation moved = new GroundStation(new TopocentricFrame(parent,
172                                                                            parent.transform(baseOrigin.subtract(deltaParent),
173                                                                                             parent.getBodyFrame(),
174                                                                                             null),
175                                                                            base.getName() + movedSuffix),
176                                                       context.ut1.getEOPHistory(),
177                                                       context.stations.get(0).getDisplacements());
178 
179         // create orbit estimator
180         final BatchLSEstimator estimator = new BatchLSEstimator(new LevenbergMarquardtOptimizer(),
181                                                                 propagatorBuilder);
182         for (final ObservedMeasurement<?> measurement : measurements) {
183             final Range range = (Range) measurement;
184             final String name = range.getStation().getBaseFrame().getName() + movedSuffix;
185                 if (moved.getBaseFrame().getName().equals(name)) {
186                     estimator.addMeasurement(new Range(moved, range.isTwoWay(), range.getDate(),
187                                                        range.getObservedValue()[0],
188                                                        range.getTheoreticalStandardDeviation()[0],
189                                                        range.getBaseWeight()[0],
190                                                        range.getSatellites().get(0)));
191                 } else {
192                     estimator.addMeasurement(range);
193                 }
194         }
195         estimator.setParametersConvergenceThreshold(1.0e-3);
196         estimator.setMaxIterations(100);
197         estimator.setMaxEvaluations(200);
198 
199         // we want to estimate station offsets
200         moved.getClockOffsetDriver().setSelected(false);
201         moved.getEastOffsetDriver().setSelected(true);
202         moved.getNorthOffsetDriver().setSelected(true);
203         moved.getZenithOffsetDriver().setSelected(true);
204 
205         EstimationTestUtils.checkFit(context, estimator, 2, 3,
206                                      0.0, 5.8e-7,
207                                      0.0, 1.8e-6,
208                                      0.0, 4.8e-7,
209                                      0.0, 2.6e-10);
210         Assert.assertEquals(deltaTopo.getX(), moved.getEastOffsetDriver().getValue(),   4.5e-7);
211         Assert.assertEquals(deltaTopo.getY(), moved.getNorthOffsetDriver().getValue(),  6.2e-7);
212         Assert.assertEquals(deltaTopo.getZ(), moved.getZenithOffsetDriver().getValue(), 2.6e-7);
213 
214         GeodeticPoint result = moved.getOffsetGeodeticPoint(null);
215 
216         GeodeticPoint reference = context.stations.get(0).getBaseFrame().getPoint();
217         Assert.assertEquals(reference.getLatitude(),  result.getLatitude(),  1.4e-14);
218         Assert.assertEquals(reference.getLongitude(), result.getLongitude(), 2.9e-14);
219         Assert.assertEquals(reference.getAltitude(),  result.getAltitude(),  2.6e-7);
220 
221         RealMatrix normalizedCovariances = estimator.getOptimum().getCovariances(1.0e-10);
222         RealMatrix physicalCovariances   = estimator.getPhysicalCovariances(1.0e-10);
223         Assert.assertEquals(9,       normalizedCovariances.getRowDimension());
224         Assert.assertEquals(9,       normalizedCovariances.getColumnDimension());
225         Assert.assertEquals(9,       physicalCovariances.getRowDimension());
226         Assert.assertEquals(9,       physicalCovariances.getColumnDimension());
227         Assert.assertEquals(0.55431, physicalCovariances.getEntry(6, 6), 1.0e-5);
228         Assert.assertEquals(0.22694, physicalCovariances.getEntry(7, 7), 1.0e-5);
229         Assert.assertEquals(0.13106, physicalCovariances.getEntry(8, 8), 1.0e-5);
230 
231         ByteArrayOutputStream bos = new ByteArrayOutputStream();
232         ObjectOutputStream    oos = new ObjectOutputStream(bos);
233         oos.writeObject(moved.getEstimatedEarthFrame().getTransformProvider());
234 
235         Assert.assertTrue(bos.size() > 155000);
236         Assert.assertTrue(bos.size() < 160000);
237 
238         ByteArrayInputStream  bis = new ByteArrayInputStream(bos.toByteArray());
239         ObjectInputStream     ois = new ObjectInputStream(bis);
240         EstimatedEarthFrameProvider deserialized  = (EstimatedEarthFrameProvider) ois.readObject();
241         Assert.assertEquals(moved.getPrimeMeridianOffsetDriver().getValue(),
242                             deserialized.getPrimeMeridianOffsetDriver().getValue(),
243                             1.0e-15);
244         Assert.assertEquals(moved.getPrimeMeridianDriftDriver().getValue(),
245                             deserialized.getPrimeMeridianDriftDriver().getValue(),
246                             1.0e-15);
247         Assert.assertEquals(moved.getPolarOffsetXDriver().getValue(),
248                             deserialized.getPolarOffsetXDriver().getValue(),
249                             1.0e-15);
250         Assert.assertEquals(moved.getPolarDriftXDriver().getValue(),
251                             deserialized.getPolarDriftXDriver().getValue(),
252                             1.0e-15);
253         Assert.assertEquals(moved.getPolarOffsetYDriver().getValue(),
254                             deserialized.getPolarOffsetYDriver().getValue(),
255                             1.0e-15);
256         Assert.assertEquals(moved.getPolarDriftYDriver().getValue(),
257                             deserialized.getPolarDriftYDriver().getValue(),
258                             1.0e-15);
259 
260     }
261 
262     @Test
263     public void testEstimateEOP() {
264 
265         Context linearEOPContext = EstimationTestUtils.eccentricContext("linear-EOP:regular-data/de431-ephemerides:potential:tides");
266 
267         final AbsoluteDate refDate = new AbsoluteDate(2000, 2, 24, linearEOPContext.utc);
268         final double dut10 = 0.3079738;
269         final double lod   = 0.0011000;
270         final double xp0   = 68450.0e-6;
271         final double xpDot =   -50.0e-6;
272         final double yp0   =    60.0e-6;
273         final double ypDot =     2.0e-6;
274         for (double dt = -2 * Constants.JULIAN_DAY; dt < 2 * Constants.JULIAN_DAY; dt += 300.0) {
275             AbsoluteDate date = refDate.shiftedBy(dt);
276             Assert.assertEquals(dut10 - dt * lod / Constants.JULIAN_DAY,
277                                 linearEOPContext.ut1.getEOPHistory().getUT1MinusUTC(date),
278                                 1.0e-15);
279             Assert.assertEquals(lod,
280                                 linearEOPContext.ut1.getEOPHistory().getLOD(date),
281                                 1.0e-15);
282             Assert.assertEquals((xp0 + xpDot * dt / Constants.JULIAN_DAY) * Constants.ARC_SECONDS_TO_RADIANS,
283                                 linearEOPContext.ut1.getEOPHistory().getPoleCorrection(date).getXp(),
284                                 1.0e-15);
285             Assert.assertEquals((yp0 + ypDot * dt / Constants.JULIAN_DAY) * Constants.ARC_SECONDS_TO_RADIANS,
286                                 linearEOPContext.ut1.getEOPHistory().getPoleCorrection(date).getYp(),
287                                 1.0e-15);
288         }
289         final NumericalPropagatorBuilder linearPropagatorBuilder =
290                         linearEOPContext.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
291                                               1.0e-6, 60.0, 0.001);
292 
293         // create perfect range measurements
294         final Propagator propagator = EstimationTestUtils.createPropagator(linearEOPContext.initialOrbit,
295                                                                            linearPropagatorBuilder);
296         final List<ObservedMeasurement<?>> linearMeasurements =
297                         EstimationTestUtils.createMeasurements(propagator,
298                                                                new RangeMeasurementCreator(linearEOPContext),
299                                                                1.0, 5.0, 60.0);
300 
301         Utils.clearFactories();
302         Context zeroEOPContext = EstimationTestUtils.eccentricContext("zero-EOP:regular-data/de431-ephemerides:potential:potential:tides");
303         for (double dt = -2 * Constants.JULIAN_DAY; dt < 2 * Constants.JULIAN_DAY; dt += 300.0) {
304             AbsoluteDate date = refDate.shiftedBy(dt);
305             Assert.assertEquals(0.0,
306                                 zeroEOPContext.ut1.getEOPHistory().getUT1MinusUTC(date),
307                                 1.0e-15);
308             Assert.assertEquals(0.0,
309                                 zeroEOPContext.ut1.getEOPHistory().getLOD(date),
310                                 1.0e-15);
311             Assert.assertEquals(0.0,
312                                 zeroEOPContext.ut1.getEOPHistory().getPoleCorrection(date).getXp(),
313                                 1.0e-15);
314             Assert.assertEquals(0.0,
315                                 zeroEOPContext.ut1.getEOPHistory().getPoleCorrection(date).getYp(),
316                                 1.0e-15);
317         }
318 
319         // create orbit estimator
320         final NumericalPropagatorBuilder zeroPropagatorBuilder =
321                         linearEOPContext.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
322                                               1.0e-6, 60.0, 0.001);
323         final BatchLSEstimator estimator = new BatchLSEstimator(new LevenbergMarquardtOptimizer(),
324                                                                 zeroPropagatorBuilder);
325         for (final ObservedMeasurement<?> linearMeasurement : linearMeasurements) {
326             Range linearRange = (Range) linearMeasurement;
327             for (final GroundStation station : zeroEOPContext.stations) {
328                 if (station.getBaseFrame().getName().equals(linearRange.getStation().getBaseFrame().getName())) {
329                     Range zeroRange = new Range(station, linearRange.isTwoWay(),
330                                                 linearRange.getDate(),
331                                                 linearRange.getObservedValue()[0],
332                                                 linearRange.getTheoreticalStandardDeviation()[0],
333                                                 linearRange.getBaseWeight()[0],
334                                                 linearRange.getSatellites().get(0));
335                     estimator.addMeasurement(zeroRange);
336                 }
337             }
338         }
339         estimator.setParametersConvergenceThreshold(1.0e-3);
340         estimator.setMaxIterations(100);
341         estimator.setMaxEvaluations(200);
342 
343         // we want to estimate pole and prime meridian
344         GroundStation station = zeroEOPContext.stations.get(0);
345         station.getPrimeMeridianOffsetDriver().setReferenceDate(refDate);
346         station.getPrimeMeridianOffsetDriver().setSelected(true);
347         station.getPrimeMeridianDriftDriver().setSelected(true);
348         station.getPolarOffsetXDriver().setReferenceDate(refDate);
349         station.getPolarOffsetXDriver().setSelected(true);
350         station.getPolarDriftXDriver().setSelected(true);
351         station.getPolarOffsetYDriver().setReferenceDate(refDate);
352         station.getPolarOffsetYDriver().setSelected(true);
353         station.getPolarDriftYDriver().setSelected(true);
354 
355         // just for the fun and to speed up test, we will use orbit determination, *without* estimating orbit
356         for (final ParameterDriver driver : zeroPropagatorBuilder.getOrbitalParametersDrivers().getDrivers()) {
357             driver.setSelected(false);
358         }
359 
360         estimator.estimate();
361 
362         final double computedDut1  = station.getPrimeMeridianOffsetDriver().getValue() / EstimatedEarthFrameProvider.EARTH_ANGULAR_VELOCITY;
363         final double computedLOD   = station.getPrimeMeridianDriftDriver().getValue() * (-Constants.JULIAN_DAY / EstimatedEarthFrameProvider.EARTH_ANGULAR_VELOCITY);
364         final double computedXp    = station.getPolarOffsetXDriver().getValue() / Constants.ARC_SECONDS_TO_RADIANS;
365         final double computedXpDot = station.getPolarDriftXDriver().getValue()  / Constants.ARC_SECONDS_TO_RADIANS * Constants.JULIAN_DAY;
366         final double computedYp    = station.getPolarOffsetYDriver().getValue() / Constants.ARC_SECONDS_TO_RADIANS;
367         final double computedYpDot = station.getPolarDriftYDriver().getValue()  / Constants.ARC_SECONDS_TO_RADIANS * Constants.JULIAN_DAY;
368         Assert.assertEquals(0.0, FastMath.abs(dut10 - computedDut1),  4.3e-10);
369         Assert.assertEquals(0.0, FastMath.abs(lod - computedLOD),     4.9e-10);
370         Assert.assertEquals(0.0, FastMath.abs(xp0 - computedXp),      5.7e-9);
371         Assert.assertEquals(0.0, FastMath.abs(xpDot - computedXpDot), 7.3e-9);
372         Assert.assertEquals(0.0, FastMath.abs(yp0 - computedYp),      1.1e-9);
373         Assert.assertEquals(0.0, FastMath.abs(ypDot - computedYpDot), 6.2e-11);
374 
375         // thresholds to use if orbit is estimated
376         // (i.e. when commenting out the loop above that sets orbital parameters drivers to "not selected")
377 //         Assert.assertEquals(dut10, computedDut1,  6.6e-3);
378 //         Assert.assertEquals(lod,   computedLOD,   1.1e-9);
379 //         Assert.assertEquals(xp0,   computedXp,    3.3e-8);
380 //         Assert.assertEquals(xpDot, computedXpDot, 2.2e-8);
381 //         Assert.assertEquals(yp0,   computedYp,    3.3e-8);
382 //         Assert.assertEquals(ypDot, computedYpDot, 3.8e-8);
383 
384     }
385 
386     @Test
387     public void testClockOffsetCartesianDerivativesOctantPxPyPz() {
388         double relativeTolerancePositionValue      =  2.3e-15;
389         double relativeTolerancePositionDerivative =  2.5e-10;
390         double relativeToleranceVelocityValue      =  3.0e-15;
391         double relativeToleranceVelocityDerivative =  1.7e-10;
392         doTestCartesianDerivatives(FastMath.toRadians(35), FastMath.toRadians(20), 1200.0, 100.0,
393                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
394                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
395                                    ".*-clock");
396     }
397 
398     @Test
399     public void testClockOffsetAngularDerivativesOctantPxPyPz() {
400         double toleranceRotationValue              =  1.9e-15;
401         double toleranceRotationDerivative         =  4.9e-15;
402         double toleranceRotationRateValue          =  1.1e-19;
403         double toleranceRotationRateDerivative     =  6.3e-19;
404         doTestAngularDerivatives(FastMath.toRadians(35), FastMath.toRadians(20), 1200.0, 100.0,
405                                  toleranceRotationValue,     toleranceRotationDerivative,
406                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
407                                  ".*-clock");
408     }
409 
410     @Test
411     public void testClockOffsetCartesianDerivativesOctantPxPyMz() {
412         double relativeTolerancePositionValue      =  1.4e-15;
413         double relativeTolerancePositionDerivative =  1.7e-10;
414         double relativeToleranceVelocityValue      =  2.5e-15;
415         double relativeToleranceVelocityDerivative =  1.8e-10;
416         doTestCartesianDerivatives(FastMath.toRadians(-35), FastMath.toRadians(20), 1200.0, 100.0,
417                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
418                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
419                                    ".*-clock");
420     }
421 
422     @Test
423     public void testClockOffsetAngularDerivativesOctantPxPyMz() {
424         double toleranceRotationValue              =  1.7e-15;
425         double toleranceRotationDerivative         =  5.0e-15;
426         double toleranceRotationRateValue          =  1.1e-19;
427         double toleranceRotationRateDerivative     =  6.3e-19;
428         doTestAngularDerivatives(FastMath.toRadians(-35), FastMath.toRadians(20), 1200.0, 100.0,
429                                  toleranceRotationValue,     toleranceRotationDerivative,
430                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
431                                  ".*-clock");
432     }
433 
434     @Test
435     public void testClockOffsetCartesianDerivativesOctantPxMyPz() {
436         double relativeTolerancePositionValue      =  1.7e-15;
437         double relativeTolerancePositionDerivative =  2.6e-10;
438         double relativeToleranceVelocityValue      =  2.8e-15;
439         double relativeToleranceVelocityDerivative =  1.8e-10;
440         doTestCartesianDerivatives(FastMath.toRadians(35), FastMath.toRadians(-20), 1200.0, 100.0,
441                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
442                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
443                                    ".*-clock");
444     }
445 
446     @Test
447     public void testClockOffsetAngularDerivativesOctantPxMyPz() {
448         double toleranceRotationValue              =  1.6e-15;
449         double toleranceRotationDerivative         =  4.9e-15;
450         double toleranceRotationRateValue          =  1.1e-19;
451         double toleranceRotationRateDerivative     =  6.3e-19;
452         doTestAngularDerivatives(FastMath.toRadians(35), FastMath.toRadians(-20), 1200.0, 100.0,
453                                  toleranceRotationValue,     toleranceRotationDerivative,
454                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
455                                  ".*-clock");
456     }
457 
458     @Test
459     public void testClockOffsetCartesianDerivativesOctantPxMyMz() {
460         double relativeTolerancePositionValue      =  1.5e-15;
461         double relativeTolerancePositionDerivative =  1.6e-10;
462         double relativeToleranceVelocityValue      =  2.3e-15;
463         double relativeToleranceVelocityDerivative =  1.7e-10;
464         doTestCartesianDerivatives(FastMath.toRadians(-35), FastMath.toRadians(-20), 1200.0, 100.0,
465                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
466                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
467                                    ".*-clock");
468     }
469 
470     @Test
471     public void testClockOffsetAngularDerivativesOctantPxMyMz() {
472         double toleranceRotationValue              =  1.5e-15;
473         double toleranceRotationDerivative         =  5.0e-15;
474         double toleranceRotationRateValue          =  1.1e-19;
475         double toleranceRotationRateDerivative     =  6.3e-19;
476         doTestAngularDerivatives(FastMath.toRadians(-35), FastMath.toRadians(-20), 1200.0, 100.0,
477                                  toleranceRotationValue,     toleranceRotationDerivative,
478                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
479                                  ".*-clock");
480     }
481 
482     @Test
483     public void testClockOffsetCartesianDerivativesOctantMxPyPz() {
484         double relativeTolerancePositionValue      =  1.9e-15;
485         double relativeTolerancePositionDerivative =  2.6e-10;
486         double relativeToleranceVelocityValue      =  2.6e-15;
487         double relativeToleranceVelocityDerivative =  2.0e-10;
488         doTestCartesianDerivatives(FastMath.toRadians(150), FastMath.toRadians(20), 1200.0, 100.0,
489                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
490                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
491                                    ".*-clock");
492     }
493 
494     @Test
495     public void testClockOffsetAngularDerivativesOctantMxPyPz() {
496         double toleranceRotationValue              =  1.4e-15;
497         double toleranceRotationDerivative         =  5.2e-15;
498         double toleranceRotationRateValue          =  1.1e-19;
499         double toleranceRotationRateDerivative     =  6.3e-19;
500         doTestAngularDerivatives(FastMath.toRadians(150), FastMath.toRadians(20), 1200.0, 100.0,
501                                  toleranceRotationValue,     toleranceRotationDerivative,
502                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
503                                  ".*-clock");
504     }
505 
506     @Test
507     public void testClockOffsetCartesianDerivativesOctantMxPyMz() {
508         double relativeTolerancePositionValue      =  1.9e-15;
509         double relativeTolerancePositionDerivative =  2.6e-10;
510         double relativeToleranceVelocityValue      =  2.6e-15;
511         double relativeToleranceVelocityDerivative =  2.0e-10;
512         doTestCartesianDerivatives(FastMath.toRadians(150), FastMath.toRadians(20), 1200.0, 100.0,
513                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
514                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
515                                    ".*-clock");
516     }
517 
518     @Test
519     public void testClockOffsetAngularDerivativesOctantMxPyMz() {
520         double toleranceRotationValue              =  1.4e-15;
521         double toleranceRotationDerivative         =  5.2e-15;
522         double toleranceRotationRateValue          =  1.1e-19;
523         double toleranceRotationRateDerivative     =  6.3e-19;
524         doTestAngularDerivatives(FastMath.toRadians(150), FastMath.toRadians(20), 1200.0, 100.0,
525                                  toleranceRotationValue,     toleranceRotationDerivative,
526                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
527                                  ".*-clock");
528     }
529 
530     @Test
531     public void testClockOffsetCartesianDerivativesOctantMxMyPz() {
532         double relativeTolerancePositionValue      =  1.5e-15;
533         double relativeTolerancePositionDerivative =  1.7e-10;
534         double relativeToleranceVelocityValue      =  2.9e-15;
535         double relativeToleranceVelocityDerivative =  1.9e-10;
536         doTestCartesianDerivatives(FastMath.toRadians(-150), FastMath.toRadians(-20), 1200.0, 100.0,
537                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
538                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
539                                    ".*-clock");
540     }
541 
542     @Test
543     public void testClockOffsetAngularDerivativesOctantMxMyPz() {
544         double toleranceRotationValue              =  1.6e-15;
545         double toleranceRotationDerivative         =  4.9e-15;
546         double toleranceRotationRateValue          =  1.1e-19;
547         double toleranceRotationRateDerivative     =  6.3e-19;
548         doTestAngularDerivatives(FastMath.toRadians(-150), FastMath.toRadians(-20), 1200.0, 100.0,
549                                  toleranceRotationValue,     toleranceRotationDerivative,
550                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
551                                  ".*-clock");
552     }
553 
554     @Test
555     public void testClockOffsetCartesianDerivativesOctantMxMyMz() {
556         double relativeTolerancePositionValue      =  1.5e-15;
557         double relativeTolerancePositionDerivative =  1.7e-10;
558         double relativeToleranceVelocityValue      =  2.9e-15;
559         double relativeToleranceVelocityDerivative =  1.9e-10;
560         doTestCartesianDerivatives(FastMath.toRadians(-150), FastMath.toRadians(-20), 1200.0, 100.0,
561                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
562                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
563                                    ".*-clock");
564     }
565 
566     @Test
567     public void testClockOffsetAngularDerivativesOctantMxMyMz() {
568         double toleranceRotationValue              =  1.6e-15;
569         double toleranceRotationDerivative         =  4.9e-15;
570         double toleranceRotationRateValue          =  1.1e-19;
571         double toleranceRotationRateDerivative     =  6.3e-19;
572         doTestAngularDerivatives(FastMath.toRadians(-150), FastMath.toRadians(-20), 1200.0, 100.0,
573                                  toleranceRotationValue,     toleranceRotationDerivative,
574                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
575                                  ".*-clock");
576     }
577 
578     @Test
579     public void testClockOffsetCartesianDerivativesNearPole() {
580         double relativeTolerancePositionValue      =  1.2e-15;
581         double relativeTolerancePositionDerivative =  1.6e-04;
582         double relativeToleranceVelocityValue      =  1.0e-13;
583         double relativeToleranceVelocityDerivative =  4.3e-09;
584         doTestCartesianDerivatives(FastMath.toRadians(89.99995), FastMath.toRadians(90), 1200.0, 100.0,
585                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
586                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
587                                    ".*-clock");
588     }
589 
590     @Test
591     public void testClockOffsetAngularDerivativesNearPole() {
592         double toleranceRotationValue              =  1.5e-15;
593         double toleranceRotationDerivative         =  5.7e-15;
594         double toleranceRotationRateValue          =  1.1e-19;
595         double toleranceRotationRateDerivative     =  6.3e-19;
596         doTestAngularDerivatives(FastMath.toRadians(89.99995), FastMath.toRadians(90), 1200.0, 100.0,
597                                  toleranceRotationValue,     toleranceRotationDerivative,
598                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
599                                  ".*-clock");
600     }
601 
602     @Test
603     public void testStationOffsetCartesianDerivativesOctantPxPyPz() {
604         double relativeTolerancePositionValue      =  2.3e-15;
605         double relativeTolerancePositionDerivative =  1.1e-10;
606         double relativeToleranceVelocityValue      =  3.3e-15;
607         double relativeToleranceVelocityDerivative =  1.2e-10;
608         doTestCartesianDerivatives(FastMath.toRadians(35), FastMath.toRadians(20), 1200.0, 100.0,
609                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
610                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
611                                    ".*-offset-East", ".*-offset-North", ".*-offset-Zenith");
612     }
613 
614     @Test
615     public void testStationOffsetAngularDerivativesOctantPxPyPz() {
616         double toleranceRotationValue              =  1.9e-15;
617         double toleranceRotationDerivative         =  3.1e-18;
618         double toleranceRotationRateValue          =  1.5e-19;
619         double toleranceRotationRateDerivative     =  Precision.SAFE_MIN;
620         doTestAngularDerivatives(FastMath.toRadians(35), FastMath.toRadians(20), 1200.0, 100.0,
621                                  toleranceRotationValue,     toleranceRotationDerivative,
622                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
623                                  ".*-offset-East", ".*-offset-North", ".*-offset-Zenith");
624     }
625 
626     @Test
627     public void testStationOffsetCartesianDerivativesOctantPxPyMz() {
628         double relativeTolerancePositionValue      =  1.4e-15;
629         double relativeTolerancePositionDerivative =  7.3e-11;
630         double relativeToleranceVelocityValue      =  2.8e-15;
631         double relativeToleranceVelocityDerivative =  1.3e-10;
632         doTestCartesianDerivatives(FastMath.toRadians(-35), FastMath.toRadians(20), 1200.0, 100.0,
633                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
634                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
635                                    ".*-offset-East", ".*-offset-North", ".*-offset-Zenith");
636     }
637 
638     @Test
639     public void testStationOffsetAngularDerivativesOctantPxPyMz() {
640         double toleranceRotationValue            =  1.7e-15;
641         double toleranceRotationDerivative       =  3.6e-18;
642         double toleranceRotationRateValue        =  1.5e-19;
643         double toleranceRotationRateDerivative   =  Precision.SAFE_MIN;
644         doTestAngularDerivatives(FastMath.toRadians(-35), FastMath.toRadians(20), 1200.0, 100.0,
645                                  toleranceRotationValue,     toleranceRotationDerivative,
646                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
647                                  ".*-offset-East", ".*-offset-North", ".*-offset-Zenith");
648     }
649 
650     @Test
651     public void testStationOffsetCartesianDerivativesOctantPxMyPz() {
652         double relativeTolerancePositionValue      =  1.7e-15;
653         double relativeTolerancePositionDerivative =  9.0e-11;
654         double relativeToleranceVelocityValue      =  2.9e-15;
655         double relativeToleranceVelocityDerivative =  8.8e-11;
656         doTestCartesianDerivatives(FastMath.toRadians(35), FastMath.toRadians(-20), 1200.0, 100.0,
657                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
658                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
659                                    ".*-offset-East", ".*-offset-North", ".*-offset-Zenith");
660     }
661 
662     @Test
663     public void testStationOffsetAngularDerivativesOctantPxMyPz() {
664         double toleranceRotationValue              =  1.6e-15;
665         double toleranceRotationDerivative         =  3.6e-18;
666         double toleranceRotationRateValue          =  1.5e-19;
667         double toleranceRotationRateDerivative     =  Precision.SAFE_MIN;
668         doTestAngularDerivatives(FastMath.toRadians(35), FastMath.toRadians(-20), 1200.0, 100.0,
669                                  toleranceRotationValue,     toleranceRotationDerivative,
670                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
671                                  ".*-offset-East", ".*-offset-North", ".*-offset-Zenith");
672     }
673 
674     @Test
675     public void testStationOffsetCartesianDerivativesOctantPxMyMz() {
676         double relativeTolerancePositionValue      =  1.5e-15;
677         double relativeTolerancePositionDerivative =  4.2e-11;
678         double relativeToleranceVelocityValue      =  2.7e-15;
679         double relativeToleranceVelocityDerivative =  6.8e-11;
680         doTestCartesianDerivatives(FastMath.toRadians(-35), FastMath.toRadians(-20), 1200.0, 100.0,
681                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
682                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
683                                    ".*-offset-East", ".*-offset-North", ".*-offset-Zenith");
684     }
685 
686     @Test
687     public void testStationOffsetAngularDerivativesOctantPxMyMz() {
688         double toleranceRotationValue            =  1.6e-15;
689         double toleranceRotationDerivative       =  2.6e-18;
690         double toleranceRotationRateValue        =  1.5e-19;
691         double toleranceRotationRateDerivative   =  Precision.SAFE_MIN;
692         doTestAngularDerivatives(FastMath.toRadians(-35), FastMath.toRadians(-20), 1200.0, 100.0,
693                                  toleranceRotationValue,     toleranceRotationDerivative,
694                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
695                                  ".*-offset-East", ".*-offset-North", ".*-offset-Zenith");
696     }
697 
698     @Test
699     public void testStationOffsetCartesianDerivativesOctantMxPyPz() {
700         double relativeTolerancePositionValue      =  1.6e-15;
701         double relativeTolerancePositionDerivative =  9.9e-11;
702         double relativeToleranceVelocityValue      =  2.6e-15;
703         double relativeToleranceVelocityDerivative =  9.5e-11;
704         doTestCartesianDerivatives(FastMath.toRadians(150), FastMath.toRadians(20), 1200.0, 100.0,
705                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
706                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
707                                    ".*-offset-East", ".*-offset-North", ".*-offset-Zenith");
708     }
709 
710     @Test
711     public void testStationOffsetAngularDerivativesOctantMxPyPz() {
712         double toleranceRotationValue            =  1.5e-15;
713         double toleranceRotationDerivative       =  3.1e-18;
714         double toleranceRotationRateValue        =  1.5e-19;
715         double toleranceRotationRateDerivative   =  Precision.SAFE_MIN;
716         doTestAngularDerivatives(FastMath.toRadians(150), FastMath.toRadians(20), 1200.0, 100.0,
717                                  toleranceRotationValue,     toleranceRotationDerivative,
718                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
719                                  ".*-offset-East", ".*-offset-North", ".*-offset-Zenith");
720     }
721 
722     @Test
723     public void testStationOffsetCartesianDerivativesOctantMxPyMz() {
724         double relativeTolerancePositionValue      =  1.6e-15;
725         double relativeTolerancePositionDerivative =  9.9e-11;
726         double relativeToleranceVelocityValue      =  2.6e-15;
727         double relativeToleranceVelocityDerivative =  9.5e-11;
728         doTestCartesianDerivatives(FastMath.toRadians(150), FastMath.toRadians(20), 1200.0, 100.0,
729                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
730                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
731                                    ".*-offset-East", ".*-offset-North", ".*-offset-Zenith");
732     }
733 
734     @Test
735     public void testStationOffsetAngularDerivativesOctantMxPyMz() {
736         double toleranceRotationValue            =  1.5e-15;
737         double toleranceRotationDerivative       =  3.1e-18;
738         double toleranceRotationRateValue        =  1.5e-19;
739         double toleranceRotationRateDerivative   =  Precision.SAFE_MIN;
740         doTestAngularDerivatives(FastMath.toRadians(150), FastMath.toRadians(20), 1200.0, 100.0,
741                                  toleranceRotationValue,     toleranceRotationDerivative,
742                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
743                                  ".*-offset-East", ".*-offset-North", ".*-offset-Zenith");
744     }
745 
746     @Test
747     public void testStationOffsetCartesianDerivativesOctantMxMyPz() {
748         double relativeTolerancePositionValue      =  1.5e-15;
749         double relativeTolerancePositionDerivative =  6.2e-11;
750         double relativeToleranceVelocityValue      =  3.2e-15;
751         double relativeToleranceVelocityDerivative =  1.1e-10;
752         doTestCartesianDerivatives(FastMath.toRadians(-150), FastMath.toRadians(-20), 1200.0, 100.0,
753                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
754                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
755                                    ".*-offset-East", ".*-offset-North", ".*-offset-Zenith");
756     }
757 
758     @Test
759     public void testStationOffsetAngularDerivativesOctantMxMyPz() {
760         double toleranceRotationValue            =  1.6e-15;
761         double toleranceRotationDerivative       =  3.4e-18;
762         double toleranceRotationRateValue        =  1.5e-19;
763         double toleranceRotationRateDerivative   =  Precision.SAFE_MIN;
764         doTestAngularDerivatives(FastMath.toRadians(-150), FastMath.toRadians(-20), 1200.0, 100.0,
765                                  toleranceRotationValue,     toleranceRotationDerivative,
766                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
767                                  ".*-offset-East", ".*-offset-North", ".*-offset-Zenith");
768     }
769 
770     @Test
771     public void testStationOffsetCartesianDerivativesOctantMxMyMz() {
772         double relativeTolerancePositionValue      =  1.5e-15;
773         double relativeTolerancePositionDerivative =  6.2e-11;
774         double relativeToleranceVelocityValue      =  3.2e-15;
775         double relativeToleranceVelocityDerivative =  1.1e-10;
776         doTestCartesianDerivatives(FastMath.toRadians(-150), FastMath.toRadians(-20), 1200.0, 100.0,
777                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
778                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
779                                    ".*-offset-East", ".*-offset-North", ".*-offset-Zenith");
780     }
781 
782     @Test
783     public void testStationOffsetAngularDerivativesOctantMxMyMz() {
784         double toleranceRotationValue            =  1.6e-15;
785         double toleranceRotationDerivative       =  3.4e-18;
786         double toleranceRotationRateValue        =  1.5e-19;
787         double toleranceRotationRateDerivative   =  Precision.SAFE_MIN;
788         doTestAngularDerivatives(FastMath.toRadians(-150), FastMath.toRadians(-20), 1200.0, 100.0,
789                                  toleranceRotationValue,     toleranceRotationDerivative,
790                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
791                                  ".*-offset-East", ".*-offset-North", ".*-offset-Zenith");
792     }
793 
794     @Test
795     public void testStationOffsetCartesianDerivativesNearPole() {
796         double relativeTolerancePositionValue      =  2.1e-15;
797         double relativeTolerancePositionDerivative =  9.4e-10;
798         double relativeToleranceVelocityValue      =  7.5e-14;
799         double relativeToleranceVelocityDerivative =  3.9e-10;
800         doTestCartesianDerivatives(FastMath.toRadians(89.99995), FastMath.toRadians(90), 1200.0, 100.0,
801                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
802                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
803                                    ".*-offset-East", ".*-offset-North", ".*-offset-Zenith");
804     }
805 
806     @Test
807     public void testStationOffsetAngularDerivativesNearPole() {
808         double toleranceRotationValue              =  3.9e-15;
809         double toleranceRotationDerivative         =  8.0e-02; // near pole, the East and North directions are singular
810         double toleranceRotationRateValue          =  1.5e-19;
811         double toleranceRotationRateDerivative     =  Precision.SAFE_MIN;
812         doTestAngularDerivatives(FastMath.toRadians(89.99995), FastMath.toRadians(90), 1200.0, 100.0,
813                                  toleranceRotationValue,     toleranceRotationDerivative,
814                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
815                                  ".*-offset-East", ".*-offset-North", ".*-offset-Zenith");
816     }
817 
818     @Test
819     public void testPolarMotionCartesianDerivativesOctantPxPyPz() {
820         double relativeTolerancePositionValue      =  2.3e-15;
821         double relativeTolerancePositionDerivative =  7.3e-09;
822         double relativeToleranceVelocityValue      =  3.3e-15;
823         double relativeToleranceVelocityDerivative =  4.8e-09;
824         doTestCartesianDerivatives(FastMath.toRadians(35), FastMath.toRadians(20), 1200.0, 1.0,
825                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
826                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
827                                    "polar-offset-X", "polar-drift-X",
828                                    "polar-offset-Y", "polar-drift-Y");
829     }
830 
831     @Test
832     public void testPolarMotionAngularDerivativesOctantPxPyPz() {
833         double toleranceRotationValue              =  1.9e-15;
834         double toleranceRotationDerivative         =  6.6e-09;
835         double toleranceRotationRateValue          =  1.5e-19;
836         double toleranceRotationRateDerivative     =  2.2e-13;
837         doTestAngularDerivatives(FastMath.toRadians(35), FastMath.toRadians(20), 1200.0, 0.2,
838                                  toleranceRotationValue,     toleranceRotationDerivative,
839                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
840                                  "polar-offset-X", "polar-drift-X",
841                                  "polar-offset-Y", "polar-drift-Y");
842     }
843 
844     @Test
845     public void testPolarMotionCartesianDerivativesOctantPxPyMz() {
846         double relativeTolerancePositionValue      =  1.4e-15;
847         double relativeTolerancePositionDerivative =  4.1e-09;
848         double relativeToleranceVelocityValue      =  2.8e-15;
849         double relativeToleranceVelocityDerivative =  4.7e-09;
850         doTestCartesianDerivatives(FastMath.toRadians(-35), FastMath.toRadians(20), 1200.0, 1.0,
851                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
852                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
853                                    "polar-offset-X", "polar-drift-X",
854                                    "polar-offset-Y", "polar-drift-Y");
855     }
856 
857     @Test
858     public void testPolarMotionAngularDerivativesOctantPxPyMz() {
859         double toleranceRotationValue              =  1.7e-15;
860         double toleranceRotationDerivative         =  6.9e-09;
861         double toleranceRotationRateValue          =  1.5e-19;
862         double toleranceRotationRateDerivative     =  2.2e-13;
863         doTestAngularDerivatives(FastMath.toRadians(-35), FastMath.toRadians(20), 1200.0, 0.2,
864                                  toleranceRotationValue,     toleranceRotationDerivative,
865                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
866                                  "polar-offset-X", "polar-drift-X",
867                                  "polar-offset-Y", "polar-drift-Y");
868     }
869 
870     @Test
871     public void testPolarMotionCartesianDerivativesOctantPxMyPz() {
872         double relativeTolerancePositionValue      =  1.7e-15;
873         double relativeTolerancePositionDerivative =  7.8e-09;
874         double relativeToleranceVelocityValue      =  2.9e-15;
875         double relativeToleranceVelocityDerivative =  4.4e-09;
876         doTestCartesianDerivatives(FastMath.toRadians(35), FastMath.toRadians(-20), 1200.0, 1.0,
877                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
878                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
879                                    "polar-offset-X", "polar-drift-X",
880                                    "polar-offset-Y", "polar-drift-Y");
881     }
882 
883     @Test
884     public void testPolarMotionAngularDerivativesOctantPxMyPz() {
885         double toleranceRotationValue              =  1.6e-15;
886         double toleranceRotationDerivative         =  7.5e-09;
887         double toleranceRotationRateValue          =  1.5e-19;
888         double toleranceRotationRateDerivative     =  2.2e-13;
889         doTestAngularDerivatives(FastMath.toRadians(35), FastMath.toRadians(-20), 1200.0, 0.2,
890                                  toleranceRotationValue,     toleranceRotationDerivative,
891                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
892                                  "polar-offset-X", "polar-drift-X",
893                                  "polar-offset-Y", "polar-drift-Y");
894     }
895 
896     @Test
897     public void testPolarMotionCartesianDerivativesOctantPxMyMz() {
898         double relativeTolerancePositionValue      =  1.5e-15;
899         double relativeTolerancePositionDerivative =  4.1e-09;
900         double relativeToleranceVelocityValue      =  2.7e-15;
901         double relativeToleranceVelocityDerivative =  4.4e-09;
902         doTestCartesianDerivatives(FastMath.toRadians(-35), FastMath.toRadians(-20), 1200.0, 1.0,
903                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
904                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
905                                    "polar-offset-X", "polar-drift-X",
906                                    "polar-offset-Y", "polar-drift-Y");
907     }
908 
909     @Test
910     public void testPolarMotionAngularDerivativesOctantPxMyMz() {
911         double toleranceRotationValue            =  1.6e-15;
912         double toleranceRotationDerivative       =  7.3e-09;
913         double toleranceRotationRateValue        =  1.5e-19;
914         double toleranceRotationRateDerivative   =  2.2e-13;
915         doTestAngularDerivatives(FastMath.toRadians(-35), FastMath.toRadians(-20), 1200.0, 0.2,
916                                  toleranceRotationValue,     toleranceRotationDerivative,
917                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
918                                  "polar-offset-X", "polar-drift-X",
919                                  "polar-offset-Y", "polar-drift-Y");
920     }
921 
922     @Test
923     public void testPolarMotionCartesianDerivativesOctantMxPyPz() {
924         double relativeTolerancePositionValue      =  1.6e-15;
925         double relativeTolerancePositionDerivative =  8.4e-09;
926         double relativeToleranceVelocityValue      =  2.6e-15;
927         double relativeToleranceVelocityDerivative =  5.0e-09;
928         doTestCartesianDerivatives(FastMath.toRadians(150), FastMath.toRadians(20), 1200.0, 1.0,
929                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
930                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
931                                    "polar-offset-X", "polar-drift-X",
932                                    "polar-offset-Y", "polar-drift-Y");
933     }
934 
935     @Test
936     public void testPolarMotionAngularDerivativesOctantMxPyPz() {
937         double toleranceRotationValue              =  1.4e-15;
938         double toleranceRotationDerivative         =  6.3e-09;
939         double toleranceRotationRateValue          =  1.5e-19;
940         double toleranceRotationRateDerivative     =  2.2e-13;
941         doTestAngularDerivatives(FastMath.toRadians(150), FastMath.toRadians(20), 1200.0, 0.2,
942                                  toleranceRotationValue,     toleranceRotationDerivative,
943                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
944                                  "polar-offset-X", "polar-drift-X",
945                                  "polar-offset-Y", "polar-drift-Y");
946     }
947 
948     @Test
949     public void testPolarMotionCartesianDerivativesOctantMxPyMz() {
950         double relativeTolerancePositionValue      =  1.6e-15;
951         double relativeTolerancePositionDerivative =  8.4e-09;
952         double relativeToleranceVelocityValue      =  2.6e-15;
953         double relativeToleranceVelocityDerivative =  5.0e-09;
954         doTestCartesianDerivatives(FastMath.toRadians(150), FastMath.toRadians(20), 1200.0, 1.0,
955                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
956                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
957                                    "polar-offset-X", "polar-drift-X",
958                                    "polar-offset-Y", "polar-drift-Y");
959     }
960 
961     @Test
962     public void testPolarMotionAngularDerivativesOctantMxPyMz() {
963         double toleranceRotationValue              =  1.4e-15;
964         double toleranceRotationDerivative         =  6.3e-09;
965         double toleranceRotationRateValue          =  1.5e-19;
966         double toleranceRotationRateDerivative     =  2.2e-13;
967         doTestAngularDerivatives(FastMath.toRadians(150), FastMath.toRadians(20), 1200.0, 0.2,
968                                  toleranceRotationValue,     toleranceRotationDerivative,
969                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
970                                  "polar-offset-X", "polar-drift-X",
971                                  "polar-offset-Y", "polar-drift-Y");
972     }
973 
974     @Test
975     public void testPolarMotionCartesianDerivativesOctantMxMyPz() {
976         double relativeTolerancePositionValue      =  1.5e-15;
977         double relativeTolerancePositionDerivative =  5.0e-09;
978         double relativeToleranceVelocityValue      =  3.2e-15;
979         double relativeToleranceVelocityDerivative =  5.3e-09;
980         doTestCartesianDerivatives(FastMath.toRadians(-150), FastMath.toRadians(-20), 1200.0, 1.0,
981                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
982                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
983                                    "polar-offset-X", "polar-drift-X",
984                                    "polar-offset-Y", "polar-drift-Y");
985     }
986 
987     @Test
988     public void testPolarMotionAngularDerivativesOctantMxMyPz() {
989         double toleranceRotationValue              =  1.6e-15;
990         double toleranceRotationDerivative         =  7.7e-09;
991         double toleranceRotationRateValue          =  1.5e-19;
992         double toleranceRotationRateDerivative     =  2.2e-13;
993         doTestAngularDerivatives(FastMath.toRadians(-150), FastMath.toRadians(-20), 1200.0, 0.2,
994                                  toleranceRotationValue,     toleranceRotationDerivative,
995                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
996                                  "polar-offset-X", "polar-drift-X",
997                                  "polar-offset-Y", "polar-drift-Y");
998     }
999 
1000     @Test
1001     public void testPolarMotionCartesianDerivativesOctantMxMyMz() {
1002         double relativeTolerancePositionValue      =  1.5e-15;
1003         double relativeTolerancePositionDerivative =  5.0e-09;
1004         double relativeToleranceVelocityValue      =  3.2e-15;
1005         double relativeToleranceVelocityDerivative =  5.3e-09;
1006         doTestCartesianDerivatives(FastMath.toRadians(-150), FastMath.toRadians(-20), 1200.0, 1.0,
1007                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
1008                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
1009                                    "polar-offset-X", "polar-drift-X",
1010                                    "polar-offset-Y", "polar-drift-Y");
1011     }
1012 
1013     @Test
1014     public void testPolarMotionAngularDerivativesOctantMxMyMz() {
1015         double toleranceRotationValue              =  1.6e-15;
1016         double toleranceRotationDerivative         =  7.7e-09;
1017         double toleranceRotationRateValue          =  1.5e-19;
1018         double toleranceRotationRateDerivative     =  2.2e-13;
1019         doTestAngularDerivatives(FastMath.toRadians(-150), FastMath.toRadians(-20), 1200.0, 0.2,
1020                                  toleranceRotationValue,     toleranceRotationDerivative,
1021                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
1022                                  "polar-offset-X", "polar-drift-X",
1023                                  "polar-offset-Y", "polar-drift-Y");
1024     }
1025 
1026     @Test
1027     public void testPolarMotionCartesianDerivativesNearPole() {
1028         double relativeTolerancePositionValue      =  1.2e-15;
1029         double relativeTolerancePositionDerivative =  5.7e-09;
1030         double relativeToleranceVelocityValue      =  9.4e-13;
1031         double relativeToleranceVelocityDerivative =  1.2e-09;
1032         doTestCartesianDerivatives(FastMath.toRadians(89.99995), FastMath.toRadians(90), 1200.0, 1.0,
1033                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
1034                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
1035                                    "polar-offset-X", "polar-drift-X",
1036                                    "polar-offset-Y", "polar-drift-Y");
1037     }
1038 
1039     @Test
1040     public void testPolarMotionAngularDerivativesNearPole() {
1041         double toleranceRotationValue              =  1.5e-15;
1042         double toleranceRotationDerivative         =  6.5e-09;
1043         double toleranceRotationRateValue          =  1.5e-19;
1044         double toleranceRotationRateDerivative     =  2.2e-13;
1045         doTestAngularDerivatives(FastMath.toRadians(89.99995), FastMath.toRadians(90), 1200.0, 0.2,
1046                                  toleranceRotationValue,     toleranceRotationDerivative,
1047                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
1048                                  "polar-offset-X", "polar-drift-X",
1049                                  "polar-offset-Y", "polar-drift-Y");
1050     }
1051 
1052     @Test
1053     public void testPrimeMeridianCartesianDerivativesOctantPxPyPz() {
1054         double relativeTolerancePositionValue      =  2.3e-15;
1055         double relativeTolerancePositionDerivative =  5.7e-09;
1056         double relativeToleranceVelocityValue      =  3.3e-15;
1057         double relativeToleranceVelocityDerivative =  2.7e-09;
1058         doTestCartesianDerivatives(FastMath.toRadians(35), FastMath.toRadians(20), 1200.0, 1.0,
1059                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
1060                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
1061                                    "prime-meridian-offset", "prime-meridian-drift");
1062     }
1063 
1064     @Test
1065     public void testPrimeMeridianAngularDerivativesOctantPxPyPz() {
1066         double toleranceRotationValue              =  1.9e-15;
1067         double toleranceRotationDerivative         =  4.9e-09;
1068         double toleranceRotationRateValue          =  1.5e-19;
1069         double toleranceRotationRateDerivative     =  2.0e-13;
1070         doTestAngularDerivatives(FastMath.toRadians(35), FastMath.toRadians(20), 1200.0, 0.2,
1071                                  toleranceRotationValue,     toleranceRotationDerivative,
1072                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
1073                                  "prime-meridian-offset", "prime-meridian-drift");
1074     }
1075 
1076     @Test
1077     public void testPrimeMeridianCartesianDerivativesOctantPxPyMz() {
1078         double relativeTolerancePositionValue      =  1.4e-15;
1079         double relativeTolerancePositionDerivative =  3.1e-09;
1080         double relativeToleranceVelocityValue      =  2.8e-15;
1081         double relativeToleranceVelocityDerivative =  3.3e-09;
1082         doTestCartesianDerivatives(FastMath.toRadians(-35), FastMath.toRadians(20), 1200.0, 1.0,
1083                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
1084                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
1085                                    "prime-meridian-offset", "prime-meridian-drift");
1086     }
1087 
1088     @Test
1089     public void testPrimeMeridianAngularDerivativesOctantPxPyMz() {
1090         double toleranceRotationValue            =  1.7e-15;
1091         double toleranceRotationDerivative       =  5.7e-09;
1092         double toleranceRotationRateValue        =  1.5e-19;
1093         double toleranceRotationRateDerivative   =  2.0e-13;
1094         doTestAngularDerivatives(FastMath.toRadians(-35), FastMath.toRadians(20), 1200.0, 0.2,
1095                                  toleranceRotationValue,     toleranceRotationDerivative,
1096                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
1097                                  "prime-meridian-offset", "prime-meridian-drift");
1098     }
1099 
1100     @Test
1101     public void testPrimeMeridianCartesianDerivativesOctantPxMyPz() {
1102         double relativeTolerancePositionValue      =  1.7e-15;
1103         double relativeTolerancePositionDerivative =  5.3e-09;
1104         double relativeToleranceVelocityValue      =  2.9e-15;
1105         double relativeToleranceVelocityDerivative =  3.4e-09;
1106         doTestCartesianDerivatives(FastMath.toRadians(35), FastMath.toRadians(-20), 1200.0, 1.0,
1107                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
1108                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
1109                                    "prime-meridian-offset", "prime-meridian-drift");
1110     }
1111 
1112     @Test
1113     public void testPrimeMeridianAngularDerivativesOctantPxMyPz() {
1114         double toleranceRotationValue              =  1.6e-15;
1115         double toleranceRotationDerivative         =  6.4e-09;
1116         double toleranceRotationRateValue          =  1.5e-19;
1117         double toleranceRotationRateDerivative     =  2.0e-13;
1118         doTestAngularDerivatives(FastMath.toRadians(35), FastMath.toRadians(-20), 1200.0, 0.2,
1119                                  toleranceRotationValue,     toleranceRotationDerivative,
1120                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
1121                                  "prime-meridian-offset", "prime-meridian-drift");
1122     }
1123 
1124     @Test
1125     public void testPrimeMeridianCartesianDerivativesOctantPxMyMz() {
1126         double relativeTolerancePositionValue      =  1.5e-15;
1127         double relativeTolerancePositionDerivative =  3.1e-09;
1128         double relativeToleranceVelocityValue      =  2.7e-15;
1129         double relativeToleranceVelocityDerivative =  3.0e-09;
1130         doTestCartesianDerivatives(FastMath.toRadians(-35), FastMath.toRadians(-20), 1200.0, 1.0,
1131                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
1132                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
1133                                    "prime-meridian-offset", "prime-meridian-drift");
1134     }
1135 
1136     @Test
1137     public void testPrimeMeridianAngularDerivativesOctantPxMyMz() {
1138         double toleranceRotationValue              =  1.5e-15;
1139         double toleranceRotationDerivative         =  6.3e-09;
1140         double toleranceRotationRateValue          =  1.5e-19;
1141         double toleranceRotationRateDerivative     =  2.0e-13;
1142         doTestAngularDerivatives(FastMath.toRadians(-35), FastMath.toRadians(-20), 1200.0, 0.2,
1143                                  toleranceRotationValue,     toleranceRotationDerivative,
1144                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
1145                                  "prime-meridian-offset", "prime-meridian-drift");
1146     }
1147 
1148     @Test
1149     public void testPrimeMeridianCartesianDerivativesOctantMxPyPz() {
1150         double relativeTolerancePositionValue      =  1.6e-15;
1151         double relativeTolerancePositionDerivative =  5.5e-09;
1152         double relativeToleranceVelocityValue      =  2.6e-15;
1153         double relativeToleranceVelocityDerivative =  2.8e-09;
1154         doTestCartesianDerivatives(FastMath.toRadians(150), FastMath.toRadians(20), 1200.0, 1.0,
1155                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
1156                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
1157                                    "prime-meridian-offset", "prime-meridian-drift");
1158     }
1159 
1160     @Test
1161     public void testPrimeMeridianAngularDerivativesOctantMxPyPz() {
1162         double toleranceRotationValue            =  1.5e-15;
1163         double toleranceRotationDerivative       =  6.8e-09;
1164         double toleranceRotationRateValue        =  1.5e-19;
1165         double toleranceRotationRateDerivative   =  2.0e-13;
1166         doTestAngularDerivatives(FastMath.toRadians(150), FastMath.toRadians(20), 1200.0, 0.2,
1167                                  toleranceRotationValue,     toleranceRotationDerivative,
1168                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
1169                                  "prime-meridian-offset", "prime-meridian-drift");
1170     }
1171 
1172     @Test
1173     public void testPrimeMeridianCartesianDerivativesOctantMxPyMz() {
1174         double relativeTolerancePositionValue      =  1.6e-15;
1175         double relativeTolerancePositionDerivative =  5.5e-09;
1176         double relativeToleranceVelocityValue      =  2.6e-15;
1177         double relativeToleranceVelocityDerivative =  2.8e-09;
1178         doTestCartesianDerivatives(FastMath.toRadians(150), FastMath.toRadians(20), 1200.0, 1.0,
1179                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
1180                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
1181                                    "prime-meridian-offset", "prime-meridian-drift");
1182     }
1183 
1184     @Test
1185     public void testPrimeMeridianAngularDerivativesOctantMxPyMz() {
1186         double toleranceRotationValue            =  1.5e-15;
1187         double toleranceRotationDerivative       =  6.8e-09;
1188         double toleranceRotationRateValue        =  1.5e-19;
1189         double toleranceRotationRateDerivative   =  2.0e-13;
1190         doTestAngularDerivatives(FastMath.toRadians(150), FastMath.toRadians(20), 1200.0, 0.2,
1191                                  toleranceRotationValue,     toleranceRotationDerivative,
1192                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
1193                                  "prime-meridian-offset", "prime-meridian-drift");
1194     }
1195 
1196     @Test
1197     public void testPrimeMeridianCartesianDerivativesOctantMxMyPz() {
1198         double relativeTolerancePositionValue      =  1.5e-15;
1199         double relativeTolerancePositionDerivative =  3.1e-09;
1200         double relativeToleranceVelocityValue      =  3.2e-15;
1201         double relativeToleranceVelocityDerivative =  3.2e-09;
1202         doTestCartesianDerivatives(FastMath.toRadians(-150), FastMath.toRadians(-20), 1200.0, 1.0,
1203                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
1204                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
1205                                    "prime-meridian-offset", "prime-meridian-drift");
1206     }
1207 
1208     @Test
1209     public void testPrimeMeridianAngularDerivativesOctantMxMyPz() {
1210         double toleranceRotationValue              =  1.6e-15;
1211         double toleranceRotationDerivative         =  6.2e-09;
1212         double toleranceRotationRateValue          =  1.5e-19;
1213         double toleranceRotationRateDerivative     =  2.0e-13;
1214         doTestAngularDerivatives(FastMath.toRadians(-150), FastMath.toRadians(-20), 1200.0, 0.2,
1215                                  toleranceRotationValue,     toleranceRotationDerivative,
1216                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
1217                                  "prime-meridian-offset", "prime-meridian-drift");
1218     }
1219 
1220     @Test
1221     public void testPrimeMeridianCartesianDerivativesOctantMxMyMz() {
1222         double relativeTolerancePositionValue      =  1.5e-15;
1223         double relativeTolerancePositionDerivative =  3.1e-09;
1224         double relativeToleranceVelocityValue      =  3.2e-15;
1225         double relativeToleranceVelocityDerivative =  3.2e-09;
1226         doTestCartesianDerivatives(FastMath.toRadians(-150), FastMath.toRadians(-20), 1200.0, 1.0,
1227                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
1228                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
1229                                    "prime-meridian-offset", "prime-meridian-drift");
1230     }
1231 
1232     @Test
1233     public void testPrimeMeridianAngularDerivativesOctantMxMyMz() {
1234         double toleranceRotationValue              =  1.6e-15;
1235         double toleranceRotationDerivative         =  6.2e-09;
1236         double toleranceRotationRateValue          =  1.5e-19;
1237         double toleranceRotationRateDerivative     =  2.0e-13;
1238         doTestAngularDerivatives(FastMath.toRadians(-150), FastMath.toRadians(-20), 1200.0, 0.2,
1239                                  toleranceRotationValue,     toleranceRotationDerivative,
1240                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
1241                                  "prime-meridian-offset", "prime-meridian-drift");
1242     }
1243 
1244     @Test
1245     public void testPrimeMeridianCartesianDerivativesNearPole() {
1246         double relativeTolerancePositionValue      =  1.2e-15;
1247         double relativeTolerancePositionDerivative =  1.6e-03;
1248         double relativeToleranceVelocityValue      =  5.8e-14;
1249         double relativeToleranceVelocityDerivative =  2.6e-08;
1250         doTestCartesianDerivatives(FastMath.toRadians(89.99995), FastMath.toRadians(90), 1200.0, 1.0,
1251                                    relativeTolerancePositionValue, relativeTolerancePositionDerivative,
1252                                    relativeToleranceVelocityValue, relativeToleranceVelocityDerivative,
1253                                    "prime-meridian-offset", "prime-meridian-drift");
1254     }
1255 
1256     @Test
1257     public void testPrimeMeridianAngularDerivativesNearPole() {
1258         double toleranceRotationValue              =  1.5e-15;
1259         double toleranceRotationDerivative         =  7.3e-09;
1260         double toleranceRotationRateValue          =  1.5e-19;
1261         double toleranceRotationRateDerivative     =  2.0e-13;
1262         doTestAngularDerivatives(FastMath.toRadians(89.99995), FastMath.toRadians(90), 1200.0, 0.2,
1263                                  toleranceRotationValue,     toleranceRotationDerivative,
1264                                  toleranceRotationRateValue, toleranceRotationRateDerivative,
1265                                  "prime-meridian-offset", "prime-meridian-drift");
1266     }
1267 
1268     @Test
1269     public void testNoReferenceDateGradient() {
1270         Utils.setDataRoot("regular-data");
1271         final Frame eme2000 = FramesFactory.getEME2000();
1272         final AbsoluteDate date = AbsoluteDate.J2000_EPOCH;
1273         final OneAxisEllipsoid earth =
1274                         new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
1275                                              Constants.WGS84_EARTH_FLATTENING,
1276                                              FramesFactory.getITRF(IERSConventions.IERS_2010, true));
1277         final GroundStation station = new GroundStation(new TopocentricFrame(earth,
1278                                                                              new GeodeticPoint(0.1, 0.2, 100),
1279                                                                              "dummy"));
1280         try {
1281             station.getOffsetToInertial(eme2000, date);
1282             Assert.fail("an exception should have been thrown");
1283         } catch (OrekitException oe) {
1284             Assert.assertEquals(OrekitMessages.NO_REFERENCE_DATE_FOR_PARAMETER, oe.getSpecifier());
1285             Assert.assertEquals("prime-meridian-offset", (String) oe.getParts()[0]);
1286         }
1287         
1288         try {
1289             int freeParameters = 9;
1290             Map<String, Integer> indices = new HashMap<>();
1291             for (final ParameterDriver driver : Arrays.asList(station.getPrimeMeridianOffsetDriver(),
1292                                                               station.getPrimeMeridianDriftDriver(),
1293                                                               station.getPolarOffsetXDriver(),
1294                                                               station.getPolarDriftXDriver(),
1295                                                               station.getPolarOffsetYDriver(),
1296                                                               station.getPolarDriftYDriver(),
1297                                                               station.getClockOffsetDriver(),
1298                                                               station.getEastOffsetDriver(),
1299                                                               station.getNorthOffsetDriver(),
1300                                                               station.getZenithOffsetDriver())) {
1301                 indices.put(driver.getName(), indices.size());
1302             }
1303             station.getOffsetToInertial(eme2000, date, freeParameters, indices);
1304             Assert.fail("an exception should have been thrown");
1305         } catch (OrekitException oe) {
1306             Assert.assertEquals(OrekitMessages.NO_REFERENCE_DATE_FOR_PARAMETER, oe.getSpecifier());
1307             Assert.assertEquals("prime-meridian-offset", (String) oe.getParts()[0]);
1308         }
1309         
1310     }
1311 
1312     private void doTestCartesianDerivatives(double latitude, double longitude, double altitude, double stepFactor,
1313                                             double relativeTolerancePositionValue, double relativeTolerancePositionDerivative,
1314                                             double relativeToleranceVelocityValue, double relativeToleranceVelocityDerivative,
1315                                             String... parameterPattern) {
1316         Utils.setDataRoot("regular-data");
1317         final Frame eme2000 = FramesFactory.getEME2000();
1318         final AbsoluteDate date = AbsoluteDate.J2000_EPOCH;
1319         final AbsoluteDate date0 = date.shiftedBy(50000);
1320         final OneAxisEllipsoid earth =
1321                         new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
1322                                              Constants.WGS84_EARTH_FLATTENING,
1323                                              FramesFactory.getITRF(IERSConventions.IERS_2010, true));
1324         final GroundStation station = new GroundStation(new TopocentricFrame(earth,
1325                                                                              new GeodeticPoint(latitude, longitude, altitude),
1326                                                                              "dummy"));
1327         final GradientField gradientField = GradientField.getField(parameterPattern.length);
1328         ParameterDriver[] selectedDrivers = new ParameterDriver[parameterPattern.length];
1329         UnivariateDifferentiableVectorFunction[] dFCartesian = new UnivariateDifferentiableVectorFunction[parameterPattern.length];
1330         final ParameterDriver[] allDrivers = selectAllDrivers(station);
1331         for (ParameterDriver driver : allDrivers) {
1332             driver.setReferenceDate(date0);
1333         }
1334         Map<String, Integer> indices = new HashMap<>();
1335         for (int k = 0; k < dFCartesian.length; ++k) {
1336             for (int i = 0; i < allDrivers.length; ++i) {
1337                 if (allDrivers[i].getName().matches(parameterPattern[k])) {
1338                     selectedDrivers[k] = allDrivers[i];
1339                     dFCartesian[k] = differentiatedStationPV(station, eme2000, date, selectedDrivers[k], stepFactor);
1340                     indices.put(selectedDrivers[k].getName(), k);
1341                 }
1342             }
1343         };
1344 
1345         RandomGenerator generator = new Well19937a(0x084d58a19c498a54l);
1346 
1347         double maxPositionValueRelativeError      = 0;
1348         double maxPositionDerivativeRelativeError = 0;
1349         double maxVelocityValueRelativeError      = 0;
1350         double maxVelocityDerivativeRelativeError = 0;
1351         for (int i = 0; i < 1000; ++i) {
1352 
1353             // randomly change one parameter
1354             ParameterDriver changed = allDrivers[generator.nextInt(allDrivers.length)];
1355             changed.setNormalizedValue(2 * generator.nextDouble() - 1);
1356 
1357             // transform to check
1358             FieldTransform<Gradient> t = station.getOffsetToInertial(eme2000, date, parameterPattern.length, indices);
1359             FieldPVCoordinates<Gradient> pv = t.transformPVCoordinates(FieldPVCoordinates.getZero(gradientField));
1360             for (int k = 0; k < dFCartesian.length; ++k) {
1361 
1362                 // reference values and derivatives computed using finite differences
1363                 Gradient[] refCartesian = dFCartesian[k].value(Gradient.variable(1, 0, selectedDrivers[k].getValue()));
1364 
1365                 // position
1366                 final Vector3D refP = new Vector3D(refCartesian[0].getValue(),
1367                                                    refCartesian[1].getValue(),
1368                                                    refCartesian[2].getValue());
1369                 final Vector3D resP = new Vector3D(pv.getPosition().getX().getValue(),
1370                                                    pv.getPosition().getY().getValue(),
1371                                                    pv.getPosition().getZ().getValue());
1372                 maxPositionValueRelativeError =
1373                                 FastMath.max(maxPositionValueRelativeError, Vector3D.distance(refP, resP) / refP.getNorm());
1374                 final Vector3D refPD = new Vector3D(refCartesian[0].getPartialDerivative(0),
1375                                                     refCartesian[1].getPartialDerivative(0),
1376                                                     refCartesian[2].getPartialDerivative(0));
1377                 final Vector3D resPD = new Vector3D(pv.getPosition().getX().getPartialDerivative(k),
1378                                                     pv.getPosition().getY().getPartialDerivative(k),
1379                                                     pv.getPosition().getZ().getPartialDerivative(k));
1380                 maxPositionDerivativeRelativeError =
1381                                 FastMath.max(maxPositionDerivativeRelativeError, Vector3D.distance(refPD, resPD) / refPD.getNorm());
1382 
1383                 // velocity
1384                 final Vector3D refV = new Vector3D(refCartesian[3].getValue(),
1385                                                    refCartesian[4].getValue(),
1386                                                    refCartesian[5].getValue());
1387                 final Vector3D resV = new Vector3D(pv.getVelocity().getX().getValue(),
1388                                                    pv.getVelocity().getY().getValue(),
1389                                                    pv.getVelocity().getZ().getValue());
1390                 maxVelocityValueRelativeError =
1391                                 FastMath.max(maxVelocityValueRelativeError, Vector3D.distance(refV, resV) / refV.getNorm());
1392                 final Vector3D refVD = new Vector3D(refCartesian[3].getPartialDerivative(0),
1393                                                     refCartesian[4].getPartialDerivative(0),
1394                                                     refCartesian[5].getPartialDerivative(0));
1395                 final Vector3D resVD = new Vector3D(pv.getVelocity().getX().getPartialDerivative(k),
1396                                                     pv.getVelocity().getY().getPartialDerivative(k),
1397                                                     pv.getVelocity().getZ().getPartialDerivative(k));
1398                 maxVelocityDerivativeRelativeError =
1399                                 FastMath.max(maxVelocityDerivativeRelativeError, Vector3D.distance(refVD, resVD) / refVD.getNorm());
1400 
1401             }
1402 
1403         }
1404 
1405         if (maxPositionValueRelativeError      > relativeTolerancePositionValue      ||
1406             maxPositionDerivativeRelativeError > relativeTolerancePositionDerivative ||
1407             maxVelocityValueRelativeError      > relativeToleranceVelocityValue      ||
1408             maxVelocityDerivativeRelativeError > relativeToleranceVelocityDerivative) {
1409             print("relativeTolerancePositionValue",          maxPositionValueRelativeError);
1410             print("relativeTolerancePositionDerivative",     maxPositionDerivativeRelativeError);
1411             print("relativeToleranceVelocityValue",      maxVelocityValueRelativeError);
1412             print("relativeToleranceVelocityDerivative", maxVelocityDerivativeRelativeError);
1413         }
1414         Assert.assertEquals(0.0, maxPositionValueRelativeError,      relativeTolerancePositionValue);
1415         Assert.assertEquals(0.0, maxPositionDerivativeRelativeError, relativeTolerancePositionDerivative);
1416         Assert.assertEquals(0.0, maxVelocityValueRelativeError,      relativeToleranceVelocityValue);
1417         Assert.assertEquals(0.0, maxVelocityDerivativeRelativeError, relativeToleranceVelocityDerivative);
1418 
1419     }
1420 
1421     private void doTestAngularDerivatives(double latitude, double longitude, double altitude, double stepFactor,
1422                                           double toleranceRotationValue,     double toleranceRotationDerivative,
1423                                           double toleranceRotationRateValue, double toleranceRotationRateDerivative,
1424                                           String... parameterPattern) {
1425         Utils.setDataRoot("regular-data");
1426         final Frame eme2000 = FramesFactory.getEME2000();
1427         final AbsoluteDate date = AbsoluteDate.J2000_EPOCH;
1428         final AbsoluteDate date0 = date.shiftedBy(50000);
1429         final OneAxisEllipsoid earth =
1430                         new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
1431                                              Constants.WGS84_EARTH_FLATTENING,
1432                                              FramesFactory.getITRF(IERSConventions.IERS_2010, true));
1433         final GroundStation station = new GroundStation(new TopocentricFrame(earth,
1434                                                                              new GeodeticPoint(latitude, longitude, altitude),
1435                                                                              "dummy"));
1436         ParameterDriver[] selectedDrivers = new ParameterDriver[parameterPattern.length];
1437         UnivariateDifferentiableVectorFunction[] dFAngular   = new UnivariateDifferentiableVectorFunction[parameterPattern.length];
1438         final ParameterDriver[] allDrivers = selectAllDrivers(station);
1439         for (ParameterDriver driver : allDrivers) {
1440             driver.setReferenceDate(date0);
1441         }
1442         Map<String, Integer> indices = new HashMap<>();
1443         for (int k = 0; k < dFAngular.length; ++k) {
1444             for (int i = 0; i < allDrivers.length; ++i) {
1445                 if (allDrivers[i].getName().matches(parameterPattern[k])) {
1446                     selectedDrivers[k] = allDrivers[i];
1447                     dFAngular[k]   = differentiatedTransformAngular(station, eme2000, date, selectedDrivers[k], stepFactor);
1448                     indices.put(selectedDrivers[k].getName(), k);
1449                 }
1450             }
1451         };
1452         RandomGenerator generator = new Well19937a(0xa01a1d8fe5d80af7l);
1453 
1454         double maxRotationValueError          = 0;
1455         double maxRotationDerivativeError     = 0;
1456         double maxRotationRateValueError      = 0;
1457         double maxRotationRateDerivativeError = 0;
1458         for (int i = 0; i < 1000; ++i) {
1459 
1460             // randomly change one parameter
1461             ParameterDriver changed = allDrivers[generator.nextInt(allDrivers.length)];
1462             changed.setNormalizedValue(2 * generator.nextDouble() - 1);
1463 
1464             // transform to check
1465             FieldTransform<Gradient> t = station.getOffsetToInertial(eme2000, date, parameterPattern.length, indices);
1466             for (int k = 0; k < dFAngular.length; ++k) {
1467 
1468                 // reference values and derivatives computed using finite differences
1469                 Gradient[] refAngular = dFAngular[k].value(Gradient.variable(1, 0, selectedDrivers[k].getValue()));
1470 
1471                 // rotation
1472                 final Rotation refQ = new Rotation(refAngular[0].getValue(),
1473                                                    refAngular[1].getValue(),
1474                                                    refAngular[2].getValue(),
1475                                                    refAngular[3].getValue(),
1476                                                    true);
1477                 final Rotation resQ = t.getRotation().toRotation();
1478                 maxRotationValueError      = FastMath.max(maxRotationValueError, Rotation.distance(refQ, resQ));
1479                 double sign = FastMath.copySign(1.0,
1480                                                 refAngular[0].getValue() * t.getRotation().getQ0().getValue() +
1481                                                 refAngular[1].getValue() * t.getRotation().getQ1().getValue() +
1482                                                 refAngular[2].getValue() * t.getRotation().getQ2().getValue() +
1483                                                 refAngular[3].getValue() * t.getRotation().getQ3().getValue());
1484                 maxRotationDerivativeError = FastMath.max(maxRotationDerivativeError,
1485                                                           FastMath.abs(sign * refAngular[0].getPartialDerivative(0) -
1486                                                                        t.getRotation().getQ0().getPartialDerivative(k)));
1487                 maxRotationDerivativeError = FastMath.max(maxRotationDerivativeError,
1488                                                           FastMath.abs(sign * refAngular[1].getPartialDerivative(0) -
1489                                                                        t.getRotation().getQ1().getPartialDerivative(k)));
1490                 maxRotationDerivativeError = FastMath.max(maxRotationDerivativeError,
1491                                                           FastMath.abs(sign * refAngular[2].getPartialDerivative(0) -
1492                                                                        t.getRotation().getQ2().getPartialDerivative(k)));
1493                 maxRotationDerivativeError = FastMath.max(maxRotationDerivativeError,
1494                                                           FastMath.abs(sign * refAngular[3].getPartialDerivative(0) -
1495                                                                        t.getRotation().getQ3().getPartialDerivative(k)));
1496 
1497                 // rotation rate
1498                 final Vector3D refRate  = new Vector3D(refAngular[4].getValue(), refAngular[5].getValue(), refAngular[6].getValue());
1499                 final Vector3D resRate  = t.getRotationRate().toVector3D();
1500                 final Vector3D refRateD = new Vector3D(refAngular[4].getPartialDerivative(0),
1501                                                        refAngular[5].getPartialDerivative(0),
1502                                                        refAngular[6].getPartialDerivative(0));
1503                 final Vector3D resRateD = new Vector3D(t.getRotationRate().getX().getPartialDerivative(k),
1504                                                        t.getRotationRate().getY().getPartialDerivative(k),
1505                                                        t.getRotationRate().getZ().getPartialDerivative(k));
1506                 maxRotationRateValueError      = FastMath.max(maxRotationRateValueError, Vector3D.distance(refRate, resRate));
1507                 maxRotationRateDerivativeError = FastMath.max(maxRotationRateDerivativeError, Vector3D.distance(refRateD, resRateD));
1508 
1509             }
1510 
1511         }
1512 
1513         if (maxRotationValueError          > toleranceRotationValue          ||
1514             maxRotationDerivativeError     > toleranceRotationDerivative     ||
1515             maxRotationRateValueError      > toleranceRotationRateValue      ||
1516             maxRotationRateDerivativeError > toleranceRotationRateDerivative) {
1517             print("toleranceRotationValue",          maxRotationValueError);
1518             print("toleranceRotationDerivative",     maxRotationDerivativeError);
1519             print("toleranceRotationRateValue",      maxRotationRateValueError);
1520             print("toleranceRotationRateDerivative", maxRotationRateDerivativeError);
1521         }
1522         Assert.assertEquals(0.0, maxRotationValueError,           toleranceRotationValue);
1523         Assert.assertEquals(0.0, maxRotationDerivativeError,      toleranceRotationDerivative);
1524         Assert.assertEquals(0.0, maxRotationRateValueError,       toleranceRotationRateValue);
1525         Assert.assertEquals(0.0, maxRotationRateDerivativeError,  toleranceRotationRateDerivative);
1526 
1527     }
1528     private void print(String name, double v) {
1529         if (v < Precision.SAFE_MIN) {
1530             System.out.format(Locale.US, "            double %-35s =  Precision.SAFE_MIN;%n", name);
1531         } else {
1532             double s = FastMath.pow(10.0, 1 - FastMath.floor(FastMath.log(v) / FastMath.log(10.0)));
1533             System.out.format(Locale.US, "            double %-35s = %8.1e;%n",
1534                               name, FastMath.ceil(s * v) / s);
1535         }
1536     }
1537 
1538     private UnivariateDifferentiableVectorFunction differentiatedStationPV(final GroundStation station,
1539                                                                            final Frame eme2000,
1540                                                                            final AbsoluteDate date,
1541                                                                            final ParameterDriver driver,
1542                                                                            final double stepFactor) {
1543 
1544         final FiniteDifferencesDifferentiator differentiator =
1545                         new FiniteDifferencesDifferentiator(5, stepFactor * driver.getScale());
1546 
1547         return differentiator.differentiate(new UnivariateVectorFunction() {
1548             @Override
1549             public double[] value(double x) {
1550                 final double[] result = new double[6];
1551                 try {
1552                     final double previouspI = driver.getValue();
1553                     driver.setValue(x);
1554                     Transform t = station.getOffsetToInertial(eme2000, date);
1555                     driver.setValue(previouspI);
1556                     PVCoordinates stationPV = t.transformPVCoordinates(PVCoordinates.ZERO);
1557                     result[ 0] = stationPV.getPosition().getX();
1558                     result[ 1] = stationPV.getPosition().getY();
1559                     result[ 2] = stationPV.getPosition().getZ();
1560                     result[ 3] = stationPV.getVelocity().getX();
1561                     result[ 4] = stationPV.getVelocity().getY();
1562                     result[ 5] = stationPV.getVelocity().getZ();
1563                 } catch (OrekitException oe) {
1564                     Assert.fail(oe.getLocalizedMessage());
1565                 }
1566                 return result;
1567             }
1568         });
1569     }
1570 
1571     private UnivariateDifferentiableVectorFunction differentiatedTransformAngular(final GroundStation station,
1572                                                                                   final Frame eme2000,
1573                                                                                   final AbsoluteDate date,
1574                                                                                   final ParameterDriver driver,
1575                                                                                   final double stepFactor) {
1576 
1577         final FiniteDifferencesDifferentiator differentiator =
1578                         new FiniteDifferencesDifferentiator(5, stepFactor * driver.getScale());
1579 
1580         return differentiator.differentiate(new UnivariateVectorFunction() {
1581             private double previous0 = Double.NaN;
1582             private double previous1 = Double.NaN;
1583             private double previous2 = Double.NaN;
1584             private double previous3 = Double.NaN;
1585             @Override
1586             public double[] value(double x) {
1587                 final double[] result = new double[7];
1588                 try {
1589                     final double previouspI = driver.getValue();
1590                     driver.setValue(x);
1591                     Transform t = station.getOffsetToInertial(eme2000, date);
1592                     driver.setValue(previouspI);
1593                     final double sign;
1594                     if (Double.isNaN(previous0)) {
1595                         sign = +1;
1596                     } else {
1597                         sign = FastMath.copySign(1.0,
1598                                                  previous0 * t.getRotation().getQ0() +
1599                                                  previous1 * t.getRotation().getQ1() +
1600                                                  previous2 * t.getRotation().getQ2() +
1601                                                  previous3 * t.getRotation().getQ3());
1602                     }
1603                     previous0 = sign * t.getRotation().getQ0();
1604                     previous1 = sign * t.getRotation().getQ1();
1605                     previous2 = sign * t.getRotation().getQ2();
1606                     previous3 = sign * t.getRotation().getQ3();
1607                     result[0] = previous0;
1608                     result[1] = previous1;
1609                     result[2] = previous2;
1610                     result[3] = previous3;
1611                     result[4] = t.getRotationRate().getX();
1612                     result[5] = t.getRotationRate().getY();
1613                     result[6] = t.getRotationRate().getZ();
1614                 } catch (OrekitException oe) {
1615                     Assert.fail(oe.getLocalizedMessage());
1616                 }
1617                 return result;
1618             }
1619         });
1620     }
1621 
1622     private ParameterDriver[] selectAllDrivers(final GroundStation station) {
1623         return new ParameterDriver[] {
1624             station.getPrimeMeridianOffsetDriver(),
1625             station.getPrimeMeridianDriftDriver(),
1626             station.getPolarOffsetXDriver(),
1627             station.getPolarDriftXDriver(),
1628             station.getPolarOffsetYDriver(),
1629             station.getPolarDriftYDriver(),
1630             station.getClockOffsetDriver(),
1631             station.getEastOffsetDriver(),
1632             station.getNorthOffsetDriver(),
1633             station.getZenithOffsetDriver()
1634         };
1635     }
1636 
1637 }
1638