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.numerical;
18  
19  import java.lang.reflect.InvocationTargetException;
20  import java.lang.reflect.Method;
21  
22  import org.hipparchus.CalculusFieldElement;
23  import org.hipparchus.Field;
24  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
25  import org.hipparchus.geometry.euclidean.threed.Vector3D;
26  import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaIntegrator;
27  import org.hipparchus.util.Binary64Field;
28  import org.hipparchus.util.Precision;
29  import org.junit.jupiter.api.Assertions;
30  import org.junit.jupiter.api.BeforeAll;
31  import org.junit.jupiter.api.Test;
32  import org.orekit.Utils;
33  import org.orekit.frames.Frame;
34  import org.orekit.frames.FramesFactory;
35  import org.orekit.frames.ITRFVersion;
36  import org.orekit.propagation.PropagationType;
37  import org.orekit.propagation.SpacecraftState;
38  import org.orekit.propagation.analytical.gnss.data.GLONASSEphemeris;
39  import org.orekit.propagation.analytical.gnss.data.GLONASSNavigationMessage;
40  import org.orekit.propagation.analytical.gnss.data.GLONASSOrbitalElements;
41  import org.orekit.time.AbsoluteDate;
42  import org.orekit.time.DateComponents;
43  import org.orekit.time.GLONASSDate;
44  import org.orekit.time.TimeComponents;
45  import org.orekit.time.TimeScalesFactory;
46  import org.orekit.utils.IERSConventions;
47  import org.orekit.utils.PVCoordinates;
48  
49  public class GLONASSNumericalPropagatorTest {
50  
51      private static GLONASSEphemeris ephemeris;
52  
53      @BeforeAll
54      public static void setUpBeforeClass() {
55          Utils.setDataRoot("regular-data");
56          // Reference values for validation are given into Glonass Interface Control Document v1.0 2016
57          ephemeris = new GLONASSEphemeris(5, 251, 11700,
58                                           7003008.789,
59                                           783.5417,
60                                           0.0,
61                                           -12206626.953,
62                                           2804.2530,
63                                           0.0,
64                                           21280765.625,
65                                           1352.5150,
66                                           0.0);
67      }
68  
69      @Test
70      public void testPerfectValues() {
71  
72          // 4th order Runge-Kutta
73          final ClassicalRungeKuttaIntegrator integrator = new ClassicalRungeKuttaIntegrator(10.);
74  
75          // Initialize the propagator
76          final GLONASSNumericalPropagator propagator = new GLONASSNumericalPropagatorBuilder(integrator, ephemeris, false).
77                          attitudeProvider(Utils.defaultLaw()).
78                          mass(1521.0).
79                          eci(FramesFactory.getEME2000()).
80                          build();
81  
82          // Target date
83          final AbsoluteDate target = new AbsoluteDate(new DateComponents(2012, 9, 7),
84                                                       new TimeComponents(12300),
85                                                       TimeScalesFactory.getGLONASS());
86  
87          // Initial verifications
88          final GLONASSOrbitalElements poe = propagator.getGLONASSOrbitalElements();
89          Assertions.assertEquals(0.0, poe.getXDotDot(), Precision.SAFE_MIN);
90          Assertions.assertEquals(0.0, poe.getYDotDot(), Precision.SAFE_MIN);
91          Assertions.assertEquals(0.0, poe.getZDotDot(), Precision.SAFE_MIN);
92          Assertions.assertEquals(5,   poe.getN4());
93          Assertions.assertEquals(251, poe.getNa());
94  
95          // Propagation
96          final SpacecraftState finalState = propagator.propagate(target);
97          final PVCoordinates pvFinal = finalState.getPVCoordinates(FramesFactory.getPZ9011(IERSConventions.IERS_2010, true));
98  
99          // Expected outputs in PZ90.11 frame
100         final Vector3D expectedPosition = new Vector3D(7523174.819, -10506961.965, 21999239.413);
101         final Vector3D expectedVelocity = new Vector3D(950.126007, 2855.687825, 1040.679862);
102 
103         // Computed outputs
104         final Vector3D computedPosition = pvFinal.getPosition();
105         final Vector3D computedVelocity = pvFinal.getVelocity();
106 
107         Assertions.assertEquals(0.0, computedPosition.distance(expectedPosition), 1.1e-3);
108         Assertions.assertEquals(0.0, computedVelocity.distance(expectedVelocity), 3.3e-6);
109     }
110 
111     @Test
112     public void testFromITRF2008ToPZ90() {
113         // Reference for the test
114         // "PARAMETRY ZEMLI 1990" (PZ-90.11) Reference Document
115         //  MILITARY TOPOGRAPHIC DEPARTMENT OF THE GENERAL STAFF OF ARMED FORCES OF THE RUSSIAN FEDERATION, Moscow, 2014"
116 
117         // Position in ITRF-2008
118         final Vector3D itrf2008P = new Vector3D(2845455.9753, 2160954.3073, 5265993.2656);
119 
120         // Ref position in PZ-90.11
121         final Vector3D refPZ90   = new Vector3D(2845455.9772, 2160954.3078, 5265993.2664);
122 
123         // Recomputed position in PZ-90.11
124         final Frame pz90 = FramesFactory.getPZ9011(IERSConventions.IERS_2010, true);
125         final Frame itrf2008 = FramesFactory.getITRF(ITRFVersion.ITRF_2008, IERSConventions.IERS_2010, true);
126         final Vector3D comPZ90 = itrf2008.getStaticTransformTo(pz90, new AbsoluteDate(2010, 1, 1, 12, 0, 0, TimeScalesFactory.getTT())).transformPosition(itrf2008P);
127 
128         // Check
129         Assertions.assertEquals(refPZ90.getX(), comPZ90.getX(), 1.0e-4);
130         Assertions.assertEquals(refPZ90.getY(), comPZ90.getY(), 1.0e-4);
131         Assertions.assertEquals(refPZ90.getZ(), comPZ90.getZ(), 1.0e-4);
132     }
133 
134     @Test
135     public void testFromITRF2008ToPZ90Field() {
136         doTestFromITRF2008ToPZ90Field(Binary64Field.getInstance());
137     }
138 
139     private <T extends CalculusFieldElement<T>> void doTestFromITRF2008ToPZ90Field(final Field<T> field)  {
140         // Reference for the test
141         // "PARAMETRY ZEMLI 1990" (PZ-90.11) Reference Document
142         //  MILITARY TOPOGRAPHIC DEPARTMENT OF THE GENERAL STAFF OF ARMED FORCES OF THE RUSSIAN FEDERATION, Moscow, 2014"
143 
144         // Position in ITRF-2008
145         final FieldVector3D<T> itrf2008P = new FieldVector3D<>(field,
146                         new Vector3D(2845455.9753, 2160954.3073, 5265993.2656));
147 
148         // Ref position in PZ-90.11
149         final FieldVector3D<T> refPZ90   = new FieldVector3D<>(field,
150                         new Vector3D(2845455.9772, 2160954.3078, 5265993.2664));
151 
152         // Recomputed position in PZ-90.11
153         final Frame pz90 = FramesFactory.getPZ9011(IERSConventions.IERS_2010, true);
154         final Frame itrf2008 = FramesFactory.getITRF(ITRFVersion.ITRF_2008, IERSConventions.IERS_2010, true);
155         final FieldVector3D<T> comPZ90 = itrf2008.getStaticTransformTo(pz90, new AbsoluteDate(2010, 1, 1, 12, 0, 0, TimeScalesFactory.getTT())).transformPosition(itrf2008P);
156 
157         // Check
158         Assertions.assertEquals(refPZ90.getX().getReal(), comPZ90.getX().getReal(), 1.0e-4);
159         Assertions.assertEquals(refPZ90.getY().getReal(), comPZ90.getY().getReal(), 1.0e-4);
160         Assertions.assertEquals(refPZ90.getZ().getReal(), comPZ90.getZ().getReal(), 1.0e-4);
161     }
162 
163     @Test
164     public void testPosition() {
165         // Frames
166         final Frame pz90 = FramesFactory.getPZ9011(IERSConventions.IERS_2010, true);
167         final Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true);
168         // Initial GLONASS orbital elements (Ref: IGS)
169         final GLONASSNavigationMessage ge = new GLONASSNavigationMessage();
170         ge.setDate(new GLONASSDate(1342, 4, 45900).getDate());
171         ge.setX(-1.0705924E7);
172         ge.setXDot(2052.252685546875);
173         ge.setXDotDot(0.0);
174         ge.setY(-1.5225037E7);
175         ge.setYDot(1229.055419921875);
176         ge.setYDotDot(-2.7939677238464355E-6);
177         ge.setZ(-1.7389698E7);
178         ge.setZDot(-2338.376953125);
179         ge.setZDotDot(1.862645149230957E-6);
180         // Date of the GLONASS orbital elements, 3 Septembre 2019 at 09:45:00 UTC
181         final AbsoluteDate target = ge.getDate().shiftedBy(-18.0);
182         // 4th order Runge-Kutta
183         final ClassicalRungeKuttaIntegrator integrator = new ClassicalRungeKuttaIntegrator(1.);
184         // Initialize the propagator
185         final GLONASSNumericalPropagator propagator = new GLONASSNumericalPropagatorBuilder(integrator, ge, true).build();
186         // Compute the PV coordinates at the date of the GLONASS orbital elements
187         final Vector3D posInPZ90 = propagator.propagate(target).getPosition(pz90);
188         final Vector3D computedPos = pz90.getStaticTransformTo(itrf, target).transformPosition(posInPZ90);
189         // Expected position (reference from IGS file igv20692_06.sp3)
190         final Vector3D expectedPos = new Vector3D(-10742801.600, -15247162.619, -17347541.633);
191         // Verify
192         Assertions.assertEquals(0., Vector3D.distance(expectedPos, computedPos), 2.8);
193     }
194 
195     @Test
196     public void testIssue544() {
197         try {
198             Method eMeSinEM = GLONASSNumericalPropagator.class.getDeclaredMethod("eMeSinE",
199                                                                                  Double.TYPE, Double.TYPE);
200             eMeSinEM.setAccessible(true);
201             final double value = (double) eMeSinEM.invoke(null, Double.NaN, Double.NaN);
202             // Verify that an infinite loop did not occur
203             Assertions.assertTrue(Double.isNaN(value));
204         } catch (NoSuchMethodException e) {
205             e.printStackTrace();
206         } catch (SecurityException e) {
207             e.printStackTrace();
208         } catch (IllegalAccessException e) {
209             e.printStackTrace();
210         } catch (IllegalArgumentException e) {
211             e.printStackTrace();
212         } catch (InvocationTargetException e) {
213             e.printStackTrace();
214         }
215     }
216 
217     @Test
218     public void testIssue1032() {
219         final GLONASSNumericalPropagator propagator = new GLONASSNumericalPropagatorBuilder(new ClassicalRungeKuttaIntegrator(10.), ephemeris, false).build();
220         Assertions.assertEquals(PropagationType.OSCULATING, propagator.getPropagationType());
221     }
222 
223 }