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.ionosphere.nequick;
18  
19  import org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.Field;
21  import org.hipparchus.util.Binary64;
22  import org.hipparchus.util.Binary64Field;
23  import org.hipparchus.util.FastMath;
24  import org.junit.jupiter.api.Assertions;
25  import org.junit.jupiter.api.Test;
26  import org.orekit.data.DataSource;
27  import org.orekit.errors.OrekitException;
28  import org.orekit.errors.OrekitMessages;
29  
30  import java.io.StringReader;
31  
32  public class ModipGridTest {
33  
34      @Test
35      public void testGalileoGrid() {
36          final String grid = "/assets/org/orekit/nequick/modipNeQG_wrapped.asc";
37          final ModipGrid modipGrid = new ModipGrid(36, 36,
38                                                    new DataSource(grid,
39                                                                   () -> ModipGridTest.class.getResourceAsStream(grid)),
40                                                    true);
41  
42          // check non-existent points past pole
43          for (double lonDeg = -1000.0; lonDeg <= 1000.0; lonDeg += 0.125) {
44              checkValue(modipGrid, -91.0, lonDeg, -90.0);
45              checkValue(modipGrid,  91.0, lonDeg,  90.0);
46          }
47  
48          // check points just before pole
49          for (double lonDeg = -1000.0; lonDeg <= 1000.0; lonDeg += 0.125) {
50              checkValue(modipGrid, -90.0 + 1.0e-10,  lonDeg, -90.0);
51              checkValue(modipGrid,  90.0 - 1.0e-10, lonDeg,  90.0);
52          }
53  
54          // check wrapping in longitude of the first/last three columns
55          for (double latDeg = -85.0; latDeg <= 90.0; latDeg += 5.0) {
56              checkEquals(modipGrid, latDeg, -190.0, 170.0);
57              checkEquals(modipGrid, latDeg, -180.0, 180.0);
58              checkEquals(modipGrid, latDeg, -170.0, 190.0);
59          }
60  
61          // check a few points exactly on grid points
62          checkValue(modipGrid, -85.0, -190.0, -77.31);
63          checkValue(modipGrid, -85.0, -120.0, -76.81);
64          checkValue(modipGrid, -85.0,  110.0, -77.19);
65          checkValue(modipGrid,  -5.0, -110.0,   3.20);
66          checkValue(modipGrid,  60.0,   20.0,  60.85);
67  
68          // check interpolated points
69          checkValue(modipGrid,  55.1,   13.4,  57.954196);
70          checkValue(modipGrid,  60.1,   13.4,  60.849386);
71          checkValue(modipGrid,  65.1,   13.4,  63.771290);
72          checkValue(modipGrid,  55.1,   23.4,  58.088051);
73          checkValue(modipGrid,  60.1,   23.4,  60.946660);
74          checkValue(modipGrid,  65.1,   23.4,  63.837296);
75          checkValue(modipGrid,  55.1,   33.4,  58.259195);
76          checkValue(modipGrid,  60.1,   33.4,  61.087074);
77          checkValue(modipGrid,  65.1,   33.4,  63.954376);
78  
79      }
80  
81      @Test
82      public void testGalileoGridField() {
83          final String grid = "/assets/org/orekit/nequick/modipNeQG_wrapped.asc";
84          final ModipGrid modipGrid = new ModipGrid(36, 36,
85                                                    new DataSource(grid,
86                                                                   () -> ModipGridTest.class.getResourceAsStream(grid)),
87                                                    true);
88  
89          final Field<Binary64> field = Binary64Field.getInstance();
90  
91          // check non-existent points past pole
92          for (double lonDeg = -1000.0; lonDeg <= 1000.0; lonDeg += 0.125) {
93              checkValue(field, modipGrid, -91.0, lonDeg, -90.0);
94              checkValue(field, modipGrid,  91.0, lonDeg,  90.0);
95          }
96  
97          // check points just before pole
98          for (double lonDeg = -1000.0; lonDeg <= 1000.0; lonDeg += 0.125) {
99              checkValue(field, modipGrid, -90.0 + 1.0e-10,  lonDeg, -90.0);
100             checkValue(field, modipGrid,  90.0 - 1.0e-10, lonDeg,  90.0);
101         }
102 
103         // check wrapping in longitude of the first/last three columns
104         for (double latDeg = -85.0; latDeg <= 90.0; latDeg += 5.0) {
105             checkEquals(field, modipGrid, latDeg, -190.0, 170.0);
106             checkEquals(field, modipGrid, latDeg, -180.0, 180.0);
107             checkEquals(field, modipGrid, latDeg, -170.0, 190.0);
108         }
109 
110         // check a few points exactly on grid points
111         checkValue(field, modipGrid, -85.0, -190.0, -77.31);
112         checkValue(field, modipGrid, -85.0, -120.0, -76.81);
113         checkValue(field, modipGrid, -85.0,  110.0, -77.19);
114         checkValue(field, modipGrid,  -5.0, -110.0,   3.20);
115         checkValue(field, modipGrid,  60.0,   20.0,  60.85);
116 
117         // check interpolated points
118         checkValue(field, modipGrid,  55.1,   13.4,  57.954196);
119         checkValue(field, modipGrid,  60.1,   13.4,  60.849386);
120         checkValue(field, modipGrid,  65.1,   13.4,  63.771290);
121         checkValue(field, modipGrid,  55.1,   23.4,  58.088051);
122         checkValue(field, modipGrid,  60.1,   23.4,  60.946660);
123         checkValue(field, modipGrid,  65.1,   23.4,  63.837296);
124         checkValue(field, modipGrid,  55.1,   33.4,  58.259195);
125         checkValue(field, modipGrid,  60.1,   33.4,  61.087074);
126         checkValue(field, modipGrid,  65.1,   33.4,  63.954376);
127 
128     }
129 
130     @Test
131     public void testItuGrid() {
132         final String grid = "/assets/org/orekit/nequick/modip.asc";
133         final ModipGrid modipGrid = new ModipGrid(180, 180,
134                                                   new DataSource(grid,
135                                                                  () -> ModipGridTest.class.getResourceAsStream(grid)),
136                                                   false);
137 
138         // check non-existent points past pole
139         for (double lonDeg = -1000.0; lonDeg <= 1000.0; lonDeg += 0.125) {
140             checkValue(modipGrid, -91.0, lonDeg, -90.0);
141             checkValue(modipGrid,  91.0, lonDeg,  90.0);
142         }
143 
144         // check points just before pole
145         for (double lonDeg = -1000.0; lonDeg <= 1000.0; lonDeg += 0.125) {
146             checkValue(modipGrid, -90.0 + 1.0e-10,  lonDeg, -89.99);
147             checkValue(modipGrid,  90.0 - 1.0e-10, lonDeg,  89.99);
148         }
149 
150         // check wrapping in longitude of the first/last three columns
151         for (double latDeg = -85.0; latDeg <= 90.0; latDeg += 5.0) {
152             checkEquals(modipGrid, latDeg, -190.0, 170.0);
153             checkEquals(modipGrid, latDeg, -180.0, 180.0);
154             checkEquals(modipGrid, latDeg, -170.0, 190.0);
155         }
156 
157         // check a few points exactly on grid points
158         checkValue(modipGrid, -85.0, -190.0, -77.91);
159         checkValue(modipGrid, -85.0, -120.0, -77.41);
160         checkValue(modipGrid, -85.0,  110.0, -77.75);
161         checkValue(modipGrid,  -5.0, -110.0,   1.97);
162         checkValue(modipGrid,  60.0,   20.0,  60.87);
163 
164         // check interpolated points
165         checkValue(modipGrid,  55.1,   13.4,  57.975913);
166         checkValue(modipGrid,  60.1,   13.4,  60.877488);
167         checkValue(modipGrid,  65.1,   13.4,  63.776020);
168         checkValue(modipGrid,  55.1,   23.4,  58.069325);
169         checkValue(modipGrid,  60.1,   23.4,  60.961068);
170         checkValue(modipGrid,  65.1,   23.4,  63.851642);
171         checkValue(modipGrid,  55.1,   33.4,  58.262319);
172         checkValue(modipGrid,  60.1,   33.4,  61.135017);
173         checkValue(modipGrid,  65.1,   33.4,  63.995992);
174 
175     }
176 
177     @Test
178     public void testItuGridField() {
179         final String grid = "/assets/org/orekit/nequick/modip.asc";
180         final ModipGrid modipGrid = new ModipGrid(180, 180,
181                                                   new DataSource(grid,
182                                                                  () -> ModipGridTest.class.getResourceAsStream(grid)),
183                                                   false);
184 
185         final Field<Binary64> field = Binary64Field.getInstance();
186 
187         // check non-existent points past pole
188         for (double lonDeg = -1000.0; lonDeg <= 1000.0; lonDeg += 0.125) {
189             checkValue(field, modipGrid, -91.0, lonDeg, -90.0);
190             checkValue(field, modipGrid,  91.0, lonDeg,  90.0);
191         }
192 
193         // check points just before pole
194         for (double lonDeg = -1000.0; lonDeg <= 1000.0; lonDeg += 0.125) {
195             checkValue(field, modipGrid, -90.0 + 1.0e-10,  lonDeg, -89.99);
196             checkValue(field, modipGrid,  90.0 - 1.0e-10, lonDeg,  89.99);
197         }
198 
199         // check wrapping in longitude of the first/last three columns
200         for (double latDeg = -85.0; latDeg <= 90.0; latDeg += 5.0) {
201             checkEquals(field, modipGrid, latDeg, -190.0, 170.0);
202             checkEquals(field, modipGrid, latDeg, -180.0, 180.0);
203             checkEquals(field, modipGrid, latDeg, -170.0, 190.0);
204         }
205 
206         // check a few points exactly on grid points
207         checkValue(field, modipGrid, -85.0, -190.0, -77.91);
208         checkValue(field, modipGrid, -85.0, -120.0, -77.41);
209         checkValue(field, modipGrid, -85.0,  110.0, -77.75);
210         checkValue(field, modipGrid,  -5.0, -110.0,   1.97);
211         checkValue(field, modipGrid,  60.0,   20.0,  60.87);
212 
213         // check interpolated points
214         checkValue(field, modipGrid,  55.1,   13.4,  57.975913);
215         checkValue(field, modipGrid,  60.1,   13.4,  60.877488);
216         checkValue(field, modipGrid,  65.1,   13.4,  63.776020);
217         checkValue(field, modipGrid,  55.1,   23.4,  58.069325);
218         checkValue(field, modipGrid,  60.1,   23.4,  60.961068);
219         checkValue(field, modipGrid,  65.1,   23.4,  63.851642);
220         checkValue(field, modipGrid,  55.1,   33.4,  58.262319);
221         checkValue(field, modipGrid,  60.1,   33.4,  61.135017);
222         checkValue(field, modipGrid,  65.1,   33.4,  63.995992);
223 
224     }
225 
226     @Test
227     public void testNoGridData() {
228         try {
229             new ModipGrid(12, 12, new DataSource("empty", () -> new StringReader("")), false);
230             Assertions.fail("an exception should have been thrown");
231         } catch (OrekitException oe) {
232             Assertions.assertEquals(OrekitMessages.MODIP_GRID_NOT_LOADED, oe.getSpecifier());
233             Assertions.assertEquals("empty", oe.getParts()[0]);
234         }
235     }
236 
237     @Test
238     public void testParsingError() {
239         try {
240             new ModipGrid(4, 2,
241                           new DataSource("dummy", () -> new StringReader("-90 -90 -90 -90 -90\n" +
242                                                                          "  0   0  ##   0   0\n" +
243                                                                          " 90  90  90  90  90\n")),
244                           false);
245             Assertions.fail("an exception should have been thrown");
246         } catch (OrekitException oe) {
247             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier());
248             Assertions.assertEquals(2, (Integer) oe.getParts()[0]);
249             Assertions.assertEquals("dummy", oe.getParts()[1]);
250             Assertions.assertEquals("  0   0  ##   0   0", oe.getParts()[2]);
251         }
252     }
253 
254     private void checkValue(final ModipGrid grid,
255                             final double latDeg, final double lonDeg, final double expected) {
256         Assertions.assertEquals(expected,
257                                 grid.computeMODIP(FastMath.toRadians(latDeg), FastMath.toRadians(lonDeg)),
258                                 1.0e-6);
259     }
260 
261     private void checkEquals(final ModipGrid grid,
262                             final double latDeg, final double lonDeg1, final double lonDeg2) {
263         Assertions.assertEquals(grid.computeMODIP(FastMath.toRadians(latDeg), FastMath.toRadians(lonDeg1)),
264                                 grid.computeMODIP(FastMath.toRadians(latDeg), FastMath.toRadians(lonDeg2)),
265                                 1.0e-6);
266     }
267 
268     private <T extends CalculusFieldElement<T>> void checkValue(final Field<T> field,
269                                                                 final ModipGrid grid,
270                                                                 final double latDeg, final double lonDeg,
271                                                                 final double expected) {
272         Assertions.assertEquals(expected,
273                                 grid.computeMODIP(FastMath.toRadians(field.getZero().newInstance(latDeg)),
274                                                   FastMath.toRadians(field.getZero().newInstance(lonDeg))).
275                                     getReal(),
276                                 1.0e-6);
277     }
278 
279     private <T extends CalculusFieldElement<T>> void checkEquals(final Field<T> field,
280                                                                  final ModipGrid grid,
281                                                                  final double latDeg, final double lonDeg1,
282                                                                  final double lonDeg2) {
283         Assertions.assertEquals(grid.computeMODIP(FastMath.toRadians(field.getZero().newInstance(latDeg)),
284                                                   FastMath.toRadians(field.getZero().newInstance(lonDeg1))).
285                                     getReal(),
286                                 grid.computeMODIP(FastMath.toRadians(field.getZero().newInstance(latDeg)),
287                                                   FastMath.toRadians(field.getZero().newInstance(lonDeg2))).
288                                     getReal(),
289                                 1.0e-6);
290     }
291 
292 }