1   /* Copyright 2011-2012 Space Applications Services
2    * Licensed to CS Communication & Systèmes (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.Binary64Field;
22  import org.hipparchus.util.FastMath;
23  import org.hipparchus.util.Precision;
24  import org.junit.jupiter.api.Assertions;
25  import org.junit.jupiter.api.BeforeEach;
26  import org.junit.jupiter.api.Test;
27  import org.orekit.Utils;
28  import org.orekit.bodies.FieldGeodeticPoint;
29  import org.orekit.bodies.GeodeticPoint;
30  import org.orekit.errors.OrekitException;
31  import org.orekit.errors.OrekitMessages;
32  import org.orekit.models.earth.weather.ConstantPressureTemperatureHumidityProvider;
33  import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity;
34  import org.orekit.models.earth.weather.PressureTemperatureHumidity;
35  import org.orekit.models.earth.weather.PressureTemperatureHumidityProvider;
36  import org.orekit.time.AbsoluteDate;
37  import org.orekit.time.FieldAbsoluteDate;
38  import org.orekit.utils.FieldTrackingCoordinates;
39  import org.orekit.utils.TrackingCoordinates;
40  
41  
42  public class ModifiedSaastamoinenModelTest {
43  
44      private static double epsilon = 1e-6;
45  
46      private double[][] expectedValues;
47  
48      private double[] elevations;
49  
50      private double[] heights;
51  
52      @Test
53      public void testIssue1078() {
54          Utils.setDataRoot("atmosphere");
55          try {
56              final double altitude      = 0.0;
57              final double temperature   = 273.15 + 18;
58              final double pressure      = TroposphericModelUtils.HECTO_PASCAL.toSI(1013.25);
59              final double humidity      = 50;
60              final double waterPressure = ModifiedSaastamoinenModel.WATER.waterVaporPressure(pressure,
61                  temperature,
62                  humidity);
63              final PressureTemperatureHumidity pth = new PressureTemperatureHumidity(altitude, pressure, temperature, waterPressure,
64                  Double.NaN,
65                  Double.NaN);
66              final PressureTemperatureHumidityProvider pthProvider = new ConstantPressureTemperatureHumidityProvider(pth);
67              new ModifiedSaastamoinenModel(pthProvider, null);
68          } catch (OrekitException oe) {
69              Assertions.assertEquals(OrekitMessages.INVALID_PARAMETER_RANGE, oe.getSpecifier());
70          }
71      }
72  
73      @Test
74      public void testFixedElevation() {
75          Utils.setDataRoot("atmosphere");
76          ModifiedSaastamoinenModel model = ModifiedSaastamoinenModel.getStandardModel();
77          double lastDelay = Double.MAX_VALUE;
78          // delay shall decline with increasing height of the station
79          for (double height = 0; height < 5000; height += 100) {
80              final double delay = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(5), 0.0),
81                                                   new GeodeticPoint(0.0, 0.0, height),
82                                                   TroposphericModelUtils.STANDARD_ATMOSPHERE,
83                                                   null, AbsoluteDate.J2000_EPOCH).getDelay();
84              Assertions.assertTrue(Precision.compareTo(delay, lastDelay, epsilon) < 0);
85              lastDelay = delay;
86          }
87      }
88  
89      @Test
90      public void testFieldFixedElevation() {
91          doTestFieldFixedElevation(Binary64Field.getInstance());
92      }
93  
94      private <T extends CalculusFieldElement<T>> void doTestFieldFixedElevation(final Field<T> field) {
95          final T zero = field.getZero();
96          Utils.setDataRoot("atmosphere");
97          ModifiedSaastamoinenModel model = ModifiedSaastamoinenModel.getStandardModel();
98          T lastDelay = zero.add(Double.MAX_VALUE);
99          // delay shall decline with increasing height of the station
100         for (double height = 0; height < 5000; height += 100) {
101             final T delay = model.pathDelay(new FieldTrackingCoordinates<>(zero,
102                                                                            zero.newInstance(FastMath.toRadians(5)),
103                                                                            zero),
104                                             new FieldGeodeticPoint<>(zero, zero, zero.add(height)),
105                                             new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE),
106                                             null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay();
107             Assertions.assertTrue(Precision.compareTo(delay.getReal(), lastDelay.getReal(), epsilon) < 0);
108             lastDelay = delay;
109         }
110     }
111 
112     @Test
113     public void testFixedHeight() {
114         Utils.setDataRoot("atmosphere");
115         ModifiedSaastamoinenModel model = ModifiedSaastamoinenModel.getStandardModel();
116         double lastDelay = Double.MAX_VALUE;
117         // delay shall decline with increasing elevation angle
118         for (double elev = 10d; elev < 90d; elev += 8d) {
119             final double delay = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elev), 0.0),
120                                                  new GeodeticPoint(0.0, 0.0, 350.0),
121                                                  TroposphericModelUtils.STANDARD_ATMOSPHERE,
122                                                  null, AbsoluteDate.J2000_EPOCH).getDelay();
123             Assertions.assertTrue(Precision.compareTo(delay, lastDelay, epsilon) < 0);
124             lastDelay = delay;
125         }
126     }
127 
128     @Test
129     public void testFieldFixedHeight() {
130         doTestFieldFixedHeight(Binary64Field.getInstance());
131     }
132 
133     private <T extends CalculusFieldElement<T>> void doTestFieldFixedHeight(final Field<T> field) {
134         final T zero = field.getZero();
135         Utils.setDataRoot("atmosphere");
136         ModifiedSaastamoinenModel model = ModifiedSaastamoinenModel.getStandardModel();
137         T lastDelay = zero.add(Double.MAX_VALUE);
138         // delay shall decline with increasing elevation angle
139         for (double elev = 10d; elev < 90d; elev += 8d) {
140             final T delay = model.pathDelay(new FieldTrackingCoordinates<>(zero,
141                                                                            zero.newInstance(FastMath.toRadians(elev)),
142                                                                            zero),
143                                             new FieldGeodeticPoint<>(zero, zero, zero.add(350.0)),
144                                             new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE),
145                                             null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay();
146             Assertions.assertTrue(Precision.compareTo(delay.getReal(), lastDelay.getReal(), epsilon) < 0);
147             lastDelay = delay;
148         }
149     }
150 
151     @Test
152     public void NoFile() {
153         Utils.setDataRoot("atmosphere");
154         try {
155             final double altitude      = 0.0;
156             final double temperature   = 273.15 + 18;
157             final double pressure      = TroposphericModelUtils.HECTO_PASCAL.toSI(1013.25);
158             final double humidity      = 0.5;
159             final double waterPressure = ModifiedSaastamoinenModel.WATER.waterVaporPressure(pressure,
160                                                                                             temperature,
161                                                                                             humidity);
162             final PressureTemperatureHumidity pth = new PressureTemperatureHumidity(altitude, pressure, temperature, waterPressure,
163                                                                                     Double.NaN,
164                                                                                     Double.NaN);
165             final PressureTemperatureHumidityProvider pthProvider = new ConstantPressureTemperatureHumidityProvider(pth);
166             new ModifiedSaastamoinenModel(pthProvider, "^non-existent-file$");
167             Assertions.fail("an exception should have been thrown");
168         } catch (OrekitException oe) {
169             Assertions.assertEquals(OrekitMessages.UNABLE_TO_FIND_FILE, oe.getSpecifier());
170             Assertions.assertEquals("non-existent-file", oe.getParts()[0]);
171         }
172     }
173 
174     @Test
175     public void compareDefaultAndLoaded() {
176         Utils.setDataRoot("atmosphere");
177         final double altitude      = 0.0;
178         final double temperature   = 273.15 + 18;
179         final double pressure      = TroposphericModelUtils.HECTO_PASCAL.toSI(1013.25);
180         final double humidity      = 0.5;
181         final double waterPressure = ModifiedSaastamoinenModel.WATER.waterVaporPressure(pressure,
182                                                                                         temperature,
183                                                                                         humidity);
184         final PressureTemperatureHumidity pth = new PressureTemperatureHumidity(altitude, pressure, temperature, waterPressure,
185                                                                                 Double.NaN,
186                                                                                 Double.NaN);
187         final PressureTemperatureHumidityProvider pthProvider = new ConstantPressureTemperatureHumidityProvider(pth);
188         ModifiedSaastamoinenModel defaultModel = new ModifiedSaastamoinenModel(pthProvider, null);
189         ModifiedSaastamoinenModel loadedModel  = new ModifiedSaastamoinenModel(pthProvider, ModifiedSaastamoinenModel.DELTA_R_FILE_NAME);
190         double[] heights = new double[] {
191             0.0, 250.0, 500.0, 750.0, 1000.0, 1250.0, 1500.0, 1750.0, 2000.0, 2250.0, 2500.0, 2750.0, 3000.0, 3250.0,
192             3500.0, 3750.0, 4000.0, 4250.0, 4500.0, 4750.0, 5000.0
193         };
194         double[] elevations = new double[] {
195             FastMath.toRadians(10.0), FastMath.toRadians(15.0), FastMath.toRadians(20.0),
196             FastMath.toRadians(25.0), FastMath.toRadians(30.0), FastMath.toRadians(35.0),
197             FastMath.toRadians(40.0), FastMath.toRadians(45.0), FastMath.toRadians(50.0),
198             FastMath.toRadians(55.0), FastMath.toRadians(60.0), FastMath.toRadians(65.0),
199             FastMath.toRadians(70.0), FastMath.toRadians(75.0), FastMath.toRadians(80.0),
200             FastMath.toRadians(85.0), FastMath.toRadians(90.0)
201         };
202         for (double v : heights) {
203             for (double value : elevations) {
204                 double expectedValue = defaultModel.pathDelay(new TrackingCoordinates(0.0, value, 0.0),
205                     new GeodeticPoint(0.0, 0.0, v),
206                     TroposphericModelUtils.STANDARD_ATMOSPHERE,
207                     null, AbsoluteDate.J2000_EPOCH).getDelay();
208                 double actualValue = loadedModel.pathDelay(new TrackingCoordinates(0.0, value, 0.0),
209                     new GeodeticPoint(0.0, 0.0, v),
210                     TroposphericModelUtils.STANDARD_ATMOSPHERE,
211                     null, AbsoluteDate.J2000_EPOCH).getDelay();
212                 Assertions.assertEquals(expectedValue, actualValue, epsilon, "For height=" + v + " elevation = " +
213                     FastMath.toDegrees(value) + " precision not met");
214             }
215         }
216     }
217 
218     @Test
219     public void testNegativeHeight() {
220         Utils.setDataRoot("atmosphere");
221         ModifiedSaastamoinenModel model = ModifiedSaastamoinenModel.getStandardModel();
222         final double height = -500.0;
223         for (double elevation = 0; elevation < FastMath.PI; elevation += 0.1) {
224             Assertions.assertEquals(model.pathDelay(new TrackingCoordinates(0.0, elevation, 0.0),
225                                                     new GeodeticPoint(0.0, 0.0, 0.0),
226                                                     TroposphericModelUtils.STANDARD_ATMOSPHERE,
227                                                     null, AbsoluteDate.J2000_EPOCH).getDelay(),
228                                 model.pathDelay(new TrackingCoordinates(0.0, elevation, 0.0),
229                                                 new GeodeticPoint(0.0, 0.0, height),
230                                                 TroposphericModelUtils.STANDARD_ATMOSPHERE,
231                                                 null, AbsoluteDate.J2000_EPOCH).getDelay(),
232                                 1.e-10);
233         }
234     }
235 
236     @Test
237     public void testFieldNegativeHeight() {
238         doTestFieldNegativeHeight(Binary64Field.getInstance());
239     }
240 
241     private <T extends CalculusFieldElement<T>> void doTestFieldNegativeHeight(final Field<T> field) {
242         final T zero = field.getZero();
243         Utils.setDataRoot("atmosphere");
244         ModifiedSaastamoinenModel model = ModifiedSaastamoinenModel.getStandardModel();
245         final T height = zero.subtract(500.0);
246         for (double elevation = 0; elevation < FastMath.PI; elevation += 0.1) {
247             Assertions.assertEquals(model.pathDelay(new FieldTrackingCoordinates<>(zero,
248                                                                                    zero.newInstance(elevation),
249                                                                                    zero),
250                                                     new FieldGeodeticPoint<>(zero, zero, zero),
251                                                     new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE),
252                                                     null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal(),
253                                 model.pathDelay(new FieldTrackingCoordinates<>(zero,
254                                                                                zero.newInstance(elevation),
255                                                                                zero),
256                                                 new FieldGeodeticPoint<>(zero, zero, zero.add(height)),
257                                                 new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE),
258                                                 null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal(),
259                                 1.e-10);
260         }
261     }
262 
263     @Test
264     public void testIssue654LowElevation() {
265         Utils.setDataRoot("atmosphere");
266         ModifiedSaastamoinenModel model = ModifiedSaastamoinenModel.getStandardModel();
267 
268         // Test model new setter/getter
269         model.setLowElevationThreshold(1e-3);
270         Assertions.assertEquals(1.e-3, model.getLowElevationThreshold(), 0.);
271 
272         // Reset to default value
273         model.setLowElevationThreshold(ModifiedSaastamoinenModel.DEFAULT_LOW_ELEVATION_THRESHOLD);
274         double lowElevationPathDelay = model.pathDelay(new TrackingCoordinates(0.0, 0.001, 0.0),
275                                                        new GeodeticPoint(0.0, 0.0, 0.0),
276                                                        TroposphericModelUtils.STANDARD_ATMOSPHERE,
277                                                        null, AbsoluteDate.J2000_EPOCH).getDelay();
278         Assertions.assertTrue(lowElevationPathDelay > 0.);
279         Assertions.assertEquals(model.pathDelay(new TrackingCoordinates(0.0, model.getLowElevationThreshold(), 0.0),
280                                                 new GeodeticPoint(0.0, 0.0, 0.0),
281                                                 TroposphericModelUtils.STANDARD_ATMOSPHERE,
282                                                 null, AbsoluteDate.J2000_EPOCH).getDelay(),
283                                 lowElevationPathDelay,
284                                 1.e-10);
285     }
286 
287     @Test
288     public void testIssue654FieldLowElevation() { doTestFieldLowElevation(Binary64Field.getInstance()); }
289 
290     private <T extends CalculusFieldElement<T>> void doTestFieldLowElevation(final Field<T> field) {
291         final T zero = field.getZero();
292         Utils.setDataRoot("atmosphere");
293         ModifiedSaastamoinenModel model = ModifiedSaastamoinenModel.getStandardModel();
294         final T elevation = zero.add(0.001);
295         double lowElevationPathDelay = model.pathDelay(new FieldTrackingCoordinates<>(zero, elevation, zero),
296                                                        new FieldGeodeticPoint<>(zero, zero, zero),
297                                                        new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE),
298                                                        null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal();
299         double thresholdElevationPathDelay = model.pathDelay(new FieldTrackingCoordinates<>(zero,
300                                                                                             zero.newInstance(model.getLowElevationThreshold()),
301                                                                                             zero),
302                                                              new FieldGeodeticPoint<>(zero, zero, zero),
303                                                              new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE),
304                                                              null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal();
305         Assertions.assertTrue(lowElevationPathDelay > 0.);
306         Assertions.assertEquals(thresholdElevationPathDelay, lowElevationPathDelay, 1.e-10);
307     }
308 
309     @Test
310     public void compareExpectedValues() {
311         Utils.setDataRoot("atmosphere");
312         final double pressure    = TroposphericModelUtils.HECTO_PASCAL.toSI(1013.25);
313         // it seems the reference values for the test have been computed using a wrong conversion
314         // between Celsius and Kelvin (273.16 offset instead of 273.15)
315         // which is probably due to a similar error in equation 5.97 of
316         // Guochang Xu, GPS - Theory, Algorithms and Applications, Springer, 2007
317         // so for the sake of the test, we use a temperature of 18.01°C and not the standard atmosphere at 18.00°C
318         final double altitude    = 0.0;
319         final double temperature = 273.15 + 18.01;
320         final double humidity    = 0.5;
321         final PressureTemperatureHumidity pth = new PressureTemperatureHumidity(altitude,
322                                                                                 pressure,
323                                                                                 temperature,
324                                                                                 ModifiedSaastamoinenModel.WATER.waterVaporPressure(pressure,
325                                                                                                                                    temperature,
326                                                                                                                                    humidity),
327                                                                                 Double.NaN,
328                                                                                 Double.NaN);
329         final PressureTemperatureHumidityProvider pth0Provider = new ConstantPressureTemperatureHumidityProvider(pth);
330         ModifiedSaastamoinenModel model = new ModifiedSaastamoinenModel(pth0Provider);
331 
332         for (int h = 0; h < heights.length; h++) {
333             for (int e = 0; e < elevations.length; e++) {
334                 double height = heights[h];
335                 double elevation = elevations[e];
336                 double expectedValue = expectedValues[h][e];
337                 final GeodeticPoint location = new GeodeticPoint(0.0, 0.0, height);
338                 final AbsoluteDate date = AbsoluteDate.J2000_EPOCH;
339                 double actualValue = model.pathDelay(new TrackingCoordinates(0.0, elevation, 0.0),
340                                                      location,
341                                                      pth0Provider.getWeatherParamerers(location, date),
342                                                      null, date).getDelay();
343                 Assertions.assertEquals(expectedValue, actualValue, epsilon, "For height=" + height + " elevation = " +
344                         FastMath.toDegrees(elevation) + " precision not met");
345             }
346         }
347     }
348 
349     @Test
350     public void compareFieldExpectedValues() {
351         doCompareFieldExpectedValues(Binary64Field.getInstance());
352     }
353 
354     private <T extends CalculusFieldElement<T>> void doCompareFieldExpectedValues(final Field<T> field) {
355         final T zero = field.getZero();
356         Utils.setDataRoot("atmosphere");
357         final double pressure    = TroposphericModelUtils.HECTO_PASCAL.toSI(1013.25);
358         // it seems the reference values for the test have been computed using a wrong conversion
359         // between Celsius and Kelvin (273.16 offset instead of 273.15)
360         // so for the sake of the test, we use a temperature of 18.01°C and not the standard atmosphere at 18.00°C
361         final double altitude    = 0.0;
362         final double temperature = 273.15 + 18.01;
363         final double humidity    = 0.5;
364         final PressureTemperatureHumidity pth = new PressureTemperatureHumidity(altitude,
365                                                                                 pressure,
366                                                                                 temperature,
367                                                                                 ModifiedSaastamoinenModel.WATER.waterVaporPressure(pressure,
368                                                                                                                                    temperature,
369                                                                                                                                    humidity),
370                                                                                 Double.NaN,
371                                                                                 Double.NaN);
372         final PressureTemperatureHumidityProvider pth0Provider = new ConstantPressureTemperatureHumidityProvider(pth);
373         ModifiedSaastamoinenModel model = new ModifiedSaastamoinenModel(pth0Provider);
374 
375         for (int h = 0; h < heights.length; h++) {
376             for (int e = 0; e < elevations.length; e++) {
377                 T height = zero.add(heights[h]);
378                 T elevation = zero.add(elevations[e]);
379                 double expectedValue = expectedValues[h][e];
380                 FieldGeodeticPoint<T> location = new FieldGeodeticPoint<>(zero, zero, zero.add(height));
381                 FieldAbsoluteDate<T> date = FieldAbsoluteDate.getJ2000Epoch(field);
382                 T actualValue = model.pathDelay(new FieldTrackingCoordinates<>(zero, elevation, zero),
383                                                 location,
384                                                 pth0Provider.getWeatherParamerers(location, date),
385                                                 null, date).getDelay();
386                 Assertions.assertEquals(expectedValue, actualValue.getReal(), epsilon, "For height=" + height + " elevation = " +
387                         FastMath.toDegrees(elevation.getReal()) + " precision not met");
388             }
389         }
390     }
391 
392     @Test
393     public void testIssue572() {
394         Utils.setDataRoot("atmosphere");
395         ModifiedSaastamoinenModel model = ModifiedSaastamoinenModel.getStandardModel();
396         final double height = 6000.0;
397         for (double elevation = 0; elevation < FastMath.PI; elevation += 0.1) {
398             Assertions.assertEquals(model.pathDelay(new TrackingCoordinates(0.0, elevation, 0.0),
399                                                     new GeodeticPoint(0.0, 0.0, 5000.0),
400                                                     TroposphericModelUtils.STANDARD_ATMOSPHERE,
401                                                     null, AbsoluteDate.J2000_EPOCH).getDelay(),
402                                     model.pathDelay(new TrackingCoordinates(0.0, elevation, 0.0),
403                                                     new GeodeticPoint(0.0, 0.0, height),
404                                                     TroposphericModelUtils.STANDARD_ATMOSPHERE,
405                                                     null, AbsoluteDate.J2000_EPOCH).getDelay(),
406                                     1.e-10);
407         }
408     }
409 
410     @Test
411     public void testFieldIssue572() {
412         doTestFieldIssue572(Binary64Field.getInstance());
413     }
414 
415     private <T extends CalculusFieldElement<T>> void doTestFieldIssue572(final Field<T> field) {
416         final T zero = field.getZero();
417         Utils.setDataRoot("atmosphere");
418         ModifiedSaastamoinenModel model = ModifiedSaastamoinenModel.getStandardModel();
419         final T height = zero.add(6000.0);
420         for (double elevation = 0; elevation < FastMath.PI; elevation += 0.1) {
421             Assertions.assertEquals(model.pathDelay(new FieldTrackingCoordinates<>(zero,
422                                                                                    zero.newInstance(elevation),
423                                                                                    zero),
424                                                     new FieldGeodeticPoint<>(zero, zero, zero.add(5000.0)),
425                                                     new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE),
426                                                     null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal(),
427                                     model.pathDelay(new FieldTrackingCoordinates<>(zero,
428                                                                                    zero.newInstance(elevation),
429                                                                                    zero),
430                                                     new FieldGeodeticPoint<>(zero, zero, zero.add(height)),
431                                                     new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE),
432                                                     null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal(),
433                                     1.e-10);
434         }
435     }
436 
437     @BeforeEach
438     public void setUp() throws Exception {
439         heights = new double[] {
440             0.0, 250.0, 500.0, 750.0, 1000.0, 1250.0, 1500.0, 1750.0, 2000.0, 2250.0, 2500.0, 2750.0, 3000.0, 3250.0,
441             3500.0, 3750.0, 4000.0, 4250.0, 4500.0, 4750.0, 5000.0
442         };
443 
444         elevations = new double[] {
445             FastMath.toRadians(10.0), FastMath.toRadians(15.0), FastMath.toRadians(20.0),
446             FastMath.toRadians(25.0), FastMath.toRadians(30.0), FastMath.toRadians(35.0),
447             FastMath.toRadians(40.0), FastMath.toRadians(45.0), FastMath.toRadians(50.0),
448             FastMath.toRadians(55.0), FastMath.toRadians(60.0), FastMath.toRadians(65.0),
449             FastMath.toRadians(70.0), FastMath.toRadians(75.0), FastMath.toRadians(80.0),
450             FastMath.toRadians(85.0), FastMath.toRadians(90.0)
451         };
452 
453         expectedValues = new double[][] {
454             {
455                 13.517414068807756, 9.204443522241771, 7.0029750138616835, 5.681588299211439, 4.8090544808193805,
456                 4.196707503563898, 3.7474156937027994, 3.408088733958258, 3.1468182787091985, 2.943369134588668,
457                 2.784381959210485, 2.6607786449639343, 2.5662805210588195, 2.496526411065139, 2.4485331622035984,
458                 2.420362989334734, 2.4109238764096896
459             },
460             {
461                 13.004543691989646, 8.85635958366884, 6.7385672069739835, 5.467398852004842, 4.627733401816586,
462                 4.038498891518074, 3.606157486803878, 3.279627416773643, 3.0282066103153564, 2.8324244661904134,
463                 2.6794262487842104, 2.56047665894495, 2.4695340768552505, 2.402401959167246, 2.356209754788866,
464                 2.329092816778967, 2.3200003434082928
465             },
466             {
467                 12.531363728988735, 8.534904937493899, 6.494310751299656, 5.269517663702665, 4.460196658874199,
468                 3.892306407739391, 3.475621589928269, 3.1609130970914956, 2.9185920283074447, 2.729893581384905,
469                 2.582428928493012, 2.4677793389442715, 2.380122124673593, 2.3154128046624067, 2.2708848382055904,
470                 2.2447411392156, 2.2359689784370986
471             },
472             {
473                 12.09063673875363, 8.235209195291242, 6.266604991324561, 5.084562550097057, 4.303522687504001,
474                 3.755568409663704, 3.353518885593948, 3.0498713508982442, 2.8160742841783275, 2.6340206738065692,
475                 2.4917559301110956, 2.3811563829818176, 2.2966034070866277, 2.234194258980332, 2.191259347593356,
476                 2.1660645619383274, 2.1576326612520003
477             },
478             {
479                 11.67727244511714, 7.953871771343415, 6.052791636499061, 4.910850401669602, 4.156351680934609,
480                 3.6271143683534492, 3.2388081756428404, 2.9455492155570195, 2.7197591598098305, 2.5439482552015917,
481                 2.4065694710150236, 2.2997761077823076, 2.218141111456481, 2.157894809849032, 2.1164586386563973,
482                 2.0921576169523175, 2.0840478264673044
483             },
484             {
485                 11.286432636721148, 7.688212194124222, 5.850570088713712, 4.747059102172618, 4.017661723553029,
486                 3.506085459183713, 3.1307362765144857, 2.8472616350388877, 2.6290036896596245, 2.459056474904878,
487                 2.3262584011701235, 2.223024699820715, 2.1441094810550236, 2.085868912585518, 2.0458105091517886,
488                 2.022315205377469, 2.0144705937765144
489             },
490             {
491                 10.915292255872545, 7.435769456080562, 5.6583502024136285, 4.591362033342799, 3.8858133055608204,
492                 3.391020479976734, 3.027986150306355, 2.7538117534167346, 2.5427137172039873, 2.3783406833254412,
493                 2.249897295933348, 2.1500476936060946, 2.0737181577274097, 2.0173844567055803, 1.978635920228296,
494                 1.9559066302818124, 1.9483141307804097
495             },
496             {
497                 10.560838722447652, 7.194507733765155, 5.474676340193435, 4.442196283017724, 3.759852141271757,
498                 3.281095182764508, 2.9298266864995415, 2.6645376694677676, 2.4602800254909183, 2.3012323491024245,
499                 2.1769492208902093, 2.080332602007238, 2.0064732734566384, 1.9519612730357911, 1.9144640973301363,
500                 1.8924666054100323, 1.8851149566358782
501             },
502             {
503                 10.221303680396087, 6.9632552008410915, 5.298576794548074, 4.299160340784349, 3.6390721146637293,
504                 3.175686404496119, 2.8356974323630437, 2.5789272031073223, 2.381228081225778, 2.2272865154903485,
505                 2.106992477081925, 2.0134758868645615, 1.9419852149640153, 1.8892200435803028, 1.8529228069725603,
506                 1.8316270449308856, 1.8245063513318645
507             },
508             {
509                 9.894629211990411, 6.740704058398638, 5.129125614634508, 4.161737017461952, 3.5228709097629975,
510                 3.0742748111390386, 2.7451382632192436, 2.4965641131187946, 2.305174987172354, 2.156146000844558,
511                 2.039689834231423, 1.949155743414788, 1.8799439192071106, 1.8288593407337934, 1.7937165420599064,
512                 1.7730958998798743, 1.7661974033814984
513             },
514             {
515                 9.579864280378851, 6.526143322382175, 4.965721065018929, 4.029207163135991, 3.4108058435845767,
516                 2.9764687866827866, 2.6577964388561925, 2.417125714868741, 2.2318215659378273, 2.087530132722662,
517                 1.9747751921856533, 1.8871174620329965, 1.820103416896286, 1.770639660836324, 1.7366102498117952,
518                 1.7166407238790062, 1.709956524792288
519             },
520             {
521                 9.274887896199909, 6.318551036055798, 4.807695974007752, 3.901070574943598, 3.302472329989199,
522                 2.881925175414427, 2.5733712370891264, 2.3403420235759187, 2.1609208036663294, 2.021209397507298,
523                 1.9120324913823707, 1.8271553141955852, 1.7622658032319802, 1.7143688314998151, 1.681415677692901,
524                 1.662075550126913, 1.6555984999945992
525             },
526             {
527                 8.979896361522131, 6.117657835260275, 4.654740323946152, 3.777036627543426, 3.197606518234132,
528                 2.7904044409768964, 2.4916434285107703, 2.266010367769514, 2.0922834230246177, 1.9570053034359607,
529                 1.851291869170061, 1.7691062591782465, 1.706273315177691, 1.6598930167213255, 1.627981703939375,
530                 1.6092508503238145, 1.6029743261170657
531             },
532             {
533                 8.69399537567174, 5.922971478822413, 4.5066379960473855, 3.6569305214071046, 3.0956861718504136,
534                 2.701448680145781, 2.412205508374445, 2.193765237935503, 2.025580422503155, 1.8946215440529706,
535                 1.7922869252688411, 1.7127316699243513, 1.6519133992903239, 1.6070243276625142, 1.5761438889147779,
536                 1.5580245420477885, 1.5519632546752067
537             },
538             {
539                 8.416815377025097, 5.734136251055505, 4.362963429392922, 3.5404077519897172, 2.996794592537471,
540                 2.615133166436779, 2.3351235507871273, 2.1236617698358717, 1.9608543092876316, 1.8340865056076205,
541                 1.735030640851246, 1.658028018024244, 1.5991650567003197, 1.555723443805895, 1.5258438192326151,
542                 1.5083184020062343, 1.5024665667687351
543             },
544             {
545                 8.1478867312736, 5.550837062543208, 4.223478184395355, 3.4272753528502906, 2.9007686780115858,
546                 2.531315719772937, 2.2602706846943197, 2.055584632739777, 1.8979986259230126, 1.7753006325382452,
547                 1.679428848769866, 1.60490532174873, 1.5479415024951926, 1.5059059371968162, 1.4769986856932136,
548                 1.4600505675451787, 1.4544027112557927
549             },
550             {
551                 7.886816833128103, 5.372810504566989, 4.087982930125399, 3.3173720077392095, 2.807472077886753,
552                 2.449877480332473, 2.187540848323867, 1.9894374123646559, 1.8369243760154002, 1.718180698301642,
553                 1.6254028270926364, 1.5532883580952295, 1.4981701861499142, 1.457501227681867, 1.4295392613913276,
554                 1.4131526030534576, 1.4077035129433761
555             },
556             {
557                 7.632757849842542, 5.199465832889435, 3.956158045029617, 3.2103200618121055, 2.7169983015566497,
558                 2.370922636048298, 2.1170374348830436, 1.9253162741272756, 1.7777165574325338, 1.6627979450991903,
559                 1.5730082599508786, 1.5032159322097494, 1.4498720192717967, 1.4105115179325056, 1.3834483541077152,
560                 1.3675873164708097, 1.3623112195283247
561             },
562             {
563                 7.385939247167236, 5.03097891384785, 3.8280091975097514, 3.1062430912053003, 2.629039083023718,
564                 2.294159790631063, 2.048490000181759, 1.8629731967599608, 1.720149999888605, 1.608950046027118,
565                 1.5220654734302106, 1.454530760098946, 1.402911820651357, 1.364823439078982, 1.3386341212762891,
566                 1.3232841113878862, 1.3181762050118586
567             },
568             {
569                 7.146111766814672, 4.867182513816216, 3.7034098358166165, 3.005038679030282, 2.5435078557931674,
570                 2.219513482041795, 1.981831207440737, 1.8023469685072222, 1.664168201116958, 1.5565841619968868,
571                 1.472524488341563, 1.407185083983383, 1.3572435292187972, 1.320392181006258, 1.295052611935796,
572                 1.2801995392223198, 1.2752551861465835
573             },
574             {
575                 6.913054711276373, 4.707928561310275, 3.58224790888857, 2.906616143639732, 2.460327972424674,
576                 2.14691689491338, 1.9170014355353862, 1.7433833914442447, 1.6097211330539392, 1.5056535083847744,
577                 1.4243410522646305, 1.3611366183164608, 1.3128263617229614, 1.277178068079704, 1.2526649111609156,
578                 1.238295129863562, 1.2335098392123367
579             }
580         };
581     }
582 
583 }