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.models.earth.troposphere;
18  
19  import org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.Field;
21  import org.hipparchus.util.FastMath;
22  import org.hipparchus.util.MathArrays;
23  import org.hipparchus.util.MathUtils;
24  import org.hipparchus.util.Precision;
25  import org.junit.jupiter.api.Assertions;
26  import org.junit.jupiter.api.BeforeEach;
27  import org.junit.jupiter.api.Test;
28  import org.orekit.Utils;
29  import org.orekit.bodies.FieldGeodeticPoint;
30  import org.orekit.bodies.GeodeticPoint;
31  import org.orekit.errors.OrekitException;
32  import org.orekit.models.earth.weather.PressureTemperatureHumidityProvider;
33  import org.orekit.time.AbsoluteDate;
34  import org.orekit.time.FieldAbsoluteDate;
35  import org.orekit.time.TimeScalesFactory;
36  import org.orekit.utils.FieldTrackingCoordinates;
37  import org.orekit.utils.TrackingCoordinates;
38  
39  public abstract class AbstractPathDelayTest<T extends TroposphericModel> {
40  
41      protected abstract T buildTroposphericModel(final PressureTemperatureHumidityProvider provider);
42  
43      // default values for doTestMappingFactors
44      protected AbsoluteDate                        defaultDate;
45      protected GeodeticPoint                       defaultPoint;
46      protected TrackingCoordinates                 defaultTrackingCoordinates;
47  
48      @BeforeEach
49      public void setUp() throws OrekitException {
50          Utils.setDataRoot("regular-data:atmosphere");
51          defaultDate                = new AbsoluteDate(2018, 11, 25, 12, 0, 0, TimeScalesFactory.getUTC());
52          defaultPoint               = new GeodeticPoint(FastMath.toRadians(37.5), FastMath.toRadians(277.5), 824.0);
53          defaultTrackingCoordinates = new TrackingCoordinates(0.0, FastMath.toRadians(38.0), 0.0);
54      }
55  
56      @Test
57      public abstract void testDelay();
58  
59      protected void doTestDelay(final AbsoluteDate date, final GeodeticPoint point,
60                                 final TrackingCoordinates trackingCoordinates,
61                                 final PressureTemperatureHumidityProvider provider,
62                                 final double expectedZh, final double expectedZw,
63                                 final double expectedSh, final double expectedSw,
64                                 final double expectedDelay) {
65          T model = buildTroposphericModel(provider);
66          final TroposphericDelay td = model.pathDelay(trackingCoordinates, point,
67                                                       model.getParameters(date), date);
68          Assertions.assertEquals(expectedZh,    td.getZh(),    0.0001);
69          Assertions.assertEquals(expectedZw,    td.getZw(),    0.0001);
70          Assertions.assertEquals(expectedSh,    td.getSh(),    0.0001);
71          Assertions.assertEquals(expectedSw,    td.getSw(),    0.0001);
72          Assertions.assertEquals(expectedDelay, td.getDelay(), 0.0001);
73      }
74  
75      @Test
76      public abstract void testFieldDelay();
77  
78      protected <F extends CalculusFieldElement<F>> void doTestDelay(final Field<F> field,
79                                                                     final AbsoluteDate date, final GeodeticPoint point,
80                                                                     final TrackingCoordinates trackingCoordinates,
81                                                                     final PressureTemperatureHumidityProvider provider,
82                                                                     final double expectedZh, final double expectedZw,
83                                                                     final double expectedSh, final double expectedSw,
84                                                                     final double expectedDelay) {
85          T model = buildTroposphericModel(provider);
86  
87          F zero = field.getZero();
88          FieldTrackingCoordinates<F> trackingCoordinatesF =
89              new FieldTrackingCoordinates<>(zero.newInstance(trackingCoordinates.getAzimuth()),
90                                             zero.newInstance(trackingCoordinates.getElevation()),
91                                             zero.newInstance(trackingCoordinates.getRange()));
92          FieldGeodeticPoint<F> pointF = new FieldGeodeticPoint<>(field, point);
93          FieldAbsoluteDate<F> dateF = new FieldAbsoluteDate<>(field, date);
94          final double[] parameters = model.getParameters(date);
95          F[] parametersF = MathArrays.buildArray(field, parameters.length);
96          for (int i = 0; i < parameters.length; i++) {
97              parametersF[i] = zero.newInstance(parameters[i]);
98          }
99          final FieldTroposphericDelay<F> td = model.pathDelay(trackingCoordinatesF, pointF, parametersF, dateF);
100         Assertions.assertEquals(expectedZh,    td.getZh().getReal(),    0.0001);
101         Assertions.assertEquals(expectedZw,    td.getZw().getReal(),    0.0001);
102         Assertions.assertEquals(expectedSh,    td.getSh().getReal(),    0.0001);
103         Assertions.assertEquals(expectedSw,    td.getSw().getReal(),    0.0001);
104         Assertions.assertEquals(expectedDelay, td.getDelay().getReal(), 0.0001);
105     }
106 
107     @Test
108     public abstract void testFixedHeight();
109 
110     protected void doTestFixedHeight(final PressureTemperatureHumidityProvider provider) {
111         final AbsoluteDate date = new AbsoluteDate();
112         final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(45.0), FastMath.toRadians(45.0), 350.0);
113         T model = buildTroposphericModel(provider);
114         double lastDelay = Double.MAX_VALUE;
115         // delay shall decline with increasing elevation angle
116         for (double elev = 10d; elev < 90d; elev += 8d) {
117             final double delay = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elev), 0.0),
118                                                  point,
119                                                  model.getParameters(date), date).getDelay();
120             Assertions.assertTrue(Precision.compareTo(delay, lastDelay, 1.0e-6) < 0);
121             lastDelay = delay;
122         }
123     }
124 
125     @Test
126     public abstract void testFieldFixedHeight();
127 
128     protected <F extends CalculusFieldElement<F>> void doTestFieldFixedHeight(final Field<F> field,
129                                                                               final PressureTemperatureHumidityProvider provider) {
130         final F zero = field.getZero();
131         final FieldAbsoluteDate<F> date = new FieldAbsoluteDate<>(field);
132         final FieldGeodeticPoint<F>
133             point = new FieldGeodeticPoint<>(zero.newInstance(FastMath.toRadians(45.0)),
134                                              zero.newInstance(FastMath.toRadians(45.0)),
135                                              zero.newInstance(350.0));
136         T model = buildTroposphericModel(provider);
137         F lastDelay = zero.newInstance(Double.MAX_VALUE);
138         // delay shall decline with increasing elevation angle
139         for (double elev = 10d; elev < 90d; elev += 8d) {
140             final F delay = model.pathDelay(new FieldTrackingCoordinates<>(zero,
141                                                                            zero.newInstance(FastMath.toRadians(elev)),
142                                                                            zero),
143                                             point,
144                                             model.getParameters(field), date).getDelay();
145             Assertions.assertTrue(Precision.compareTo(delay.getReal(), lastDelay.getReal(), 1.0e-6) < 0);
146             lastDelay = delay;
147         }
148     }
149 
150     @Test
151     public abstract void testFixedElevation();
152 
153     protected void doTestFixedElevation(final PressureTemperatureHumidityProvider provider) {
154         T model = buildTroposphericModel(provider);
155         double lastDelay = Double.MAX_VALUE;
156         // delay shall decline with increasing height of the station
157         for (double height = 0; height < 5000; height += 100) {
158             final double delay = model.pathDelay(defaultTrackingCoordinates,
159                                                  new GeodeticPoint(defaultPoint.getLatitude(),
160                                                                    defaultPoint.getLongitude(),
161                                                                    height),
162                                                  model.getParameters(defaultDate), defaultDate).getDelay();
163             // some models have small noise, hence the 0.1mm margin
164             Assertions.assertTrue(delay < lastDelay + 0.0001);
165             lastDelay = delay;
166         }
167     }
168 
169     @Test
170     public abstract void testFieldFixedElevation();
171 
172     protected <F extends CalculusFieldElement<F>> void doTestFieldFixedElevation(final Field<F> field,
173                                                                                  final PressureTemperatureHumidityProvider provider) {
174         final F zero = field.getZero();
175         T model = buildTroposphericModel(provider);
176         F lastDelay = zero.newInstance(Double.MAX_VALUE);
177         // delay shall decline with increasing height of the station
178         final FieldAbsoluteDate<F> date = new FieldAbsoluteDate<>(field, defaultDate);
179         for (double height = 0; height < 5000; height += 100) {
180             final F delay = model.pathDelay(new FieldTrackingCoordinates<>(field, defaultTrackingCoordinates),
181                                             new FieldGeodeticPoint<>(zero.newInstance(defaultPoint.getLatitude()),
182                                                                      zero.newInstance(defaultPoint.getLongitude()),
183                                                                      zero.newInstance(height)),
184                                             model.getParameters(date.getField(), date), date).getDelay();
185             // some models have small noise, hence the 0.1mm margin
186             Assertions.assertTrue(delay.getReal() < lastDelay.getReal() + 0.0001);
187             lastDelay = delay;
188         }
189     }
190 
191     protected void doTestVsOtherModel(final TroposphericModel referenceModel,
192                                       final TroposphericModel testedModel,
193                                       final double tolZh, final double tolZw, final double tolSh, final double tolSw) {
194         double maxErrorZh = 0;
195         double maxErrorZw = 0;
196         double maxErrorSh = 0;
197         double maxErrorSw = 0;
198         for (double elevation = FastMath.toRadians(5.0); elevation < MathUtils.SEMI_PI; elevation += 0.01) {
199             final TrackingCoordinates trackingCoordinates = new TrackingCoordinates(2.75, elevation, 1.4e6);
200             final TroposphericDelay referenceDelay = referenceModel.pathDelay(trackingCoordinates, defaultPoint,
201                                                                               referenceModel.getParameters(defaultDate),
202                                                                               defaultDate);
203             final TroposphericDelay testedDelay = testedModel.pathDelay(trackingCoordinates, defaultPoint,
204                                                                         testedModel.getParameters(defaultDate),
205                                                                         defaultDate);
206             maxErrorZh = FastMath.max(maxErrorZh, FastMath.abs(testedDelay.getZh() - referenceDelay.getZh()));
207             maxErrorZw = FastMath.max(maxErrorZw, FastMath.abs(testedDelay.getZw() - referenceDelay.getZw()));
208             maxErrorSh = FastMath.max(maxErrorSh, FastMath.abs(testedDelay.getSh() - referenceDelay.getSh()));
209             maxErrorSw = FastMath.max(maxErrorSw, FastMath.abs(testedDelay.getSw() - referenceDelay.getSw()));
210         }
211 
212         Assertions.assertEquals(0.0, maxErrorZh, tolZh);
213         Assertions.assertEquals(0.0, maxErrorZw, tolZw);
214         Assertions.assertEquals(0.0, maxErrorSh, tolSh);
215         Assertions.assertEquals(0.0, maxErrorSw, tolSw);
216 
217     }
218 
219 }