1   /* Copyright 2002-2025 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.orbits;
18  
19  import java.text.MessageFormat;
20  import java.util.ArrayList;
21  import java.util.List;
22  import java.util.Locale;
23  
24  import org.hipparchus.analysis.polynomials.SmoothStepFactory;
25  import org.hipparchus.geometry.euclidean.threed.Vector3D;
26  import org.hipparchus.linear.RealMatrix;
27  import org.hipparchus.stat.descriptive.DescriptiveStatistics;
28  import org.hipparchus.util.FastMath;
29  import org.junit.jupiter.api.Assertions;
30  import org.junit.jupiter.api.BeforeAll;
31  import org.junit.jupiter.api.DisplayName;
32  import org.junit.jupiter.api.Test;
33  import org.mockito.Mockito;
34  import org.orekit.errors.OrekitIllegalArgumentException;
35  import org.orekit.errors.OrekitMessages;
36  import org.orekit.frames.Frame;
37  import org.orekit.frames.FramesFactory;
38  import org.orekit.propagation.MatricesHarvester;
39  import org.orekit.propagation.SpacecraftState;
40  import org.orekit.propagation.SpacecraftStateInterpolator;
41  import org.orekit.propagation.StateCovariance;
42  import org.orekit.propagation.StateCovarianceKeplerianHermiteInterpolatorTest;
43  import org.orekit.propagation.StateCovarianceMatrixProvider;
44  import org.orekit.propagation.analytical.AbstractAnalyticalPropagator;
45  import org.orekit.propagation.analytical.BrouwerLyddanePropagator;
46  import org.orekit.propagation.analytical.EcksteinHechlerPropagator;
47  import org.orekit.propagation.analytical.Ephemeris;
48  import org.orekit.propagation.analytical.KeplerianPropagator;
49  import org.orekit.propagation.numerical.NumericalPropagator;
50  import org.orekit.time.AbsoluteDate;
51  import org.orekit.time.AbstractTimeInterpolator;
52  import org.orekit.time.TimeInterpolator;
53  import org.orekit.utils.AbsolutePVCoordinatesTest;
54  import org.orekit.utils.Constants;
55  import org.orekit.utils.PVCoordinates;
56  
57  class OrbitBlenderTest {
58  
59      private static SpacecraftState sergeiState;
60      private static Orbit           sergeiOrbit;
61      private static Frame           sergeiFrame;
62      // Constants
63      private final  double          DEFAULT_SERGEI_PROPAGATION_TIME   = 2400;
64      private final  double          DEFAUTL_SERGEI_TABULATED_TIMESTEP = 2400;
65  
66      @BeforeAll
67      public static void setUp() {
68          StateCovarianceKeplerianHermiteInterpolatorTest.setUp();
69          sergeiState = StateCovarianceKeplerianHermiteInterpolatorTest.generateSergeiReferenceState();
70          sergeiOrbit = sergeiState.getOrbit();
71          sergeiFrame = sergeiOrbit.getFrame();
72      }
73  
74      public static void assertOrbit(final Orbit expected, final Orbit actual, final double epsilon) {
75          Assertions.assertEquals(expected.getMu(), actual.getMu());
76          Assertions.assertEquals(expected.getType(), actual.getType());
77          AbsolutePVCoordinatesTest.assertPV(expected.getPVCoordinates(expected.getFrame()),
78                                             actual.getPVCoordinates(expected.getFrame()),
79                                             epsilon);
80      }
81  
82      private DescriptiveStatistics[] computeStatisticsOrbitInterpolationOnSergeiCase(
83              final double propagationDuration,
84              final double tabulatedTimeStep,
85              final TimeInterpolator<SpacecraftState> stateInterpolator) {
86  
87          // Given
88          final List<SpacecraftState> referenceStates    = new ArrayList<>();
89          final List<SpacecraftState> tabulatedStates    = new ArrayList<>();
90          final List<SpacecraftState> interpolatedStates = new ArrayList<>();
91  
92          // Initialize reference state
93          final AbsoluteDate initialDate = sergeiOrbit.getDate();
94  
95          // Initialize reference covariance matrix
96          final RealMatrix sergeiCovarianceMatrix =
97                  StateCovarianceKeplerianHermiteInterpolatorTest.generateSergeiCovarianceMatrix();
98  
99          // Initialize propagator
100         final NumericalPropagator propagator = new NumericalPropagator(
101                 StateCovarianceKeplerianHermiteInterpolatorTest.generateDefaultIntegrator(sergeiOrbit, OrbitType.CARTESIAN));
102 
103         propagator.setOrbitType(OrbitType.CARTESIAN);
104 
105         // Initialize harvester
106         final MatricesHarvester harvester =
107                 propagator.setupMatricesComputation("harvester", null, null);
108 
109         // Initialize state covariance matrix provider
110         final StateCovariance sergeiCovariance =
111                 new StateCovariance(sergeiCovarianceMatrix, sergeiState.getDate(), sergeiState.getFrame(),
112                                     OrbitType.CARTESIAN, PositionAngleType.MEAN);
113 
114         final StateCovarianceMatrixProvider stateCovarianceMatrixProvider =
115                 new StateCovarianceMatrixProvider("covariance", "harvester", harvester, sergeiCovariance);
116 
117         // Configuring propagator
118         propagator.setInitialState(sergeiState);
119 
120         StateCovarianceKeplerianHermiteInterpolatorTest.configurePropagatorForSergeiCase(propagator);
121 
122         propagator.addAdditionalDataProvider(stateCovarianceMatrixProvider);
123 
124         propagator.getMultiplexer().add(1, (currentState) -> {
125             referenceStates.add(currentState);
126 
127             // Save tabulated state and covariance
128             final double durationFromStart = currentState.getDate().durationFrom(sergeiState.getDate());
129             if (durationFromStart % tabulatedTimeStep == 0) {
130                 tabulatedStates.add(currentState);
131             }
132         });
133 
134         // Propagation
135         propagator.propagate(initialDate.shiftedBy(propagationDuration));
136 
137         // Create custom Ephemeris
138         final Ephemeris ephemeris =
139                 new Ephemeris(tabulatedStates, stateInterpolator);
140 
141         // Interpolate
142         for (int dt = 0; dt < referenceStates.size(); dt++) {
143             final AbsoluteDate currentInterpolationDate = initialDate.shiftedBy(dt);
144 
145             interpolatedStates.add(ephemeris.propagate(currentInterpolationDate));
146         }
147 
148         // Make statistics
149         return computeRelativeError(referenceStates, interpolatedStates);
150     }
151 
152     private DescriptiveStatistics[] computeRelativeError(final List<SpacecraftState> referenceStates,
153                                                          final List<SpacecraftState> interpolatedStates) {
154 
155         final DescriptiveStatistics[] statistics =
156                 new DescriptiveStatistics[] { new DescriptiveStatistics(), new DescriptiveStatistics() };
157 
158         final List<PVCoordinates> relativePV = new ArrayList<>();
159         for (int i = 0; i < referenceStates.size(); i++) {
160             final PVCoordinates currentReferencePV    = referenceStates.get(i).getPVCoordinates();
161             final PVCoordinates currentInterpolatedPV = interpolatedStates.get(i).getPVCoordinates();
162 
163             relativePV.add(new PVCoordinates(currentReferencePV, currentInterpolatedPV));
164 
165             // Add position error norm
166             statistics[0].addValue(
167                     100 * relativePV.get(i).getPosition().getNorm() / currentReferencePV.getPosition().getNorm());
168 
169             // Add velocity error norm
170             statistics[1].addValue(
171                     100 * relativePV.get(i).getVelocity().getNorm() / currentReferencePV.getVelocity().getNorm());
172         }
173 
174         return statistics;
175     }
176 
177     private Orbit getDefaultOrbitAtDate(final AbsoluteDate date, final Frame inertialFrame) {
178         final double DEFAULT_MU   = Constants.IERS2010_EARTH_MU;
179         final double EARTH_RADIUS = Constants.IERS2010_EARTH_EQUATORIAL_RADIUS;
180 
181         final Vector3D      position = new Vector3D(EARTH_RADIUS + 4000000, 0, 0);
182         final Vector3D      velocity = new Vector3D(0, FastMath.sqrt(DEFAULT_MU / position.getNorm()), 0);
183         final PVCoordinates pv       = new PVCoordinates(position, velocity);
184 
185         return new CartesianOrbit(pv, inertialFrame, date, DEFAULT_MU);
186     }
187 
188     private void doTestInterpolation(final TimeInterpolator<SpacecraftState> stateInterpolator,
189                                      final double propagationHorizon,
190                                      final double tabulatedTimeStep,
191                                      final double expectedMeanRMSPositionError,
192                                      final double expectedMeanRMSVelocityError,
193                                      final double expectedMedianRMSPositionError,
194                                      final double expectedMedianRMSVelocityError,
195                                      final double expectedMaxRMSPositionError,
196                                      final double expectedMaxRMSVelocityError,
197                                      final double tolerance,
198                                      final boolean showResults) {
199         final DescriptiveStatistics[] statistics =
200                 computeStatisticsOrbitInterpolationOnSergeiCase(propagationHorizon, tabulatedTimeStep, stateInterpolator);
201 
202         // Then
203         if (showResults) {
204             System.out.format(Locale.US, "%.17f%n", statistics[0].getMean());
205             System.out.format(Locale.US, "%.17f%n", statistics[1].getMean());
206             System.out.format(Locale.US, "%.17f%n", statistics[0].getPercentile(50));
207             System.out.format(Locale.US, "%.17f%n", statistics[1].getPercentile(50));
208             System.out.format(Locale.US, "%.17f%n", statistics[0].getMax());
209             System.out.format(Locale.US, "%.17f%n", statistics[1].getMax());
210         }
211         Assertions.assertEquals(expectedMeanRMSPositionError, statistics[0].getMean(), tolerance);
212         Assertions.assertEquals(expectedMeanRMSVelocityError, statistics[1].getMean(), tolerance);
213         Assertions.assertEquals(expectedMedianRMSPositionError, statistics[0].getPercentile(50), tolerance);
214         Assertions.assertEquals(expectedMedianRMSVelocityError, statistics[1].getPercentile(50), tolerance);
215         Assertions.assertEquals(expectedMaxRMSPositionError, statistics[0].getMax(), tolerance);
216         Assertions.assertEquals(expectedMaxRMSVelocityError, statistics[1].getMax(), tolerance);
217     }
218 
219     @Test
220     @DisplayName("non regression test on Keplerian quadratic orbit blending on full force model test case from : "
221             + "TANYGIN, Sergei. Efficient covariance interpolation using blending of approximate covariance propagations. "
222             + "The Journal of the Astronautical Sciences, 2014, vol. 61, no 1, p. 107-132.")
223     void testKeplerianQuadraticBlendingOnSergeiCase() {
224         // Given
225         final SmoothStepFactory.SmoothStepFunction quadratic    = SmoothStepFactory.getQuadratic();
226         final AbstractAnalyticalPropagator         propagator   = new KeplerianPropagator(sergeiOrbit);
227         final OrbitBlender                         orbitBlender = new OrbitBlender(quadratic, propagator, sergeiFrame);
228 
229         final TimeInterpolator<SpacecraftState> stateInterpolator =
230                 new SpacecraftStateInterpolator(2, 1.0e-3, sergeiFrame, orbitBlender, null, null, null, null);
231 
232         // When & Then
233         doTestInterpolation(stateInterpolator, DEFAULT_SERGEI_PROPAGATION_TIME, DEFAUTL_SERGEI_TABULATED_TIMESTEP,
234                             0.05185755740700528,
235                             0.08169252246167892,
236                             0.05262772652596856,
237                             0.08349987869494085,
238                             0.10151652739088853,
239                             0.14827634525717634,
240                             1e-12, false);
241     }
242 
243     @SuppressWarnings("deprecation")
244     @Test
245     @DisplayName("non regression test on Keplerian quadratic orbit blending on full force model test case from : "
246             + "TANYGIN, Sergei. Efficient covariance interpolation using blending of approximate covariance propagations. "
247             + "The Journal of the Astronautical Sciences, 2014, vol. 61, no 1, p. 107-132.")
248     void testKeplerianQuadraticBlendingOnSergeiCaseDeprecated() {
249         // Given
250         final SmoothStepFactory.SmoothStepFunction quadratic    = SmoothStepFactory.getQuadratic();
251         final AbstractAnalyticalPropagator         propagator   = new KeplerianPropagator(sergeiOrbit);
252         final OrbitBlender                         orbitBlender = new OrbitBlender(quadratic, propagator, sergeiFrame);
253 
254         final TimeInterpolator<SpacecraftState> stateInterpolator =
255                 new SpacecraftStateInterpolator(AbstractTimeInterpolator.DEFAULT_INTERPOLATION_POINTS,
256                                                 AbstractTimeInterpolator.DEFAULT_EXTRAPOLATION_THRESHOLD_SEC,
257                                                 sergeiFrame, orbitBlender, null, null, null, null);
258 
259         // When & Then
260         doTestInterpolation(stateInterpolator, DEFAULT_SERGEI_PROPAGATION_TIME, DEFAUTL_SERGEI_TABULATED_TIMESTEP,
261                             0.05185755740700528,
262                             0.08169252246167892,
263                             0.05262772652596856,
264                             0.08349987869494085,
265                             0.10151652739088853,
266                             0.14827634525717634,
267                             1e-12, false);
268     }
269 
270     @Test
271     @DisplayName("non regression test on Brouwer-Lyddane quadratic orbit blending on full force model test case from : "
272             + "TANYGIN, Sergei. Efficient covariance interpolation using blending of approximate covariance propagations. "
273             + "The Journal of the Astronautical Sciences, 2014, vol. 61, no 1, p. 107-132.")
274     void testBrouwerLyddaneQuadraticBlendingOnSergeiCase() {
275         // Given
276         final SpacecraftState sergeiState = StateCovarianceKeplerianHermiteInterpolatorTest.generateSergeiReferenceState();
277         final Frame           sergeiFrame = sergeiState.getFrame();
278 
279         final SmoothStepFactory.SmoothStepFunction quadratic = SmoothStepFactory.getQuadratic();
280         final AbstractAnalyticalPropagator propagator =
281                 new BrouwerLyddanePropagator(sergeiState.getOrbit(), sergeiState.getMass(),
282                                              Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS,
283                                              Constants.EIGEN5C_EARTH_MU, Constants.EIGEN5C_EARTH_C20,
284                                              Constants.EIGEN5C_EARTH_C30, Constants.EIGEN5C_EARTH_C40,
285                                              Constants.EIGEN5C_EARTH_C50, 0);
286         final OrbitBlender orbitBlender = new OrbitBlender(quadratic, propagator, sergeiFrame);
287 
288         final TimeInterpolator<SpacecraftState> stateInterpolator =
289                 new SpacecraftStateInterpolator(2, 1.0e-3, sergeiFrame, orbitBlender, null, null, null, null);
290 
291         // When & Then
292         doTestInterpolation(stateInterpolator, DEFAULT_SERGEI_PROPAGATION_TIME, DEFAUTL_SERGEI_TABULATED_TIMESTEP,
293                             0.1162978884760,
294                             0.0588245986334,
295                             0.1184075880169,
296                             0.0640733000793,
297                             0.2095374397997,
298                             0.0901515566892,
299                             1e-13, false);
300     }
301 
302     @SuppressWarnings("deprecation")
303     @Test
304     @DisplayName("non regression test on Brouwer-Lyddane quadratic orbit blending on full force model test case from : "
305             + "TANYGIN, Sergei. Efficient covariance interpolation using blending of approximate covariance propagations. "
306             + "The Journal of the Astronautical Sciences, 2014, vol. 61, no 1, p. 107-132.")
307     void testBrouwerLyddaneQuadraticBlendingOnSergeiCaseDeprecated() {
308         // Given
309         final SpacecraftState sergeiState = StateCovarianceKeplerianHermiteInterpolatorTest.generateSergeiReferenceState();
310         final Frame           sergeiFrame = sergeiState.getFrame();
311 
312         final SmoothStepFactory.SmoothStepFunction quadratic = SmoothStepFactory.getQuadratic();
313         final AbstractAnalyticalPropagator propagator =
314                 new BrouwerLyddanePropagator(sergeiState.getOrbit(), sergeiState.getMass(),
315                                              Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS,
316                                              Constants.EIGEN5C_EARTH_MU, Constants.EIGEN5C_EARTH_C20,
317                                              Constants.EIGEN5C_EARTH_C30, Constants.EIGEN5C_EARTH_C40,
318                                              Constants.EIGEN5C_EARTH_C50, 0);
319         final OrbitBlender orbitBlender = new OrbitBlender(quadratic, propagator, sergeiFrame);
320 
321         final TimeInterpolator<SpacecraftState> stateInterpolator =
322                 new SpacecraftStateInterpolator(AbstractTimeInterpolator.DEFAULT_INTERPOLATION_POINTS,
323                                                 AbstractTimeInterpolator.DEFAULT_EXTRAPOLATION_THRESHOLD_SEC,
324                                                 sergeiFrame, orbitBlender, null, null, null, null);
325 
326         // When & Then
327         doTestInterpolation(stateInterpolator, DEFAULT_SERGEI_PROPAGATION_TIME, DEFAUTL_SERGEI_TABULATED_TIMESTEP,
328                             0.1162978884760,
329                             0.0588245986334,
330                             0.1184075880169,
331                             0.0640733000793,
332                             0.2095374397997,
333                             0.0901515566892,
334                             1e-13, false);
335     }
336 
337     @Test
338     @DisplayName("non regression test on Eckstein-Hechler quadratic orbit blending on full force model test case from : "
339             + "TANYGIN, Sergei. Efficient covariance interpolation using blending of approximate covariance propagations. "
340             + "The Journal of the Astronautical Sciences, 2014, vol. 61, no 1, p. 107-132.")
341     void testEcksteinHechlerQuadraticBlendingOnSergeiCase() {
342         // Given
343         final SmoothStepFactory.SmoothStepFunction quadratic = SmoothStepFactory.getQuadratic();
344         final AbstractAnalyticalPropagator propagator =
345                 new EcksteinHechlerPropagator(sergeiState.getOrbit(), sergeiState.getMass(),
346                                               Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS,
347                                               Constants.EIGEN5C_EARTH_MU, Constants.EIGEN5C_EARTH_C20,
348                                               Constants.EIGEN5C_EARTH_C30, Constants.EIGEN5C_EARTH_C40,
349                                               Constants.EIGEN5C_EARTH_C50, Constants.EIGEN5C_EARTH_C60);
350         final OrbitBlender orbitBlender = new OrbitBlender(quadratic, propagator, sergeiFrame);
351 
352         final TimeInterpolator<SpacecraftState> stateInterpolator =
353                 new SpacecraftStateInterpolator(2, 1.0e-3, sergeiFrame, orbitBlender, null, null, null, null);
354 
355         // When & Then
356         doTestInterpolation(stateInterpolator, DEFAULT_SERGEI_PROPAGATION_TIME, DEFAUTL_SERGEI_TABULATED_TIMESTEP,
357                             0.00854503692718802,
358                             0.01192593186465725,
359                             0.00895077301312331,
360                             0.01299681287562801,
361                             0.01600030634898298,
362                             0.01743228685360753,
363                             1e-17, false);
364                             
365     }
366 
367     @SuppressWarnings("deprecation")
368     @Test
369     @DisplayName("non regression test on Eckstein-Hechler quadratic orbit blending on full force model test case from : "
370             + "TANYGIN, Sergei. Efficient covariance interpolation using blending of approximate covariance propagations. "
371             + "The Journal of the Astronautical Sciences, 2014, vol. 61, no 1, p. 107-132.")
372     void testEcksteinHechlerQuadraticBlendingOnSergeiCaseDeprecated() {
373         // Given
374         final SmoothStepFactory.SmoothStepFunction quadratic = SmoothStepFactory.getQuadratic();
375         final AbstractAnalyticalPropagator propagator =
376                 new EcksteinHechlerPropagator(sergeiState.getOrbit(), sergeiState.getMass(),
377                                               Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS,
378                                               Constants.EIGEN5C_EARTH_MU, Constants.EIGEN5C_EARTH_C20,
379                                               Constants.EIGEN5C_EARTH_C30, Constants.EIGEN5C_EARTH_C40,
380                                               Constants.EIGEN5C_EARTH_C50, Constants.EIGEN5C_EARTH_C60);
381         final OrbitBlender orbitBlender = new OrbitBlender(quadratic, propagator, sergeiFrame);
382 
383         final TimeInterpolator<SpacecraftState> stateInterpolator =
384                 new SpacecraftStateInterpolator(AbstractTimeInterpolator.DEFAULT_INTERPOLATION_POINTS,
385                                                 AbstractTimeInterpolator.DEFAULT_EXTRAPOLATION_THRESHOLD_SEC,
386                                                 sergeiFrame, orbitBlender, null, null, null, null);
387 
388         // When & Then
389         doTestInterpolation(stateInterpolator, DEFAULT_SERGEI_PROPAGATION_TIME, DEFAUTL_SERGEI_TABULATED_TIMESTEP,
390                             0.00854503692718802,
391                             0.01192593186465725,
392                             0.00895077301312331,
393                             0.01299681287562801,
394                             0.01600030634898298,
395                             0.01743228685360753,
396                             1e-17, false);
397     }
398 
399     @Test
400     @DisplayName("test blending case with Keplerian dynamic (exact results expected)")
401     void testBlendingWithKeplerianDynamic() {
402         // Given
403         final Frame orbitFrame  = FramesFactory.getEME2000();
404         final Frame outputFrame = FramesFactory.getGCRF();
405 
406         final AbsoluteDate previousTabulatedDate = new AbsoluteDate();
407         final AbsoluteDate nextTabulatedDate     = previousTabulatedDate.shiftedBy(3600);
408 
409         final AbsoluteDate interpolationDate1 = previousTabulatedDate.shiftedBy(1200);
410         final AbsoluteDate interpolationDate2 = previousTabulatedDate.shiftedBy(1500);
411         final AbsoluteDate interpolationDate3 = previousTabulatedDate.shiftedBy(2000);
412 
413         final Orbit                        previousTabulatedOrbit = getDefaultOrbitAtDate(previousTabulatedDate, orbitFrame);
414         final AbstractAnalyticalPropagator propagator             = new KeplerianPropagator(previousTabulatedOrbit);
415 
416         final Orbit       nextTabulatedOrbit = propagator.propagate(nextTabulatedDate).getOrbit();
417         final List<Orbit> orbitSample        = new ArrayList<>();
418         orbitSample.add(previousTabulatedOrbit);
419         orbitSample.add(nextTabulatedOrbit);
420 
421         final SmoothStepFactory.SmoothStepFunction blendingFunction = SmoothStepFactory.getQuadratic();
422         final TimeInterpolator<Orbit> orbitBlender = new OrbitBlender(blendingFunction, propagator, outputFrame);
423 
424         // When & Then
425         final double epsilon = 1e-8; // 10 nm
426 
427         assertOrbit(propagator.propagate(interpolationDate1).getOrbit(),
428                     orbitBlender.interpolate(interpolationDate1, orbitSample), epsilon);
429         assertOrbit(propagator.propagate(interpolationDate2).getOrbit(),
430                     orbitBlender.interpolate(interpolationDate2, orbitSample), epsilon);
431         assertOrbit(propagator.propagate(interpolationDate3).getOrbit(),
432                     orbitBlender.interpolate(interpolationDate3, orbitSample), epsilon);
433 
434     }
435 
436     @Test
437     @DisplayName("test specific case (blending at tabulated date)")
438     void testBlendingAtTabulatedDate() {
439         // Given
440         final Frame orbitFrame  = FramesFactory.getEME2000();
441         final Frame outputFrame = FramesFactory.getGCRF();
442 
443         final AbsoluteDate previousTabulatedDate = new AbsoluteDate();
444         final AbsoluteDate nextTabulatedDate     = previousTabulatedDate.shiftedBy(3600);
445 
446         final Orbit       previousTabulatedOrbit = getDefaultOrbitAtDate(previousTabulatedDate, orbitFrame);
447         final Orbit       nextTabulatedOrbit     = getDefaultOrbitAtDate(nextTabulatedDate, orbitFrame);
448         final List<Orbit> orbitSample            = new ArrayList<>();
449         orbitSample.add(previousTabulatedOrbit);
450         orbitSample.add(nextTabulatedOrbit);
451 
452         final SmoothStepFactory.SmoothStepFunction blendingFunction = SmoothStepFactory.getQuadratic();
453         final OrbitBlender orbitBlender = new OrbitBlender(blendingFunction,
454                                                            new KeplerianPropagator(previousTabulatedOrbit),
455                                                            outputFrame);
456 
457         // When
458         final Orbit blendedOrbit = orbitBlender.interpolate(previousTabulatedDate, orbitSample);
459 
460         // Then
461         assertOrbit(previousTabulatedOrbit, blendedOrbit, 1e-11);
462         Assertions.assertEquals(outputFrame, orbitBlender.getOutputInertialFrame());
463     }
464 
465     @Test
466     @DisplayName("Test error thrown when using non inertial frame")
467     void testErrorThrownWhenUsingNonInertialFrame() {
468         // Given
469         final SmoothStepFactory.SmoothStepFunction blendingFunctionMock = Mockito.mock(
470               SmoothStepFactory.SmoothStepFunction.class);
471 
472         final AbstractAnalyticalPropagator propagatorMock =
473               Mockito.mock(AbstractAnalyticalPropagator.class);
474 
475         final Frame nonInertialFrame = Mockito.mock(Frame.class);
476         final String frameName = "frameName";
477         Mockito.when(nonInertialFrame.isPseudoInertial()).thenReturn(false);
478         Mockito.when(nonInertialFrame.getName()).thenReturn(frameName);
479 
480         // When & Then
481         Exception thrown = Assertions.assertThrows(OrekitIllegalArgumentException.class,
482                                                    () -> new OrbitBlender(blendingFunctionMock,
483                                                                           propagatorMock,
484                                                                           nonInertialFrame));
485 
486         Assertions.assertEquals(MessageFormat.format(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME.getSourceString(), frameName),
487                                 thrown.getMessage());
488     }
489 
490     @Test
491     @DisplayName("Test error thrown when using orbits with different gravitational parameters")
492     void testErrorThrownWhenUsingOrbitsWithDifferentMus() {
493         // Given
494 
495         // Create blender
496         final SmoothStepFactory.SmoothStepFunction blendingFunctionMock = Mockito.mock(
497               SmoothStepFactory.SmoothStepFunction.class);
498 
499         final AbstractAnalyticalPropagator propagatorMock =
500               Mockito.mock(AbstractAnalyticalPropagator.class);
501 
502         final Frame inertialFrame = Mockito.mock(Frame.class);
503         Mockito.when(inertialFrame.isPseudoInertial()).thenReturn(true);
504 
505         final OrbitBlender interpolator = new OrbitBlender(blendingFunctionMock, propagatorMock, inertialFrame);
506 
507         // Create sample
508         final List<Orbit> sample = new ArrayList<>();
509 
510         final Orbit orbit1Mock = Mockito.mock(Orbit.class);
511         final Orbit orbit2Mock = Mockito.mock(Orbit.class);
512 
513         final double firstMu  = 1.;
514         final double secondMu = 2.;
515 
516         Mockito.when(orbit1Mock.getMu()).thenReturn(firstMu);
517         Mockito.when(orbit2Mock.getMu()).thenReturn(secondMu);
518 
519         sample.add(orbit1Mock);
520         sample.add(orbit2Mock);
521 
522         // When & Then
523         Exception thrown = Assertions.assertThrows(OrekitIllegalArgumentException.class,
524                                                    () -> interpolator.interpolate(new AbsoluteDate(), sample));
525 
526         Assertions.assertEquals(MessageFormat.format(OrekitMessages.ORBITS_MUS_MISMATCH.getSourceString(), firstMu, secondMu),
527                                 thrown.getMessage());
528     }
529 
530 }