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.util.FastMath;
23  import org.hipparchus.util.MathUtils;
24  import org.junit.jupiter.api.Assertions;
25  import org.junit.jupiter.api.Test;
26  import org.orekit.attitudes.LofOffset;
27  import org.orekit.estimation.Context;
28  import org.orekit.estimation.EstimationTestUtils;
29  import org.orekit.estimation.measurements.EstimatedMeasurementBase;
30  import org.orekit.estimation.measurements.GroundStation;
31  import org.orekit.estimation.measurements.ObservedMeasurement;
32  import org.orekit.estimation.measurements.OneWayRangeMeasurementCreator;
33  import org.orekit.estimation.measurements.Range;
34  import org.orekit.estimation.measurements.TwoWayRangeMeasurementCreator;
35  import org.orekit.frames.LOFType;
36  import org.orekit.gnss.antenna.FrequencyPattern;
37  import org.orekit.gnss.antenna.PhaseCenterVariationFunction;
38  import org.orekit.gnss.antenna.TwoDVariation;
39  import org.orekit.orbits.OrbitType;
40  import org.orekit.orbits.PositionAngleType;
41  import org.orekit.propagation.Propagator;
42  import org.orekit.propagation.SpacecraftState;
43  import org.orekit.propagation.conversion.NumericalPropagatorBuilder;
44  import org.orekit.utils.Constants;
45  
46  public class PhaseCentersRangeModifierTest {
47  
48      @Test
49      public void testPreliminary() {
50  
51          // this test does not check PhaseCentersRangeModifier at all,
52          // it just checks RangeMeasurementCreator behaves as necessary for the other test
53          // the *real* test is testEffect below
54          Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
55  
56          final NumericalPropagatorBuilder propagatorBuilder =
57                          context.createBuilder(OrbitType.KEPLERIAN, PositionAngleType.TRUE, true,
58                                                1.0e-6, 60.0, 0.001);
59          propagatorBuilder.setAttitudeProvider(new LofOffset(propagatorBuilder.getFrame(), LOFType.LVLH));
60  
61          // create perfect range measurements without antenna offset
62          final Propagator p1 = EstimationTestUtils.createPropagator(context.initialOrbit,
63                                                                             propagatorBuilder);
64          final List<ObservedMeasurement<?>> spacecraftCenteredMeasurements =
65                          EstimationTestUtils.createMeasurements(p1,
66                                                                 new TwoWayRangeMeasurementCreator(context),
67                                                                 1.0, 3.0, 300.0);
68  
69          // create perfect range measurements with antenna offset
70          final double xOffset = -2.5;
71          final Propagator p2 = EstimationTestUtils.createPropagator(context.initialOrbit,
72                                                                     propagatorBuilder);
73          final List<ObservedMeasurement<?>> antennaCenteredMeasurements =
74                          EstimationTestUtils.createMeasurements(p2,
75                                                                 new TwoWayRangeMeasurementCreator(context,
76                                                                                                   Vector3D.ZERO, null,
77                                                                                                   new Vector3D(xOffset, 0, 0), null,
78                                                                                                   0),
79                                                                 1.0, 3.0, 300.0);
80  
81          for (int i = 0; i < spacecraftCenteredMeasurements.size(); ++i) {
82              Range sr = (Range) spacecraftCenteredMeasurements.get(i);
83              Range ar = (Range) antennaCenteredMeasurements.get(i);
84              double alphaMax = FastMath.asin(Constants.WGS84_EARTH_EQUATORIAL_RADIUS / sr.getObservedValue()[0]);
85              Assertions.assertEquals(0.0, sr.getDate().durationFrom(ar.getDate()), 1.0e-8);
86              Assertions.assertTrue(ar.getObservedValue()[0] - sr.getObservedValue()[0] >= xOffset);
87              Assertions.assertTrue(ar.getObservedValue()[0] - sr.getObservedValue()[0] <= xOffset * FastMath.cos(alphaMax));
88          }
89      }
90  
91      @Test
92      public void testOneWayEffect() {
93  
94          Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
95  
96          final NumericalPropagatorBuilder propagatorBuilder =
97                          context.createBuilder(OrbitType.KEPLERIAN, PositionAngleType.TRUE, true,
98                                                1.0e-6, 60.0, 0.001);
99          propagatorBuilder.setAttitudeProvider(new LofOffset(propagatorBuilder.getFrame(), LOFType.LVLH));
100         final double groundClockOffset = 1.234e-3;
101         for (final GroundStation station : context.stations) {
102             station.getClockOffsetDriver().setValue(groundClockOffset);
103         }
104 
105         // create perfect range measurements without antenna offset
106         final Propagator p1 = EstimationTestUtils.createPropagator(context.initialOrbit, propagatorBuilder);
107         final List<ObservedMeasurement<?>> spacecraftCenteredMeasurements =
108                         EstimationTestUtils.createMeasurements(p1,
109                                                                new OneWayRangeMeasurementCreator(context,
110                                                                                                  Vector3D.ZERO, null,
111                                                                                                  Vector3D.ZERO, null,
112                                                                                                  0),
113                                                                1.0, 3.0, 300.0);
114 
115         // create perfect range measurements with antenna offset
116         final Vector3D stationMeanPosition   = new Vector3D(0.25, 0.25, -0.5);
117         final PhaseCenterVariationFunction stationPCV = new TwoDVariation(0, FastMath.PI, MathUtils.SEMI_PI,
118                                                                           new double[][] {
119                                                                               { 0.0,  0.25, -0.25, 0.5 },
120                                                                               { 0.0, -0.25,  0.25, 0.5 }
121                                                                           });
122         final Vector3D satelliteMeanPosition = new Vector3D(-2.5,  0,  0);
123         final PhaseCenterVariationFunction satellitePCV = new TwoDVariation(0, FastMath.PI, MathUtils.SEMI_PI,
124                                                                             new double[][] {
125                                                                                 { 0.0,  0.5, -0.5, 1.0 },
126                                                                                 { 0.0, -0.5,  0.5, 1.0 }
127                                                                             });
128         final Propagator p2 = EstimationTestUtils.createPropagator(context.initialOrbit,
129                                                                    propagatorBuilder);
130         final List<ObservedMeasurement<?>> antennaCenteredMeasurements =
131                         EstimationTestUtils.createMeasurements(p2,
132                                                                new OneWayRangeMeasurementCreator(context,
133                                                                                                  stationMeanPosition,   stationPCV,
134                                                                                                  satelliteMeanPosition, satellitePCV,
135                                                                                                  0),
136                                                                1.0, 3.0, 300.0);
137 
138         final Propagator p3 = EstimationTestUtils.createPropagator(context.initialOrbit,
139                                                                    propagatorBuilder);
140 
141         PhaseCentersRangeModifier modifier = new PhaseCentersRangeModifier(new FrequencyPattern(stationMeanPosition,
142                                                                                                 stationPCV),
143                                                                            new FrequencyPattern(satelliteMeanPosition,
144                                                                                                 satellitePCV));
145         for (int i = 0; i < spacecraftCenteredMeasurements.size(); ++i) {
146             Range sr = (Range) spacecraftCenteredMeasurements.get(i);
147             sr.addModifier(modifier);
148             EstimatedMeasurementBase<Range> estimated = sr.estimateWithoutDerivatives(new SpacecraftState[] { p3.propagate(sr.getDate()) });
149             Range ar = (Range) antennaCenteredMeasurements.get(i);
150             Assertions.assertEquals(0.0, sr.getDate().durationFrom(ar.getDate()), 1.0e-8);
151             Assertions.assertEquals(ar.getObservedValue()[0], estimated.getEstimatedValue()[0], 1.7e-6);
152         }
153 
154     }
155 
156     @Test
157     public void testTwoWayEffect() {
158 
159         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
160 
161         final NumericalPropagatorBuilder propagatorBuilder =
162                         context.createBuilder(OrbitType.KEPLERIAN, PositionAngleType.TRUE, true,
163                                               1.0e-6, 60.0, 0.001);
164         propagatorBuilder.setAttitudeProvider(new LofOffset(propagatorBuilder.getFrame(), LOFType.LVLH));
165         final double groundClockOffset = 1.234e-3;
166         for (final GroundStation station : context.stations) {
167             station.getClockOffsetDriver().setValue(groundClockOffset);
168         }
169 
170         // create perfect range measurements without antenna offset
171         final Propagator p1 = EstimationTestUtils.createPropagator(context.initialOrbit, propagatorBuilder);
172         final List<ObservedMeasurement<?>> spacecraftCenteredMeasurements =
173                         EstimationTestUtils.createMeasurements(p1,
174                                                                new TwoWayRangeMeasurementCreator(context,
175                                                                                                  Vector3D.ZERO, null,
176                                                                                                  Vector3D.ZERO, null,
177                                                                                                  0),
178                                                                1.0, 3.0, 300.0);
179 
180         // create perfect range measurements with antenna offset
181         final Vector3D stationMeanPosition   = new Vector3D(0.25, 0.25, -0.5);
182         final PhaseCenterVariationFunction stationPCV = new TwoDVariation(0, FastMath.PI, MathUtils.SEMI_PI,
183                                                                           new double[][] {
184                                                                               { 0.0,  0.25, -0.25, 0.5 },
185                                                                               { 0.0, -0.25,  0.25, 0.5 }
186                                                                           });
187         final Vector3D satelliteMeanPosition = new Vector3D(-2.5,  0,  0);
188         final PhaseCenterVariationFunction satellitePCV = new TwoDVariation(0, FastMath.PI, MathUtils.SEMI_PI,
189                                                                             new double[][] {
190                                                                                 { 0.0,  0.5, -0.5, 1.0 },
191                                                                                 { 0.0, -0.5,  0.5, 1.0 }
192                                                                             });
193         final Propagator p2 = EstimationTestUtils.createPropagator(context.initialOrbit,
194                                                                    propagatorBuilder);
195         final List<ObservedMeasurement<?>> antennaCenteredMeasurements =
196                         EstimationTestUtils.createMeasurements(p2,
197                                                                new TwoWayRangeMeasurementCreator(context,
198                                                                                                  stationMeanPosition,   stationPCV,
199                                                                                                  satelliteMeanPosition, satellitePCV,
200                                                                                                  0),
201                                                                1.0, 3.0, 300.0);
202 
203         final Propagator p3 = EstimationTestUtils.createPropagator(context.initialOrbit,
204                                                                    propagatorBuilder);
205 
206         PhaseCentersRangeModifier modifier = new PhaseCentersRangeModifier(new FrequencyPattern(stationMeanPosition,
207                                                                                                 stationPCV),
208                                                                            new FrequencyPattern(satelliteMeanPosition,
209                                                                                                 satellitePCV));
210         for (int i = 0; i < spacecraftCenteredMeasurements.size(); ++i) {
211             Range sr = (Range) spacecraftCenteredMeasurements.get(i);
212             sr.addModifier(modifier);
213             EstimatedMeasurementBase<Range> estimated = sr.estimateWithoutDerivatives(new SpacecraftState[] { p3.propagate(sr.getDate()) });
214             Range ar = (Range) antennaCenteredMeasurements.get(i);
215             Assertions.assertEquals(0.0, sr.getDate().durationFrom(ar.getDate()), 1.0e-8);
216             Assertions.assertEquals(ar.getObservedValue()[0], estimated.getEstimatedValue()[0], 3.4e-7);
217             Assertions.assertEquals(1,
218                                     estimated.getAppliedEffects().entrySet().stream().
219                                     filter(e -> e.getKey().getEffectName().equals("mean phase center")).count());
220         }
221 
222     }
223 
224 }
225 
226