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.estimation.measurements.modifiers;
18  
19  import java.util.List;
20  
21  import org.hipparchus.geometry.euclidean.threed.Vector3D;
22  import org.hipparchus.stat.descriptive.DescriptiveStatistics;
23  import org.junit.jupiter.api.Assertions;
24  import org.junit.jupiter.api.Test;
25  import org.orekit.attitudes.LofOffset;
26  import org.orekit.estimation.Context;
27  import org.orekit.estimation.EstimationTestUtils;
28  import org.orekit.estimation.measurements.EstimatedMeasurementBase;
29  import org.orekit.estimation.measurements.EstimationModifier;
30  import org.orekit.estimation.measurements.ObservedMeasurement;
31  import org.orekit.estimation.measurements.gnss.OneWayGNSSPhase;
32  import org.orekit.estimation.measurements.gnss.OneWayGNSSPhaseCreator;
33  import org.orekit.frames.LOFType;
34  import org.orekit.gnss.PredefinedGnssSignal;
35  import org.orekit.gnss.RadioWave;
36  import org.orekit.orbits.CartesianOrbit;
37  import org.orekit.orbits.Orbit;
38  import org.orekit.orbits.OrbitType;
39  import org.orekit.orbits.PositionAngleType;
40  import org.orekit.propagation.BoundedPropagator;
41  import org.orekit.propagation.EphemerisGenerator;
42  import org.orekit.propagation.Propagator;
43  import org.orekit.propagation.SpacecraftState;
44  import org.orekit.propagation.conversion.NumericalPropagatorBuilder;
45  import org.orekit.utils.TimeStampedPVCoordinates;
46  
47  public class ShapiroOneWayGNSSPhaseModifierTest {
48  
49      /** Radio wave of the measurements. */
50      private static final RadioWave RADIO_WAVE = PredefinedGnssSignal.G01;
51  
52      @Test
53      public void testShapiro() {
54          doTestShapiro(0.000047764, 0.000086953, 0.000164659);
55      }
56  
57      private void doTestShapiro(final double expectedMin, final double expectedMean, final double expectedMax) {
58  
59          Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
60  
61          final NumericalPropagatorBuilder propagatorBuilder =
62                          context.createBuilder(OrbitType.KEPLERIAN, PositionAngleType.TRUE, true,
63                                                1.0e-6, 60.0, 0.001);
64          propagatorBuilder.setAttitudeProvider(new LofOffset(propagatorBuilder.getFrame(), LOFType.LVLH));
65  
66          // create perfect one way GNSS phase measurements without antenna offset
67          final TimeStampedPVCoordinates original = context.initialOrbit.getPVCoordinates();
68          final Orbit closeOrbit = new CartesianOrbit(new TimeStampedPVCoordinates(context.initialOrbit.getDate(),
69                                                                                   original.getPosition().add(new Vector3D(1000, 2000, 3000)),
70                                                                                   original.getVelocity().add(new Vector3D(-0.03, 0.01, 0.02))),
71                                                      context.initialOrbit.getFrame(),
72                                                      context.initialOrbit.getMu());
73          final Propagator closePropagator = EstimationTestUtils.createPropagator(closeOrbit,
74                                                                                  propagatorBuilder);
75          final EphemerisGenerator generator = closePropagator.getEphemerisGenerator();
76          closePropagator.propagate(context.initialOrbit.getDate().shiftedBy(3.5 * closeOrbit.getKeplerianPeriod()));
77          final BoundedPropagator ephemeris = generator.getGeneratedEphemeris();
78          final Propagator p1 = EstimationTestUtils.createPropagator(context.initialOrbit,
79                                                                             propagatorBuilder);
80          final int    ambiguity         = 1234;
81          final double localClockOffset  = 0.137e-6;
82          final double remoteClockOffset = 469.0e-6;
83          List<ObservedMeasurement<?>> measurements =
84                          EstimationTestUtils.createMeasurements(p1,
85                                                                 new OneWayGNSSPhaseCreator(ephemeris,
86                                                                                            "remote", RADIO_WAVE,
87                                                                                            ambiguity,
88                                                                                            localClockOffset,
89                                                                                            remoteClockOffset,
90                                                                                            Vector3D.ZERO,
91                                                                                            Vector3D.ZERO),
92                                                                 1.0, 3.0, 300.0);
93  
94          final ShapiroOneWayGNSSPhaseModifier modifier = new ShapiroOneWayGNSSPhaseModifier(context.initialOrbit.getMu());
95          final Propagator p3 = EstimationTestUtils.createPropagator(context.initialOrbit,
96                                                                     propagatorBuilder);
97          DescriptiveStatistics stat = new DescriptiveStatistics();
98          for (ObservedMeasurement<?> measurement : measurements) {
99              OneWayGNSSPhase sr = (OneWayGNSSPhase) measurement;
100             SpacecraftState[] states = new SpacecraftState[] {
101                 p3.propagate(sr.getDate()), ephemeris.propagate(sr.getDate())
102             };
103             EstimatedMeasurementBase<OneWayGNSSPhase>
104                 evalNoMod =
105                 sr.estimateWithoutDerivatives(states);
106 
107             // add modifier
108             sr.addModifier(modifier);
109             boolean found = false;
110             for (final EstimationModifier<OneWayGNSSPhase> existing : sr.getModifiers()) {
111                 found = found || existing == modifier;
112             }
113             Assertions.assertTrue(found);
114             EstimatedMeasurementBase<OneWayGNSSPhase>
115                 eval =
116                 sr.estimateWithoutDerivatives(states);
117 
118             stat.addValue(eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]);
119             Assertions.assertEquals(1,
120                                     eval.getAppliedEffects().entrySet().stream().
121                                     filter(e -> e.getKey().getEffectName().equals("Shapiro")).count());
122 
123         }
124         final double wavelength = ((OneWayGNSSPhase) measurements.get(0)).getWavelength();
125 
126         Assertions.assertEquals(expectedMin,  stat.getMin() * wavelength,  1.0e-9);
127         Assertions.assertEquals(expectedMean, stat.getMean() * wavelength, 1.0e-9);
128         Assertions.assertEquals(expectedMax,  stat.getMax() * wavelength,  1.0e-9);
129 
130     }
131 
132 }