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.propagation.analytical.gnss;
18  
19  import org.hipparchus.geometry.euclidean.threed.Vector3D;
20  import org.hipparchus.util.FastMath;
21  import org.hipparchus.util.Precision;
22  import org.junit.jupiter.api.Assertions;
23  import org.junit.jupiter.api.BeforeAll;
24  import org.junit.jupiter.api.Test;
25  import org.orekit.TestUtils;
26  import org.orekit.Utils;
27  import org.orekit.data.DataContext;
28  import org.orekit.errors.OrekitException;
29  import org.orekit.errors.OrekitMessages;
30  import org.orekit.frames.Frame;
31  import org.orekit.frames.FramesFactory;
32  import org.orekit.propagation.AdditionalDataProvider;
33  import org.orekit.propagation.analytical.gnss.data.GLONASSAlmanac;
34  import org.orekit.propagation.analytical.gnss.data.GLONASSOrbitalElements;
35  import org.orekit.propagation.analytical.gnss.data.GNSSConstants;
36  import org.orekit.time.AbsoluteDate;
37  import org.orekit.time.DateComponents;
38  import org.orekit.time.GLONASSDate;
39  import org.orekit.time.TimeComponents;
40  import org.orekit.time.TimeInterpolator;
41  import org.orekit.time.TimeScalesFactory;
42  import org.orekit.utils.CartesianDerivativesFilter;
43  import org.orekit.utils.Constants;
44  import org.orekit.utils.IERSConventions;
45  import org.orekit.utils.PVCoordinates;
46  import org.orekit.utils.TimeStampedPVCoordinates;
47  import org.orekit.utils.TimeStampedPVCoordinatesHermiteInterpolator;
48  
49  import java.util.ArrayList;
50  import java.util.List;
51  
52  public class GLONASSAnalyticalPropagatorTest {
53  
54      private static GLONASSAlmanac almanac;
55  
56      @BeforeAll
57      public static void setUpBeforeClass() {
58          Utils.setDataRoot("gnss");
59          // Reference values for validation are given into Glonass Interface Control Document v1.0 2016
60          final double pi = GNSSConstants.GLONASS_PI;
61          almanac = new GLONASSAlmanac(0, 1, 22, 12, 2007, 33571.625,
62                                       -0.293967247009277 * pi,
63                                       -0.00012947082519531 * pi,
64                                       0.57867431640625 * pi,
65                                       0.000432968139648438,
66                                       0.01953124999975,
67                                       6.103515625e-5,
68                                       0.0, 0.0, 0.0);
69      }
70  
71      @Test
72      public void testPerfectValues() {
73          // Build the propagator
74          final GLONASSAnalyticalPropagator propagator = almanac.getPropagator(DataContext.getDefault(), Utils.defaultLaw(),
75                                                                               FramesFactory.getEME2000(),
76                                                                               FramesFactory.getITRF(IERSConventions.IERS_2010, false), 1521.0);
77  
78          // Target
79          final AbsoluteDate target = new AbsoluteDate(new DateComponents(2007, 12, 23),
80                                                       new TimeComponents(51300),
81                                                       TimeScalesFactory.getGLONASS());
82  
83          Assertions.assertEquals(0.0, almanac.getGlo2UTC(), Precision.SAFE_MIN);
84          Assertions.assertEquals(0.0, almanac.getGloOffset(), Precision.SAFE_MIN);
85          Assertions.assertEquals(0.0, almanac.getGPS2Glo(), Precision.SAFE_MIN);
86          Assertions.assertEquals(1,   almanac.getHealth());
87          Assertions.assertEquals(0,   almanac.getFrequencyChannel());
88  
89          // Compute PVCoordinates at the date in the ECEF
90          final PVCoordinates pvFinal    = propagator.propagateInEcef(target);
91  
92          // Reference values (see GLONASS ICD)
93          final PVCoordinates pvExpected = new PVCoordinates(new Vector3D(10697116.4874360654,
94                                                                          21058292.4241863210,
95                                                                          -9635679.33963303405),
96                                                             new Vector3D(-0686.100809921691084,
97                                                                          -1136.54864124521881,
98                                                                          -3249.98587740305799));
99  
100         // Check
101         Assertions.assertEquals(1521.0, propagator.getMass(target), 0.1);
102         Assertions.assertEquals(0.0, pvFinal.getPosition().distance(pvExpected.getPosition()), 1.1e-7);
103         Assertions.assertEquals(0.0, pvFinal.getVelocity().distance(pvExpected.getVelocity()), 1.1e-5);
104 
105         // Get PVCoordinates at the date in the ECEF
106         final PVCoordinates pvFinal2 = propagator.getPVCoordinates(target, propagator.getECEF());
107         Assertions.assertEquals(0., pvFinal.getPosition().distance(pvFinal2.getPosition()), 1.9e-8);
108     }
109 
110     @Test
111     public void testFrames() {
112         // Builds the GLONASSAnalyticalPropagator from the almanac
113         final GLONASSAnalyticalPropagator propagator = almanac.getPropagator();
114         Assertions.assertEquals("EME2000", propagator.getFrame().getName());
115         Assertions.assertEquals("EME2000", propagator.getECI().getName());
116         Assertions.assertEquals(3.986004418e+14, GNSSConstants.GLONASS_MU, 1.0e6);
117         // Defines some date
118         final AbsoluteDate date = new AbsoluteDate(2016, 3, 3, 12, 0, 0., TimeScalesFactory.getUTC());
119         // Get PVCoordinates at the date in the ECEF
120         final PVCoordinates pv0 = propagator.propagateInEcef(date);
121         // Get PVCoordinates at the date in the ECEF
122         final PVCoordinates pv1 = propagator.getPVCoordinates(date, propagator.getECEF());
123 
124         // Checks
125         Assertions.assertEquals(0., pv0.getPosition().distance(pv1.getPosition()), 2.6e-8);
126         Assertions.assertEquals(0., pv0.getVelocity().distance(pv1.getVelocity()), 2.8e-12);
127     }
128 
129     @Test
130     public void testDerivativesConsistency() {
131 
132         final Frame eme2000 = FramesFactory.getEME2000();
133         double errorP = 0;
134         double errorV = 0;
135         double errorA = 0;
136         final GLONASSAnalyticalPropagator propagator = almanac.getPropagator();
137         GLONASSOrbitalElements elements = propagator.getGLONASSOrbitalElements();
138         AbsoluteDate t0 = new GLONASSDate(elements.getNa(), elements.getN4(), elements.getTime()).getDate();
139         for (double dt = 0; dt < Constants.JULIAN_DAY; dt += 600) {
140             final AbsoluteDate central = t0.shiftedBy(dt);
141             final PVCoordinates pv = propagator.getPVCoordinates(central, eme2000);
142             final double h = 10.0;
143             List<TimeStampedPVCoordinates> sample = new ArrayList<TimeStampedPVCoordinates>();
144             for (int i = -3; i <= 3; ++i) {
145                 sample.add(propagator.getPVCoordinates(central.shiftedBy(i * h), eme2000));
146             }
147 
148             // create interpolator
149             final TimeInterpolator<TimeStampedPVCoordinates> interpolator =
150                     new TimeStampedPVCoordinatesHermiteInterpolator(sample.size(), CartesianDerivativesFilter.USE_P);
151 
152             final PVCoordinates interpolated = interpolator.interpolate(central, sample);
153             errorP = FastMath.max(errorP, Vector3D.distance(pv.getPosition(), interpolated.getPosition()));
154             errorV = FastMath.max(errorV, Vector3D.distance(pv.getVelocity(), interpolated.getVelocity()));
155             errorA = FastMath.max(errorA, Vector3D.distance(pv.getAcceleration(), interpolated.getAcceleration()));
156         }
157         Assertions.assertEquals(0.0, errorP, 1.9e-9);
158         Assertions.assertEquals(0.0, errorV, 3.2e-3);
159         Assertions.assertEquals(0.0, errorA, 7.0e-4);
160 
161     }
162 
163     @Test
164     public void testNoReset() {
165         try {
166             final GLONASSAnalyticalPropagator propagator = almanac.getPropagator();
167             propagator.resetInitialState(propagator.getInitialState());
168             Assertions.fail("an exception should have been thrown");
169         } catch (OrekitException oe) {
170             Assertions.assertEquals(OrekitMessages.NON_RESETABLE_STATE, oe.getSpecifier());
171         }
172         try {
173             GLONASSAnalyticalPropagator propagator = new GLONASSAnalyticalPropagatorBuilder(almanac).build();
174             propagator.resetIntermediateState(propagator.getInitialState(), true);
175             Assertions.fail("an exception should have been thrown");
176         } catch (OrekitException oe) {
177             Assertions.assertEquals(OrekitMessages.NON_RESETABLE_STATE, oe.getSpecifier());
178         }
179     }
180 
181     @Test
182     public void testIssue544() {
183         // Builds the GLONASSAnalyticalPropagator from the almanac
184         final GLONASSAnalyticalPropagator propagator = almanac.getPropagator(DataContext.getDefault());
185         // In order to test the issue, we volontary set a Double.NaN value in the date.
186         final AbsoluteDate date0 = new AbsoluteDate(2010, 5, 7, 7, 50, Double.NaN, TimeScalesFactory.getUTC());
187         final PVCoordinates pv0 = propagator.propagateInEcef(date0);
188         // Verify that an infinite loop did not occur
189         Assertions.assertEquals(Vector3D.NaN, pv0.getPosition());
190         Assertions.assertEquals(Vector3D.NaN, pv0.getVelocity());
191 
192     }
193 
194     /** Error with specific propagators & additional state provider throwing a NullPointerException when propagating */
195     @Test
196     public void testIssue949() {
197         // GIVEN
198         // Setup propagator
199         final GLONASSAnalyticalPropagator propagator = almanac.getPropagator();
200 
201         // Setup additional data provider which use the initial state in its init method
202         final AdditionalDataProvider<double[]> additionalDataProvider = TestUtils.getAdditionalProviderWithInit();
203         propagator.addAdditionalDataProvider(additionalDataProvider);
204 
205         // WHEN & THEN
206         Assertions.assertDoesNotThrow(() -> propagator.propagate(new AbsoluteDate()), "No error should have been thrown");
207 
208     }
209 
210 }