1   /* Copyright 2002-2024 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.gnss.metric.ntrip;
18  
19  import org.hipparchus.util.FastMath;
20  import org.junit.jupiter.api.Assertions;
21  import org.junit.jupiter.api.Test;
22  import org.orekit.errors.OrekitException;
23  import org.orekit.errors.OrekitMessages;
24  
25  import java.io.IOException;
26  import java.net.InetSocketAddress;
27  import java.net.Proxy;
28  import java.net.URISyntaxException;
29  import java.nio.file.Paths;
30  import java.util.concurrent.TimeUnit;
31  
32  public class NtripClientTest {
33  
34      @Test
35      public void testProxy() {
36          NtripClient client = new NtripClient("ntrip.example.org", NtripClient.DEFAULT_PORT);
37          client.setProxy(Proxy.Type.SOCKS, "localhost", 1080);
38          Assertions.assertEquals(Proxy.Type.SOCKS, client.getProxy().type());
39          Assertions.assertEquals("localhost", ((InetSocketAddress) client.getProxy().address()).getHostName());
40          Assertions.assertEquals(1080, ((InetSocketAddress) client.getProxy().address()).getPort());
41      }
42  
43      @Test
44      public void testUnknownProxy() {
45          final String nonExistant = "socks.invalid";
46          try {
47              NtripClient client = new NtripClient("ntrip.example.org", NtripClient.DEFAULT_PORT);
48              client.setProxy(Proxy.Type.SOCKS, nonExistant, 1080);
49              Assertions.fail("an exception should have been thrown");
50          } catch (OrekitException me) {
51              Assertions.assertEquals(OrekitMessages.UNKNOWN_HOST, me.getSpecifier());
52              Assertions.assertEquals(nonExistant, me.getParts()[0]);
53          }
54      }
55  
56      @Test
57      public void testUnknownCaster() {
58          final String nonExistant = "caster.invalid";
59          try {
60              NtripClient client = new NtripClient(nonExistant, NtripClient.DEFAULT_PORT);
61              client.setTimeout(100);
62              client.getSourceTable();
63              Assertions.fail("an exception should have been thrown");
64          } catch (OrekitException me) {
65              Assertions.assertEquals(OrekitMessages.CANNOT_PARSE_SOURCETABLE, me.getSpecifier());
66              Assertions.assertEquals(nonExistant, me.getParts()[0]);
67          }
68      }
69  
70      @Test
71      public void testWrongContentType1() {
72          try {
73              DummyServer server = prepareServer("/gnss/ntrip/wrong-content-type.txt");
74              server.run();
75              NtripClient client = new NtripClient("localhost", server.getServerPort());
76              client.setTimeout(500);
77              client.getSourceTable();
78              Assertions.fail("an exception should have been thrown");
79          } catch (OrekitException me) {
80              Assertions.assertEquals(OrekitMessages.UNEXPECTED_CONTENT_TYPE, me.getSpecifier());
81              Assertions.assertEquals("text/html", me.getParts()[0]);
82          }
83      }
84  
85      @Test
86      public void testWrongContentType2() {
87          DummyServer server = prepareServer("/gnss/ntrip/sourcetable-products.igs-ip.net.txt",
88                                             "/gnss/ntrip/wrong-content-type.txt");
89          server.run();
90          NtripClient client = new NtripClient("localhost", server.getServerPort());
91          client.setTimeout(500);
92          client.setReconnectParameters(0.001, 2.0, 2);
93          try {
94              client.startStreaming("RTCM3EPH01", Type.RTCM, false, false);
95              try {
96                  Thread.sleep(400);
97              } catch (InterruptedException ie) {
98                  // ignored
99              }
100             client.stopStreaming(100);
101             Assertions.fail("an exception should have been thrown");
102         } catch (OrekitException me) {
103             Assertions.assertEquals(OrekitMessages.UNEXPECTED_CONTENT_TYPE, me.getSpecifier());
104             Assertions.assertEquals("text/html", me.getParts()[0]);
105         }
106     }
107 
108     @Test
109     public void testOtherResponseCode() {
110         DummyServer server = prepareServer("/gnss/ntrip/sourcetable-products.igs-ip.net.txt",
111                                            "/gnss/ntrip/gone.txt");
112         server.run();
113         NtripClient client = new NtripClient("localhost", server.getServerPort());
114         client.setTimeout(500);
115         client.setReconnectParameters(0.001, 2.0, 2);
116         try {
117             client.startStreaming("RTCM3EPH01", Type.RTCM, false, false);
118             try {
119                 Thread.sleep(400);
120             } catch (InterruptedException ie) {
121                 // ignored
122             }
123             client.stopStreaming(100);
124             Assertions.fail("an exception should have been thrown");
125         } catch (OrekitException me) {
126             Assertions.assertEquals(OrekitMessages.CONNECTION_ERROR, me.getSpecifier());
127             Assertions.assertEquals("localhost", me.getParts()[0]);
128             Assertions.assertEquals("Gone", me.getParts()[1]);
129         }
130     }
131 
132     @Test
133     public void testWrongRecordType() {
134         try {
135             DummyServer server = prepareServer("/gnss/ntrip/wrong-record-type.txt");
136             server.run();
137             NtripClient client = new NtripClient("localhost", server.getServerPort());
138             client.setTimeout(500);
139             client.getSourceTable();
140             Assertions.fail("an exception should have been thrown");
141         } catch (OrekitException me) {
142             Assertions.assertEquals(OrekitMessages.SOURCETABLE_PARSE_ERROR, me.getSpecifier());
143             Assertions.assertEquals("localhost", me.getParts()[0]);
144             Assertions.assertEquals(7,           me.getParts()[1]);
145             Assertions.assertEquals("BCE;CLK01;BRDC_CoM_ITRF;RTCM 3.1;1057(60),1058(5),1059(5),1063(60),1064(5);0;GPS+GLO;MISC;DEU;50.09;8.66;0;1;RTNet;none;B;N;1400;BKG",       me.getParts()[2]);
146         }
147     }
148 
149     @Test
150     public void testLocalSourceTable() {
151         DummyServer server = prepareServer("/gnss/ntrip/sourcetable-products.igs-ip.net.txt");
152         server.run();
153         NtripClient client = new NtripClient("localhost", server.getServerPort());
154         client.setTimeout(500);
155         Assertions.assertEquals("localhost", client.getHost());
156         Assertions.assertEquals(server.getServerPort(), client.getPort());
157         SourceTable table = client.getSourceTable();
158         Assertions.assertEquals("st_filter,st_auth,st_match,st_strict,rtsp,plain_rtp", table.getNtripFlags());
159         Assertions.assertEquals( 2, table.getCasters().size());
160         Assertions.assertEquals( 2, table.getNetworks().size());
161         Assertions.assertEquals(42, table.getDataStreams().size());
162         Assertions.assertSame(table, client.getSourceTable());
163     }
164 
165     @Test
166     public void testUnknownMessage() throws Exception {
167         DummyServer server = prepareServer("/gnss/ntrip/sourcetable-products.igs-ip.net.txt",
168                                            "/gnss/ntrip/RTCM3EPH01.dat");
169         server.run();
170         NtripClient client = new NtripClient("localhost", server.getServerPort());
171         client.setTimeout(100);
172         client.setReconnectParameters(0.001, 2.0, 2);
173         final CountingObserver counter = new CountingObserver(m -> true);
174         client.addObserver(1042, "RTCM3EPH01", counter);
175         client.addObserver(1042, "RTCM3EPH01", new LoggingObserver());
176         client.startStreaming("RTCM3EPH01", Type.RTCM, false, false);
177         server.await(10, TimeUnit.SECONDS);
178         // the 31st message causes the exception
179         counter.awaitCount(30, 30 * 1000);
180         // wait a bit for next message, the 31st
181         // better condition would be to wait for StreamMonitor.exception to not be null
182         Thread.sleep(1000);
183         try {
184             client.stopStreaming(100);
185             Assertions.fail("an exception should have been thrown");
186         } catch (OrekitException oe) {
187             Assertions.assertEquals(OrekitMessages.UNKNOWN_ENCODED_MESSAGE_NUMBER, oe.getSpecifier());
188             Assertions.assertEquals("1046", oe.getParts()[0]);
189         }
190     }
191 
192     @Test
193     public void testMountPointAlreadyConnected() {
194         DummyServer server = prepareServer("/gnss/ntrip/sourcetable-products.igs-ip.net.txt",
195                                            "/gnss/ntrip/RTCM3EPH01.dat");
196         server.run();
197         NtripClient client = new NtripClient("localhost", server.getServerPort());
198         client.setTimeout(100);
199         client.setReconnectParameters(0.001, 2.0, 2);
200         client.startStreaming("RTCM3EPH01", Type.RTCM, false, false);
201         try {
202             client.startStreaming("RTCM3EPH01", Type.RTCM, false, false);
203             Assertions.fail("an exception should have been thrown");
204         } catch (OrekitException oe) {
205             Assertions.assertEquals(OrekitMessages.MOUNPOINT_ALREADY_CONNECTED, oe.getSpecifier());
206             Assertions.assertEquals("RTCM3EPH01", oe.getParts()[0]);
207         }
208     }
209 
210     @Test
211     public void testGGA() {
212         DummyServer server = prepareServer("/gnss/ntrip/sourcetable-products.igs-ip.net.txt",
213                                            "/gnss/ntrip/zero-length-response.txt");
214         server.run();
215         NtripClient client = new NtripClient("localhost", server.getServerPort());
216         client.setTimeout(100);
217         client.setFix(2, 42, 13.456, FastMath.toRadians(43.5), FastMath.toRadians(-1.25), 317.5, 12.2);
218         client.startStreaming("", Type.IGS_SSR, true, true);
219         try {
220             Thread.sleep(400);
221         } catch (InterruptedException ie) {
222             // ignored
223         }
224         client.stopStreaming(100);
225         Assertions.assertEquals("$GPGGA,024213.456,4330.0000,N,0115.0000,W,1,04,1.0,317.5,M,12.2,M,,*7A",
226                             server.getRequestProperty("Ntrip-GGA"));
227     }
228 
229     @Test
230     public void testGGA2() {
231         DummyServer server = prepareServer("/gnss/ntrip/sourcetable-products.igs-ip.net.txt",
232                                            "/gnss/ntrip/zero-length-response.txt");
233         server.run();
234         NtripClient client = new NtripClient("localhost", server.getServerPort());
235         client.setTimeout(100);
236         client.setFix(2, 42, 13.456, FastMath.toRadians(-43.5), FastMath.toRadians(1.25), 317.5, 12.2);
237         client.startStreaming("", Type.IGS_SSR, true, true);
238         try {
239             Thread.sleep(400);
240         } catch (InterruptedException ie) {
241             // ignored
242         }
243         client.stopStreaming(100);
244         Assertions.assertEquals("$GPGGA,024213.456,4330.0000,S,0115.0000,E,1,04,1.0,317.5,M,12.2,M,,*75",
245                             server.getRequestProperty("Ntrip-GGA"));
246     }
247 
248     @Test
249     public void testNullGGA() {
250         DummyServer server = prepareServer("/gnss/ntrip/sourcetable-products.igs-ip.net.txt",
251                                            "/gnss/ntrip/zero-length-response.txt");
252         server.run();
253         NtripClient client = new NtripClient("localhost", server.getServerPort());
254         client.setTimeout(100);
255         try {
256             client.startStreaming("", Type.IGS_SSR, true, true);
257             try {
258                 Thread.sleep(400);
259             } catch (InterruptedException ie) {
260                 // ignored
261             }
262             client.stopStreaming(100);
263         } catch (OrekitException oe) {
264             Assertions.assertEquals(OrekitMessages.STREAM_REQUIRES_NMEA_FIX, oe.getSpecifier());
265         }
266     }
267 
268     @Test
269     public void testAuthenticationStream() {
270         DummyServer server = prepareServer("/gnss/ntrip/sourcetable-products.igs-ip.net.txt",
271                                            "/gnss/ntrip/requires-basic-authentication.txt");
272         server.run();
273         NtripClient client = new NtripClient("localhost", server.getServerPort());
274         client.setTimeout(100);
275         try {
276             client.startStreaming("RTCM3EPH01", Type.RTCM, false, false);
277             try {
278                 Thread.sleep(400);
279             } catch (InterruptedException ie) {
280                 // ignored
281             }
282             client.stopStreaming(100);
283             Assertions.fail("an exception should have been thrown");
284         } catch (OrekitException oe) {
285             Assertions.assertEquals(OrekitMessages.FAILED_AUTHENTICATION, oe.getSpecifier());
286             Assertions.assertEquals("RTCM3EPH01", oe.getParts()[0]);
287         }
288     }
289 
290     @Test
291     public void testAuthenticationCaster() {
292         DummyServer server = prepareServer("/gnss/ntrip/requires-basic-authentication.txt");
293         server.run();
294         NtripClient client = new NtripClient("localhost", server.getServerPort());
295         client.setTimeout(100);
296         try {
297             client.getSourceTable();
298             try {
299                 Thread.sleep(400);
300             } catch (InterruptedException ie) {
301                 // ignored
302             }
303             client.stopStreaming(100);
304             Assertions.fail("an exception should have been thrown");
305         } catch (OrekitException oe) {
306             Assertions.assertEquals(OrekitMessages.FAILED_AUTHENTICATION, oe.getSpecifier());
307             Assertions.assertEquals("caster", oe.getParts()[0]);
308         }
309     }
310 
311     @Test
312     public void testForbiddenRequest() {
313         DummyServer server = prepareServer("/gnss/ntrip/forbidden-request.txt");
314         server.run();
315         NtripClient client = new NtripClient("localhost", server.getServerPort());
316         client.setTimeout(100);
317         try {
318             client.getSourceTable();
319             try {
320                 Thread.sleep(400);
321             } catch (InterruptedException ie) {
322                 // ignored
323             }
324             client.stopStreaming(100);
325             Assertions.fail("an exception should have been thrown");
326         } catch (OrekitException oe) {
327             Assertions.assertEquals(OrekitMessages.CONNECTION_ERROR, oe.getSpecifier());
328             Assertions.assertEquals("localhost", oe.getParts()[0]);
329             Assertions.assertEquals("Forbidden", oe.getParts()[1]);
330         }
331     }
332 
333     @Test
334     public void testMissingFlags() {
335         DummyServer server = prepareServer("/gnss/ntrip/missing-flags.txt");
336         server.run();
337         NtripClient client = new NtripClient("localhost", server.getServerPort());
338         client.setTimeout(100);
339         try {
340             client.getSourceTable();
341             try {
342                 Thread.sleep(400);
343             } catch (InterruptedException ie) {
344                 // ignored
345             }
346             client.stopStreaming(100);
347             Assertions.fail("an exception should have been thrown");
348         } catch (OrekitException oe) {
349             Assertions.assertEquals(OrekitMessages.MISSING_HEADER, oe.getSpecifier());
350             Assertions.assertEquals("localhost", oe.getParts()[0]);
351             Assertions.assertEquals("Ntrip-Flags", oe.getParts()[1]);
352         }
353     }
354 
355     private DummyServer prepareServer(String... names) {
356         DummyServer server = null;
357         try {
358             final String[] fileNames = new String[names.length];
359             for (int i = 0; i < names.length; ++i) {
360                 fileNames[i] = Paths.get(getClass().getResource(names[i]).toURI()).toString();
361             }
362             server = new DummyServer(fileNames);
363         } catch (URISyntaxException | IOException e) {
364             Assertions.fail(e.getLocalizedMessage());
365         }
366         return server;
367     }
368 
369 }