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.junit.jupiter.api.Assertions;
22  import org.junit.jupiter.api.BeforeAll;
23  import org.junit.jupiter.api.Test;
24  import org.orekit.Utils;
25  import org.orekit.data.DataContext;
26  import org.orekit.frames.Frame;
27  import org.orekit.frames.FramesFactory;
28  import org.orekit.gnss.SatelliteSystem;
29  import org.orekit.propagation.SpacecraftState;
30  import org.orekit.propagation.analytical.gnss.data.GNSSOrbitalElements;
31  import org.orekit.propagation.analytical.gnss.data.QZSSAlmanac;
32  import org.orekit.propagation.analytical.gnss.data.QZSSLegacyNavigationMessage;
33  import org.orekit.time.AbsoluteDate;
34  import org.orekit.time.GNSSDate;
35  import org.orekit.time.TimeInterpolator;
36  import org.orekit.time.TimeScalesFactory;
37  import org.orekit.utils.CartesianDerivativesFilter;
38  import org.orekit.utils.Constants;
39  import org.orekit.utils.IERSConventions;
40  import org.orekit.utils.PVCoordinates;
41  import org.orekit.utils.TimeStampedPVCoordinates;
42  import org.orekit.utils.TimeStampedPVCoordinatesHermiteInterpolator;
43  
44  import java.util.ArrayList;
45  import java.util.List;
46  
47  public class QZSSPropagatorTest {
48  
49      private static QZSSAlmanac almanac;
50  
51      @BeforeAll
52      public static void setUpBeforeClass() {
53          Utils.setDataRoot("gnss");
54  
55          // Almanac for satellite 193 for May 27th 2019 (q201914.alm)
56          almanac = new QZSSAlmanac(DataContext.getDefault().getTimeScales(), SatelliteSystem.QZSS);
57          almanac.setPRN(193);
58          almanac.setWeek(7);
59          almanac.setTime(348160.0);
60          almanac.setSqrtA(6493.145996);
61          almanac.setE(7.579761505E-02);
62          almanac.setI0(0.7201680272);
63          almanac.setOmega0(-1.643310999);
64          almanac.setOmegaDot(-3.005839491E-09);
65          almanac.setPa(-1.561775201);
66          almanac.setM0(-4.050903957E-01);
67          almanac.setAf0(-2.965927124E-04);
68          almanac.setAf1(7.275957614E-12);
69          almanac.setHealth(0);
70  
71      }
72  
73      @Test
74      public void testQZSSCycle() {
75          // Builds the QZSS propagator from the almanac
76          final GNSSPropagator propagator = almanac.getPropagator();
77          // Propagate at the QZSS date and one QZSS cycle later
78          final AbsoluteDate date0 = almanac.getDate();
79          final Vector3D p0 = propagator.propagateInEcef(date0).getPosition();
80          final double bdtCycleDuration = almanac.getCycleDuration();
81          final AbsoluteDate date1 = date0.shiftedBy(bdtCycleDuration);
82          final Vector3D p1 = propagator.propagateInEcef(date1).getPosition();
83  
84          // Checks
85          Assertions.assertEquals(0., p0.distance(p1), 0.);
86      }
87  
88      @Test
89      public void testFrames() {
90          // Builds the QZSS propagator from the almanac
91          final GNSSPropagator propagator = almanac.getPropagator();
92          Assertions.assertEquals("EME2000", propagator.getFrame().getName());
93          Assertions.assertEquals(3.986005e+14, almanac.getMu(), 1.0e6);
94          // Defines some date
95          final AbsoluteDate date = new AbsoluteDate(2016, 3, 3, 12, 0, 0., TimeScalesFactory.getUTC());
96          // Get PVCoordinates at the date in the ECEF
97          final PVCoordinates pv0 = propagator.propagateInEcef(date);
98          // Get PVCoordinates at the date in the ECEF
99          final PVCoordinates pv1 = propagator.getPVCoordinates(date, propagator.getECEF());
100 
101         // Checks
102         Assertions.assertEquals(0., pv0.getPosition().distance(pv1.getPosition()), 3.3e-8);
103         Assertions.assertEquals(0., pv0.getVelocity().distance(pv1.getVelocity()), 3.9e-12);
104     }
105 
106     @Test
107     public void testResetInitialState() {
108         final GNSSPropagator propagator = almanac.getPropagator();
109         final SpacecraftState old = propagator.getInitialState();
110         propagator.resetInitialState(new SpacecraftState(old.getOrbit(), old.getAttitude(), old.getMass() + 1000));
111         Assertions.assertEquals(old.getMass() + 1000, propagator.getInitialState().getMass(), 1.0e-9);
112     }
113 
114     @Test
115     public void testResetIntermediateState() {
116         GNSSPropagator propagator = new GNSSPropagatorBuilder(almanac).build();
117         final SpacecraftState old = propagator.getInitialState();
118         propagator.resetIntermediateState(new SpacecraftState(old.getOrbit(), old.getAttitude(), old.getMass() + 1000),
119                                           true);
120         Assertions.assertEquals(old.getMass() + 1000, propagator.getInitialState().getMass(), 1.0e-9);
121     }
122 
123     @Test
124     public void testDerivativesConsistency() {
125 
126         final Frame eme2000 = FramesFactory.getEME2000();
127         double errorP = 0;
128         double errorV = 0;
129         double errorA = 0;
130         final GNSSPropagator propagator = almanac.getPropagator();
131         GNSSOrbitalElements<?> elements = propagator.getOrbitalElements();
132         AbsoluteDate t0 = new GNSSDate(elements.getWeek(), elements.getTime(), SatelliteSystem.QZSS).getDate();
133         for (double dt = 0; dt < Constants.JULIAN_DAY; dt += 600) {
134             final AbsoluteDate central = t0.shiftedBy(dt);
135             final PVCoordinates pv = propagator.getPVCoordinates(central, eme2000);
136             final double h = 60.0;
137             List<TimeStampedPVCoordinates> sample = new ArrayList<>();
138             for (int i = -3; i <= 3; ++i) {
139                 sample.add(propagator.getPVCoordinates(central.shiftedBy(i * h), eme2000));
140             }
141 
142             // create interpolator
143             final TimeInterpolator<TimeStampedPVCoordinates> interpolator =
144                     new TimeStampedPVCoordinatesHermiteInterpolator(sample.size(), CartesianDerivativesFilter.USE_P);
145 
146             final PVCoordinates interpolated = interpolator.interpolate(central, sample);
147             errorP = FastMath.max(errorP, Vector3D.distance(pv.getPosition(), interpolated.getPosition()));
148             errorV = FastMath.max(errorV, Vector3D.distance(pv.getVelocity(), interpolated.getVelocity()));
149             errorA = FastMath.max(errorA, Vector3D.distance(pv.getAcceleration(), interpolated.getAcceleration()));
150         }
151 
152         Assertions.assertEquals(0.0, errorP, 3.8e-9);
153         Assertions.assertEquals(0.0, errorV, 8.4e-8);
154         Assertions.assertEquals(0.0, errorA, 2.1e-8);
155 
156     }
157 
158     @Test
159     public void testPosition() {
160         // Initial QZSS orbital elements (Ref: IGS)
161         final QZSSLegacyNavigationMessage qoe =
162             new QZSSLegacyNavigationMessage(DataContext.getDefault().getTimeScales(), SatelliteSystem.QZSS);
163         qoe.setPRN(195);
164         qoe.setWeek(21);
165         qoe.setTime(226800.0);
166         qoe.setSqrtA(6493.226968765259);
167         qoe.setE(0.07426900835707784);
168         qoe.setDeltaN0(4.796628370253418E-10);
169         qoe.setI0(0.7116940567084221);
170         qoe.setIDot(4.835915721014987E-10);
171         qoe.setOmega0(0.6210371871830609);
172         qoe.setOmegaDot(-8.38963517626603E-10);
173         qoe.setPa(-1.5781555771543598);
174         qoe.setM0(1.077008903618136);
175         qoe.setCuc(-8.8568776845932E-6);
176         qoe.setCus(1.794286072254181E-5);
177         qoe.setCrc(-344.03125);
178         qoe.setCrs(-305.6875);
179         qoe.setCic(1.2032687664031982E-6);
180         qoe.setCis(-2.6728957891464233E-6);
181         // Date of the QZSS orbital elements
182         final AbsoluteDate target = qoe.getDate();
183         // Build the QZSS propagator
184         final GNSSPropagator propagator = qoe.getPropagator();
185         // Compute the PV coordinates at the date of the QZSS orbital elements
186         final PVCoordinates pv = propagator.getPVCoordinates(target, FramesFactory.getITRF(IERSConventions.IERS_2010, true));
187         // Computed position
188         final Vector3D computedPos = pv.getPosition();
189         // Expected position (reference from QZSS sp3 file qzu20693_00.sp3)
190         final Vector3D expectedPos = new Vector3D(-35047225.493, 18739632.916, -9522204.569);
191         Assertions.assertEquals(0., Vector3D.distance(expectedPos, computedPos), 0.7);
192     }
193 
194     @Test
195     public void testIssue544() {
196         // Builds the QZSSPropagator from the almanac
197         final GNSSPropagator propagator = almanac.getPropagator();
198         // In order to test the issue, we voluntary set a Double.NaN value in the date.
199         final AbsoluteDate date0 = new AbsoluteDate(2010, 5, 7, 7, 50, Double.NaN, TimeScalesFactory.getUTC());
200         final PVCoordinates pv0 = propagator.propagateInEcef(date0);
201         // Verify that an infinite loop did not occur
202         Assertions.assertEquals(Vector3D.NaN, pv0.getPosition());
203         Assertions.assertEquals(Vector3D.NaN, pv0.getVelocity());
204 
205     }
206 
207     @Test
208     public void testConversion() {
209         GnssTestUtils.checkFieldConversion(almanac);
210     }
211 
212 }