1   /* Copyright 2002-2022 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  import java.util.Map;
21  
22  import org.hipparchus.util.MathUtils;
23  import org.hipparchus.util.Precision;
24  import org.junit.After;
25  import org.junit.Assert;
26  import org.junit.Before;
27  import org.junit.Test;
28  import org.orekit.estimation.Context;
29  import org.orekit.estimation.EstimationTestUtils;
30  import org.orekit.estimation.measurements.AngularAzEl;
31  import org.orekit.estimation.measurements.AngularAzElMeasurementCreator;
32  import org.orekit.estimation.measurements.EstimatedMeasurement;
33  import org.orekit.estimation.measurements.GroundStation;
34  import org.orekit.estimation.measurements.ObservedMeasurement;
35  import org.orekit.estimation.measurements.Range;
36  import org.orekit.estimation.measurements.RangeMeasurementCreator;
37  import org.orekit.estimation.measurements.RangeRate;
38  import org.orekit.estimation.measurements.RangeRateMeasurementCreator;
39  import org.orekit.estimation.measurements.TurnAroundRange;
40  import org.orekit.estimation.measurements.TurnAroundRangeMeasurementCreator;
41  import org.orekit.estimation.measurements.gnss.Phase;
42  import org.orekit.estimation.measurements.gnss.PhaseMeasurementCreator;
43  import org.orekit.frames.TopocentricFrame;
44  import org.orekit.gnss.Frequency;
45  import org.orekit.models.earth.EarthITU453AtmosphereRefraction;
46  import org.orekit.models.earth.troposphere.EstimatedTroposphericModel;
47  import org.orekit.models.earth.troposphere.NiellMappingFunctionModel;
48  import org.orekit.models.earth.troposphere.SaastamoinenModel;
49  import org.orekit.orbits.OrbitType;
50  import org.orekit.orbits.PositionAngle;
51  import org.orekit.propagation.Propagator;
52  import org.orekit.propagation.SpacecraftState;
53  import org.orekit.propagation.conversion.NumericalPropagatorBuilder;
54  import org.orekit.time.AbsoluteDate;
55  import org.orekit.utils.ParameterDriver;
56  
57  public class TropoModifierTest {
58  
59      @Before
60      public void setUp() throws Exception {
61  
62      }
63  
64      @After
65      public void tearDown() {
66  
67      }
68  
69      @Test
70      public void testRangeTropoModifier() {
71  
72          Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
73  
74          final NumericalPropagatorBuilder propagatorBuilder =
75                          context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
76                                                1.0e-6, 60.0, 0.001);
77  
78          // create perfect range measurements
79          for (final GroundStation station : context.stations) {
80              station.getClockOffsetDriver().setSelected(true);
81              station.getEastOffsetDriver().setSelected(true);
82              station.getNorthOffsetDriver().setSelected(true);
83              station.getZenithOffsetDriver().setSelected(true);
84          }
85          final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
86                                                                             propagatorBuilder);
87          final List<ObservedMeasurement<?>> measurements =
88                          EstimationTestUtils.createMeasurements(propagator,
89                                                                 new RangeMeasurementCreator(context),
90                                                                 1.0, 3.0, 300.0);
91          propagator.clearStepHandlers();
92  
93          final RangeTroposphericDelayModifier modifier = new RangeTroposphericDelayModifier(SaastamoinenModel.getStandardModel());
94  
95          for (final ObservedMeasurement<?> measurement : measurements) {
96              final AbsoluteDate date = measurement.getDate();
97  
98              final SpacecraftState refState = propagator.propagate(date);
99  
100             Range range = (Range) measurement;
101             EstimatedMeasurement<Range> evalNoMod = range.estimate(0, 0, new SpacecraftState[] { refState });
102 
103 
104             // add modifier
105             range.addModifier(modifier);
106             EstimatedMeasurement<Range> eval = range.estimate(0, 0, new SpacecraftState[] { refState });
107 
108             final double diffMeters = eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0];
109 
110             final double epsilon = 1e-6;
111             Assert.assertTrue(Precision.compareTo(diffMeters, 12., epsilon) < 0);
112             Assert.assertTrue(Precision.compareTo(diffMeters, 0., epsilon) > 0);
113         }
114     }
115 
116     @Test
117     public void testPhaseTropoModifier() {
118 
119         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
120 
121         final NumericalPropagatorBuilder propagatorBuilder =
122                         context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
123                                               1.0e-6, 60.0, 0.001);
124 
125         // create perfect range measurements
126         for (final GroundStation station : context.stations) {
127             station.getClockOffsetDriver().setSelected(true);
128             station.getEastOffsetDriver().setSelected(true);
129             station.getNorthOffsetDriver().setSelected(true);
130             station.getZenithOffsetDriver().setSelected(true);
131         }
132         final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
133                                                                            propagatorBuilder);
134         final int    ambiguity         = 1234;
135         final double groundClockOffset =  12.0e-6;
136         for (final GroundStation station : context.stations) {
137             station.getClockOffsetDriver().setValue(groundClockOffset);
138         }
139         final double satClockOffset    = 345.0e-6;
140         final List<ObservedMeasurement<?>> measurements =
141                         EstimationTestUtils.createMeasurements(propagator,
142                                                                new PhaseMeasurementCreator(context, Frequency.G01,
143                                                                                            ambiguity,
144                                                                                            satClockOffset),
145                                                                1.0, 3.0, 300.0);
146         propagator.clearStepHandlers();
147 
148         final PhaseTroposphericDelayModifier modifier = new PhaseTroposphericDelayModifier(SaastamoinenModel.getStandardModel());
149 
150         for (final ObservedMeasurement<?> measurement : measurements) {
151             final AbsoluteDate date = measurement.getDate();
152 
153             final SpacecraftState refState = propagator.propagate(date);
154 
155             Phase phase = (Phase) measurement;
156             EstimatedMeasurement<Phase> evalNoMod = phase.estimate(0, 0, new SpacecraftState[] { refState });
157 
158 
159             // add modifier
160             phase.addModifier(modifier);
161             EstimatedMeasurement<Phase> eval = phase.estimate(0, 0, new SpacecraftState[] { refState });
162 
163             final double diffMeters = (eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]) * phase.getWavelength();
164 
165             final double epsilon = 1e-6;
166             Assert.assertTrue(Precision.compareTo(diffMeters, 12., epsilon) < 0);
167             Assert.assertTrue(Precision.compareTo(diffMeters, 0., epsilon) > 0);
168         }
169     }
170 
171     @Test
172     public void testRangeEstimatedTropoModifier() {
173 
174         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
175 
176         final NumericalPropagatorBuilder propagatorBuilder =
177                         context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
178                                               1.0e-6, 60.0, 0.001);
179 
180         final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
181                                                                            propagatorBuilder);
182         final List<ObservedMeasurement<?>> measurements =
183                         EstimationTestUtils.createMeasurements(propagator,
184                                                                new RangeMeasurementCreator(context),
185                                                                1.0, 3.0, 300.0);
186         propagator.clearStepHandlers();
187 
188         for (final ObservedMeasurement<?> measurement : measurements) {
189             final AbsoluteDate date = measurement.getDate();
190 
191             final SpacecraftState refState = propagator.propagate(date);
192 
193             Range range = (Range) measurement;
194             EstimatedMeasurement<Range> evalNoMod = range.estimate(0, 0, new SpacecraftState[] { refState });
195 
196 
197             // add modifier
198             final GroundStation stationParameter = ((Range) measurement).getStation();
199             final TopocentricFrame baseFrame = stationParameter.getBaseFrame();
200             final NiellMappingFunctionModel mappingFunction = new NiellMappingFunctionModel();
201             final EstimatedTroposphericModel tropoModel     = new EstimatedTroposphericModel(mappingFunction, 5.0);
202             final RangeTroposphericDelayModifier modifier = new RangeTroposphericDelayModifier(tropoModel);
203             
204             final ParameterDriver parameterDriver = modifier.getParametersDrivers().get(0);
205             parameterDriver.setSelected(true);
206             parameterDriver.setName(baseFrame.getName() + EstimatedTroposphericModel.TOTAL_ZENITH_DELAY);
207             range.addModifier(modifier);
208             EstimatedMeasurement<Range> eval = range.estimate(0, 0, new SpacecraftState[] { refState });
209 
210             final double diffMeters = eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0];
211 
212             final double epsilon = 1e-6;
213             Assert.assertTrue(Precision.compareTo(diffMeters, 12., epsilon) < 0);
214             Assert.assertTrue(Precision.compareTo(diffMeters, 0., epsilon) > 0);
215         }
216     }
217 
218     @Test
219     public void testPhaseEstimatedTropoModifier() {
220 
221         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
222 
223         final NumericalPropagatorBuilder propagatorBuilder =
224                         context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
225                                               1.0e-6, 60.0, 0.001);
226 
227         final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
228                                                                            propagatorBuilder);
229         final int    ambiguity         = 1234;
230         final double groundClockOffset =  12.0e-6;
231         for (final GroundStation station : context.stations) {
232             station.getClockOffsetDriver().setValue(groundClockOffset);
233         }
234         final double satClockOffset    = 345.0e-6;
235         final List<ObservedMeasurement<?>> measurements =
236                         EstimationTestUtils.createMeasurements(propagator,
237                                                                new PhaseMeasurementCreator(context, Frequency.G01,
238                                                                                            ambiguity,
239                                                                                            satClockOffset),
240                                                                1.0, 3.0, 300.0);
241         propagator.clearStepHandlers();
242 
243         for (final ObservedMeasurement<?> measurement : measurements) {
244             final AbsoluteDate date = measurement.getDate();
245 
246             final SpacecraftState refState = propagator.propagate(date);
247 
248             Phase phase = (Phase) measurement;
249             EstimatedMeasurement<Phase> evalNoMod = phase.estimate(0, 0, new SpacecraftState[] { refState });
250 
251 
252             // add modifier
253             final GroundStation stationParameter = phase.getStation();
254             final TopocentricFrame baseFrame = stationParameter.getBaseFrame();
255             final NiellMappingFunctionModel mappingFunction = new NiellMappingFunctionModel();
256             final EstimatedTroposphericModel tropoModel     = new EstimatedTroposphericModel(mappingFunction, 5.0);
257             final PhaseTroposphericDelayModifier modifier = new PhaseTroposphericDelayModifier(tropoModel);
258             
259             final ParameterDriver parameterDriver = modifier.getParametersDrivers().get(0);
260             parameterDriver.setSelected(true);
261             parameterDriver.setName(baseFrame.getName() + EstimatedTroposphericModel.TOTAL_ZENITH_DELAY);
262             phase.addModifier(modifier);
263             EstimatedMeasurement<Phase> eval = phase.estimate(0, 0, new SpacecraftState[] { refState });
264 
265             final double diffMeters = (eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]) * phase.getWavelength();
266 
267             final double epsilon = 1e-6;
268             Assert.assertTrue(Precision.compareTo(diffMeters, 12., epsilon) < 0);
269             Assert.assertTrue(Precision.compareTo(diffMeters, 0., epsilon) > 0);
270         }
271     }
272 
273     @Test
274     public void testTurnAroundRangeTropoModifier() {
275 
276         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
277 
278         final NumericalPropagatorBuilder propagatorBuilder =
279                         context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
280                                               1.0e-6, 60.0, 0.001);
281 
282         // Create perfect turn-around measurements
283         for (Map.Entry<GroundStation, GroundStation> entry : context.TARstations.entrySet()) {
284             final GroundStation    primaryStation = entry.getKey();
285             final GroundStation    secondaryStation  = entry.getValue();
286             primaryStation.getClockOffsetDriver().setSelected(true);
287             primaryStation.getEastOffsetDriver().setSelected(true);
288             primaryStation.getNorthOffsetDriver().setSelected(true);
289             primaryStation.getZenithOffsetDriver().setSelected(true);
290             secondaryStation.getClockOffsetDriver().setSelected(false);
291             secondaryStation.getEastOffsetDriver().setSelected(true);
292             secondaryStation.getNorthOffsetDriver().setSelected(true);
293             secondaryStation.getZenithOffsetDriver().setSelected(true);
294         }
295         final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
296                                                                            propagatorBuilder);
297         final List<ObservedMeasurement<?>> measurements =
298                         EstimationTestUtils.createMeasurements(propagator,
299                                                                new TurnAroundRangeMeasurementCreator(context),
300                                                                1.0, 3.0, 300.0);
301         propagator.clearStepHandlers();
302 
303         final TurnAroundRangeTroposphericDelayModifier modifier = new TurnAroundRangeTroposphericDelayModifier(SaastamoinenModel.getStandardModel());
304 
305         for (final ObservedMeasurement<?> measurement : measurements) {
306             final AbsoluteDate date = measurement.getDate();
307 
308             final SpacecraftState refState = propagator.propagate(date);
309 
310             TurnAroundRange turnAroundRange = (TurnAroundRange) measurement;
311             EstimatedMeasurement<TurnAroundRange> evalNoMod = turnAroundRange.estimate(0, 0, new SpacecraftState[] { refState });
312 
313             // add modifier
314             turnAroundRange.addModifier(modifier);
315             //
316             EstimatedMeasurement<TurnAroundRange> eval = turnAroundRange.estimate(0, 0, new SpacecraftState[] { refState });
317 
318             final double diffMeters = eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0];
319 
320             final double epsilon = 1e-6;
321             Assert.assertTrue(Precision.compareTo(diffMeters, 12., epsilon) < 0);
322             Assert.assertTrue(Precision.compareTo(diffMeters, 0., epsilon) > 0);
323         }
324     }
325 
326     @Test
327     public void testRangeRateTropoModifier() {
328 
329         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
330 
331         final NumericalPropagatorBuilder propagatorBuilder =
332                         context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
333                                               1.0e-6, 60.0, 0.001);
334 
335         // create perfect range measurements
336         for (final GroundStation station : context.stations) {
337             station.getClockOffsetDriver().setSelected(true);
338             station.getEastOffsetDriver().setSelected(true);
339             station.getNorthOffsetDriver().setSelected(true);
340             station.getZenithOffsetDriver().setSelected(true);
341         }
342         final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
343                                                                            propagatorBuilder);
344         final double satClkDrift = 3.2e-10;
345         final List<ObservedMeasurement<?>> measurements =
346                         EstimationTestUtils.createMeasurements(propagator,
347                                                                new RangeRateMeasurementCreator(context, false, satClkDrift),
348                                                                1.0, 3.0, 300.0);
349         propagator.clearStepHandlers();
350 
351         final RangeRateTroposphericDelayModifier modifier = new RangeRateTroposphericDelayModifier(SaastamoinenModel.getStandardModel(), false);
352 
353         for (final ObservedMeasurement<?> measurement : measurements) {
354             final AbsoluteDate date = measurement.getDate();
355 
356             final SpacecraftState refState = propagator.propagate(date);
357 
358             RangeRate rangeRate = (RangeRate) measurement;
359             EstimatedMeasurement<RangeRate> evalNoMod = rangeRate.estimate(0, 0, new SpacecraftState[] { refState });
360 
361             // add modifier
362             rangeRate.addModifier(modifier);
363 
364             //
365             EstimatedMeasurement<RangeRate> eval = rangeRate.estimate(0, 0, new SpacecraftState[] { refState });
366 
367             final double diffMetersSec = eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0];
368 
369             final double epsilon = 1e-6;
370             Assert.assertTrue(Precision.compareTo(diffMetersSec, 0.01, epsilon) < 0);
371             Assert.assertTrue(Precision.compareTo(diffMetersSec, -0.01, epsilon) > 0);
372         }
373     }
374 
375     @Test
376     public void testRangeRateEstimatedTropoModifier() {
377 
378         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
379 
380         final NumericalPropagatorBuilder propagatorBuilder =
381                         context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
382                                               1.0e-6, 60.0, 0.001);
383 
384         final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
385                                                                            propagatorBuilder);
386         final double satClkDrift = 3.2e-10;
387         final List<ObservedMeasurement<?>> measurements =
388                         EstimationTestUtils.createMeasurements(propagator,
389                                                                new RangeRateMeasurementCreator(context, false, satClkDrift),
390                                                                1.0, 3.0, 300.0);
391         propagator.clearStepHandlers();
392 
393         for (final ObservedMeasurement<?> measurement : measurements) {
394             final AbsoluteDate date = measurement.getDate();
395 
396             final SpacecraftState refState = propagator.propagate(date);
397 
398             RangeRate rangeRate = (RangeRate) measurement;
399             EstimatedMeasurement<RangeRate> evalNoMod = rangeRate.estimate(0, 0, new SpacecraftState[] { refState });
400 
401             // add modifier
402             final GroundStation stationParameter = ((RangeRate) measurement).getStation();
403             final TopocentricFrame baseFrame = stationParameter.getBaseFrame();
404             final NiellMappingFunctionModel mappingFunction = new NiellMappingFunctionModel();
405             final EstimatedTroposphericModel tropoModel     = new EstimatedTroposphericModel(mappingFunction, 5.0);
406             final RangeRateTroposphericDelayModifier modifier = new RangeRateTroposphericDelayModifier(tropoModel, false);
407 
408             final ParameterDriver parameterDriver = modifier.getParametersDrivers().get(0);
409             parameterDriver.setSelected(true);
410             parameterDriver.setName(baseFrame.getName() + EstimatedTroposphericModel.TOTAL_ZENITH_DELAY);
411             rangeRate.addModifier(modifier);
412 
413             //
414             EstimatedMeasurement<RangeRate> eval = rangeRate.estimate(0, 0, new SpacecraftState[] { refState });
415 
416             final double diffMetersSec = eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0];
417 
418             final double epsilon = 1e-6;
419             Assert.assertTrue(Precision.compareTo(diffMetersSec, 0.01, epsilon) < 0);
420             Assert.assertTrue(Precision.compareTo(diffMetersSec, -0.01, epsilon) > 0);
421         }
422     }
423 
424     @Test
425     public void testAngularTropoModifier() {
426 
427         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
428 
429         final NumericalPropagatorBuilder propagatorBuilder =
430                         context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
431                                               1.0e-6, 60.0, 0.001);
432 
433         // create perfect angular measurements
434         for (final GroundStation station : context.stations) {
435             station.getClockOffsetDriver().setSelected(true);
436             station.getEastOffsetDriver().setSelected(true);
437             station.getNorthOffsetDriver().setSelected(true);
438             station.getZenithOffsetDriver().setSelected(true);
439         }
440         final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
441                                                                            propagatorBuilder);
442         final List<ObservedMeasurement<?>> measurements =
443                         EstimationTestUtils.createMeasurements(propagator,
444                                                                new AngularAzElMeasurementCreator(context),
445                                                                1.0, 3.0, 300.0);
446         propagator.clearStepHandlers();
447 
448         final AngularTroposphericDelayModifier modifier = new AngularTroposphericDelayModifier(SaastamoinenModel.getStandardModel());
449 
450         for (final ObservedMeasurement<?> measurement : measurements) {
451             final AbsoluteDate date = measurement.getDate();
452 
453             final SpacecraftState refState = propagator.propagate(date);
454 
455             AngularAzEl angular = (AngularAzEl) measurement;
456             EstimatedMeasurement<AngularAzEl> evalNoMod = angular.estimate(0, 0, new SpacecraftState[] { refState });
457 
458             // add modifier
459             angular.addModifier(modifier);
460             //
461             EstimatedMeasurement<AngularAzEl> eval = angular.estimate(0, 0, new SpacecraftState[] { refState });
462 
463             final double diffAz = MathUtils.normalizeAngle(eval.getEstimatedValue()[0], evalNoMod.getEstimatedValue()[0]) - evalNoMod.getEstimatedValue()[0];
464             final double diffEl = MathUtils.normalizeAngle(eval.getEstimatedValue()[1], evalNoMod.getEstimatedValue()[1]) - evalNoMod.getEstimatedValue()[1];
465             // TODO: check threshold
466             Assert.assertEquals(0.0, diffAz, 5.0e-5);
467             Assert.assertEquals(0.0, diffEl, 5.0e-6);
468         }
469     }
470 
471     @Test
472     public void testAngularEstimatedTropoModifier() {
473 
474         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
475 
476         final NumericalPropagatorBuilder propagatorBuilder =
477                         context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
478                                               1.0e-6, 60.0, 0.001);
479 
480         // create perfect angular measurements
481         for (final GroundStation station : context.stations) {
482             station.getEastOffsetDriver().setSelected(true);
483             station.getNorthOffsetDriver().setSelected(true);
484             station.getZenithOffsetDriver().setSelected(true);
485         }
486         final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
487                                                                            propagatorBuilder);
488         final List<ObservedMeasurement<?>> measurements =
489                         EstimationTestUtils.createMeasurements(propagator,
490                                                                new AngularAzElMeasurementCreator(context),
491                                                                1.0, 3.0, 300.0);
492         propagator.clearStepHandlers();
493 
494         for (final ObservedMeasurement<?> measurement : measurements) {
495             final AbsoluteDate date = measurement.getDate();
496 
497             final SpacecraftState refState = propagator.propagate(date);
498 
499             AngularAzEl angular = (AngularAzEl) measurement;
500             EstimatedMeasurement<AngularAzEl> evalNoMod = angular.estimate(0, 0, new SpacecraftState[] { refState });
501 
502             // add modifier
503             final GroundStation stationParameter = ((AngularAzEl) measurement).getStation();
504             final TopocentricFrame baseFrame = stationParameter.getBaseFrame();
505             final NiellMappingFunctionModel mappingFunction = new NiellMappingFunctionModel();
506             final EstimatedTroposphericModel tropoModel     = new EstimatedTroposphericModel(mappingFunction, 5.0);
507             final AngularTroposphericDelayModifier modifier = new AngularTroposphericDelayModifier(tropoModel);
508 
509             final ParameterDriver parameterDriver = modifier.getParametersDrivers().get(0);
510             parameterDriver.setSelected(true);
511             parameterDriver.setName(baseFrame.getName() + EstimatedTroposphericModel.TOTAL_ZENITH_DELAY);
512             angular.addModifier(modifier);
513             //
514             EstimatedMeasurement<AngularAzEl> eval = angular.estimate(0, 0, new SpacecraftState[] { refState });
515 
516             final double diffAz = MathUtils.normalizeAngle(eval.getEstimatedValue()[0], evalNoMod.getEstimatedValue()[0]) - evalNoMod.getEstimatedValue()[0];
517             final double diffEl = MathUtils.normalizeAngle(eval.getEstimatedValue()[1], evalNoMod.getEstimatedValue()[1]) - evalNoMod.getEstimatedValue()[1];
518 
519             Assert.assertEquals(0.0, diffAz, 1.9e-5);
520             Assert.assertEquals(0.0, diffEl, 2.1e-6);
521         }
522     }
523 
524     @Test
525     public void testAngularRadioRefractionModifier() {
526 
527         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
528 
529         final NumericalPropagatorBuilder propagatorBuilder =
530                         context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
531                                               1.0e-6, 60.0, 0.001);
532 
533         // create perfect angular measurements
534         for (final GroundStation station : context.stations) {
535             station.getClockOffsetDriver().setSelected(true);
536             station.getEastOffsetDriver().setSelected(true);
537             station.getNorthOffsetDriver().setSelected(true);
538             station.getZenithOffsetDriver().setSelected(true);
539         }
540         final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
541                                                                            propagatorBuilder);
542         final List<ObservedMeasurement<?>> measurements =
543                         EstimationTestUtils.createMeasurements(propagator,
544                                                                new AngularAzElMeasurementCreator(context),
545                                                                1.0, 3.0, 300.0);
546         propagator.clearStepHandlers();
547 
548 
549 
550         for (final ObservedMeasurement<?> measurement : measurements) {
551             final AbsoluteDate date = measurement.getDate();
552 
553             final SpacecraftState refState = propagator.propagate(date);
554 
555             AngularAzEl angular = (AngularAzEl) measurement;
556             EstimatedMeasurement<AngularAzEl> evalNoMod = angular.estimate(0, 0, new SpacecraftState[] { refState });
557 
558             // get the altitude of the station (in kilometers)
559             final double altitude = angular.getStation().getBaseFrame().getPoint().getAltitude() / 1000.;
560 
561             final AngularRadioRefractionModifier modifier = new AngularRadioRefractionModifier(new EarthITU453AtmosphereRefraction(altitude));
562             // add modifier
563             angular.addModifier(modifier);
564             //
565             EstimatedMeasurement<AngularAzEl> eval = angular.estimate(0, 0, new SpacecraftState[] { refState });
566 
567             final double diffEl = MathUtils.normalizeAngle(eval.getEstimatedValue()[1], evalNoMod.getEstimatedValue()[1]) - evalNoMod.getEstimatedValue()[1];
568             // TODO: check threshold
569             Assert.assertEquals(0.0, diffEl, 1.0e-3);
570         }
571     }
572 }