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.estimation.measurements.gnss;
18  
19  import org.junit.jupiter.api.Assertions;
20  import org.junit.jupiter.api.BeforeEach;
21  import org.junit.jupiter.api.Test;
22  import org.orekit.Utils;
23  import org.orekit.data.DataFilter;
24  import org.orekit.data.DataSource;
25  import org.orekit.data.GzipFilter;
26  import org.orekit.data.UnixCompressFilter;
27  import org.orekit.files.rinex.HatanakaCompressFilter;
28  import org.orekit.files.rinex.observation.ObservationDataSet;
29  import org.orekit.files.rinex.observation.RinexObservationParser;
30  import org.orekit.gnss.PredefinedGnssSignal;
31  import org.orekit.time.AbsoluteDate;
32  import org.orekit.time.TimeScalesFactory;
33  
34  import java.io.File;
35  import java.io.FileInputStream;
36  import java.io.IOException;
37  import java.net.URISyntaxException;
38  import java.nio.file.Files;
39  import java.util.Arrays;
40  import java.util.List;
41  
42  
43  public class PhaseMinusCodeCycleSlipDetectorTest {
44  
45      @BeforeEach
46      public void setUp() {
47          Utils.setDataRoot("regular-data");
48      }
49  
50      @Test
51      public void testTheNumberOFCycleFind() throws URISyntaxException, IOException {
52  
53          final String inputPath = GeometryFreeCycleSlipDetectorTest.class.getClassLoader().getResource("gnss/cycleSlip/seat0440.16d.Z").toURI().getPath();
54          final File input  = new File(inputPath);
55          String fileName = "seat0440.16d.Z";
56          DataSource nd = new DataSource(fileName,
57                                       () -> Files.newInputStream(new File(input.getParentFile(), fileName).toPath()));
58          for (final DataFilter filter : Arrays.asList(new GzipFilter(),
59                                                       new UnixCompressFilter(),
60                                                       new HatanakaCompressFilter())) {
61              nd = filter.filter(nd);
62          }
63          final RinexObservationParser parser = new RinexObservationParser();
64          final List<ObservationDataSet> obserDataSets = parser.parse(nd).getObservationDataSets();
65          PhaseMinusCodeCycleSlipDetector slipDetectors =
66              new PhaseMinusCodeCycleSlipDetector(90, 10, 20, 3);
67          final List<CycleSlipDetectorResults> results = slipDetectors.detect(obserDataSets);
68          for(CycleSlipDetectorResults d: results) {
69              switch(getPrn(d)) {
70                  case 1:
71                      Assertions.assertEquals(19.0, d.getEndDate(PredefinedGnssSignal.G01).durationFrom(new AbsoluteDate(2016,  2, 13,  5,  0,  0.0000000, TimeScalesFactory.getTAI())),1e-9);
72                      Assertions.assertEquals(19.0, d.getBeginDate(PredefinedGnssSignal.G01).durationFrom(new AbsoluteDate(2016,2, 13  ,2  ,33 ,30.0000000, TimeScalesFactory.getTAI())),1e-9);
73  
74                      Assertions.assertEquals(19.0, d.getEndDate(PredefinedGnssSignal.G02).durationFrom(new AbsoluteDate(2016,  2, 13,  5,  0,  0.0000000, TimeScalesFactory.getTAI())),1e-9);
75                      Assertions.assertEquals(19.0, d.getBeginDate(PredefinedGnssSignal.G02).durationFrom(new AbsoluteDate(2016,2, 13  ,2  ,40 ,00.0000000, TimeScalesFactory.getTAI())),1e-9);
76                      break;
77  
78                  case 5:
79                      Assertions.assertEquals(19.0, d.getEndDate(PredefinedGnssSignal.G01).durationFrom(new AbsoluteDate(2016,  2, 13,  2, 44, 30.0000000, TimeScalesFactory.getTAI())),1e-9);
80                      Assertions.assertEquals(19.0, d.getBeginDate(PredefinedGnssSignal.G01).durationFrom(new AbsoluteDate(2016,2, 13  ,2  ,31 ,30.0000000, TimeScalesFactory.getTAI())),1e-9);
81  
82                      Assertions.assertEquals(19.0, d.getEndDate(PredefinedGnssSignal.G02).durationFrom(new AbsoluteDate(2016,  2, 13,  2, 44, 30.0000000, TimeScalesFactory.getTAI())),1e-9);
83                      Assertions.assertEquals(19.0, d.getBeginDate(PredefinedGnssSignal.G02).durationFrom(new AbsoluteDate(2016,2, 13  ,2  ,31 ,30.0000000, TimeScalesFactory.getTAI())),1e-9);
84                      break;
85  
86                  case 6:
87                      Assertions.assertEquals(19.0, d.getEndDate(PredefinedGnssSignal.G01).durationFrom(new AbsoluteDate(2016, 2, 13,  5,  0,  0.0000000, TimeScalesFactory.getTAI())),1e-9);
88                      Assertions.assertEquals(19.0, d.getBeginDate(PredefinedGnssSignal.G01).durationFrom(new AbsoluteDate(2016,  2, 13,  4, 28, 30.0000000, TimeScalesFactory.getTAI())),1e-9);
89  
90                      Assertions.assertEquals(19.0, d.getEndDate(PredefinedGnssSignal.G02).durationFrom(new AbsoluteDate(2016, 2, 13,  5,  0,  0.0000000, TimeScalesFactory.getTAI())),1e-9);
91                      Assertions.assertEquals(19.0, d.getBeginDate(PredefinedGnssSignal.G02).durationFrom(new AbsoluteDate(2016,  2, 13,  4, 31, 0.0000000, TimeScalesFactory.getTAI())),1e-9);
92                      break;
93  
94                  case 7:
95                      Assertions.assertEquals(19.0, d.getEndDate(PredefinedGnssSignal.G01).durationFrom(new AbsoluteDate(2016,  2, 13,  4, 13,  30.0000000, TimeScalesFactory.getTAI())),1e-9);
96                      Assertions.assertEquals(19.0, d.getBeginDate(PredefinedGnssSignal.G01).durationFrom(new AbsoluteDate(2016,2, 13  ,2  ,31 ,30.0000000, TimeScalesFactory.getTAI())),1e-9);
97  
98                      Assertions.assertEquals(19.0, d.getEndDate(PredefinedGnssSignal.G02).durationFrom(new AbsoluteDate(2016,  2, 13,  4, 11,  00.0000000, TimeScalesFactory.getTAI())),1e-9);
99                      Assertions.assertEquals(19.0, d.getBeginDate(PredefinedGnssSignal.G02).durationFrom(new AbsoluteDate(2016,2, 13  ,2  ,31 ,30.0000000, TimeScalesFactory.getTAI())),1e-9);
100                     break;
101 
102                 case 9:
103                     Assertions.assertEquals(19.0, d.getEndDate(PredefinedGnssSignal.G01).durationFrom(new AbsoluteDate(2016, 2, 13,  2, 32,  00.0000000, TimeScalesFactory.getTAI())),1e-9);
104                     Assertions.assertEquals(19.0, d.getBeginDate(PredefinedGnssSignal.G01).durationFrom(new AbsoluteDate(2016,2, 13  ,2  ,31 ,30.0000000, TimeScalesFactory.getTAI())),1e-9);
105 
106                     Assertions.assertEquals(19.0, d.getEndDate(PredefinedGnssSignal.G02).durationFrom(new AbsoluteDate(2016, 2, 13,  2, 31,  30.0000000, TimeScalesFactory.getTAI())),1e-9);
107                     Assertions.assertEquals(19.0, d.getBeginDate(PredefinedGnssSignal.G02).durationFrom(new AbsoluteDate(2016,2, 13  ,2  ,31 ,30.0000000, TimeScalesFactory.getTAI())),1e-9);
108                     break;
109 
110                 case 11:
111                     Assertions.assertEquals(19.0, d.getEndDate(PredefinedGnssSignal.G01).durationFrom(new AbsoluteDate(2016,  2, 13,  5,  0,  0.0000000, TimeScalesFactory.getTAI())),1e-9);
112                     Assertions.assertEquals(19.0, d.getBeginDate(PredefinedGnssSignal.G01).durationFrom(new AbsoluteDate(2016,2, 13  ,2  ,31 ,30.0000000, TimeScalesFactory.getTAI())),1e-9);
113 
114                     Assertions.assertEquals(19.0, d.getEndDate(PredefinedGnssSignal.G02).durationFrom(new AbsoluteDate(2016,  2, 13,  5,  0,  0.0000000, TimeScalesFactory.getTAI())),1e-9);
115                     Assertions.assertEquals(19.0, d.getBeginDate(PredefinedGnssSignal.G02).durationFrom(new AbsoluteDate(2016,2, 13  ,2  ,31 ,30.0000000, TimeScalesFactory.getTAI())),1e-9);
116                     break;
117 
118                 default:   break;
119             }
120         }
121     }
122 
123     //Test to verify that the cycle-slips because of data gap are computed
124     @Test
125     public void testTimeCycleSlip() throws URISyntaxException, IOException {
126 
127         final String inputPath = GeometryFreeCycleSlipDetectorTest.class.getClassLoader().getResource("gnss/cycleSlip/WithCycleSlip.16o").toURI().getPath();
128         final File input  = new File(inputPath);
129         String fileName = "WithCycleSlip.16o";
130         DataSource nd = new DataSource(fileName,
131                                      () -> Files.newInputStream(new File(input.getParentFile(), fileName).toPath()));
132         for (final DataFilter filter : Arrays.asList(new GzipFilter(),
133                                                      new UnixCompressFilter(),
134                                                      new HatanakaCompressFilter())) {
135             nd = filter.filter(nd);
136         }
137         final RinexObservationParser parser = new RinexObservationParser();
138         final List<ObservationDataSet> obserDataSets = parser.parse(nd).getObservationDataSets();
139         PhaseMinusCodeCycleSlipDetector slipDetectors =
140             new PhaseMinusCodeCycleSlipDetector(90, 1e15, 20, 3);
141         final List<CycleSlipDetectorResults> results = slipDetectors.detect(obserDataSets);
142         for(CycleSlipDetectorResults d: results) {
143             if (getPrn(d) == 1) {
144                 //The date have been created  manually within the file
145                 AbsoluteDate[] dateCycleSlipL1 = new AbsoluteDate[] {
146                         new AbsoluteDate(2016, 2, 13, 4, 37, 43.000, TimeScalesFactory.getUTC()),
147                         new AbsoluteDate(2016, 2, 13, 4, 45, 13.000, TimeScalesFactory.getUTC()),
148                         new AbsoluteDate(2016, 2, 13, 4, 54, 13.000, TimeScalesFactory.getUTC())};
149                 int i1 = 0;
150                 for (AbsoluteDate dateL1 : d.getCycleSlipMap().get(PredefinedGnssSignal.G01)) {
151                     Assertions.assertEquals(0, dateL1.compareTo(dateCycleSlipL1[i1]));
152                     i1++;
153                 }
154                 //The dates have been created manually within the file
155                 AbsoluteDate[] dateCycleSlipL2 = new AbsoluteDate[]{
156                         new AbsoluteDate(2016, 2, 13, 4, 38, 13.000, TimeScalesFactory.getUTC()),
157                         new AbsoluteDate(2016, 2, 13, 4, 41, 13.000, TimeScalesFactory.getUTC()),
158                         new AbsoluteDate(2016, 2, 13, 4, 45, 43.000, TimeScalesFactory.getUTC()),
159                         new AbsoluteDate(2016, 2, 13, 4, 54, 13.000, TimeScalesFactory.getUTC())};
160                 int i2 = 0;
161                 for (AbsoluteDate dateL2 : d.getCycleSlipMap().get(PredefinedGnssSignal.G02)) {
162                     Assertions.assertEquals(0, dateL2.compareTo(dateCycleSlipL2[i2]));
163                     i2++;
164                 }
165             }
166         }
167     }
168 
169     //Test to check the detectors find the cycle slip added to data on purpose
170     @Test
171     public void testCycleSlipDetection() throws URISyntaxException, IOException {
172         final String inputPath = GeometryFreeCycleSlipDetectorTest.class.getClassLoader().getResource("gnss/cycleSlip/WithCycleSlip.16o").toURI().getPath();
173         final File input  = new File(inputPath);
174         String fileName = "WithoutCycleSlip.16o";
175         DataSource nd = new DataSource(fileName,
176                                      () -> Files.newInputStream(new File(input.getParentFile(), fileName).toPath()));
177         for (final DataFilter filter : Arrays.asList(new GzipFilter(),
178                                                      new UnixCompressFilter(),
179                                                      new HatanakaCompressFilter())) {
180             nd = filter.filter(nd);
181         }
182         final RinexObservationParser parser = new RinexObservationParser();
183         final List<ObservationDataSet> obserDataSets = parser.parse(nd).getObservationDataSets();
184         final double dt = 31; //great time gap threshold to don't detect cycle-slip because of time gap
185         final int N = 25;
186         final int m = 2;
187         //Test on L2
188         final double[] thresholdL2 = new double[] {
189             0.4,2};
190         //The date have been computed with an excel spreadsheet.
191         final AbsoluteDate[] dateL2 = new AbsoluteDate[] {
192           new AbsoluteDate(2016, 2, 13 ,1, 43, 13.000, TimeScalesFactory.getUTC()),
193         };
194 
195         for(int i = 0; i<thresholdL2.length; i++) {
196             PhaseMinusCodeCycleSlipDetector slipDetectorsL2 =
197                             new PhaseMinusCodeCycleSlipDetector(dt, thresholdL2[i], N, m);
198             final List<CycleSlipDetectorResults> resultsL2 = slipDetectorsL2.detect(obserDataSets);
199             for(CycleSlipDetectorResults d : resultsL2) {
200                 if(i == 0) {
201                     final List<AbsoluteDate> computedDateOnL2 = d.getCycleSlipMap().get(PredefinedGnssSignal.G02);
202                     Assertions.assertEquals(3, computedDateOnL2.size());
203                     Assertions.assertEquals(0.0, computedDateOnL2.get(0).durationFrom(dateL2[i]), 0.0);
204                 } else {
205                     final List<AbsoluteDate> computedDateOnL2 = d.getCycleSlipMap().get(PredefinedGnssSignal.G02);
206                     Assertions.assertEquals(0, computedDateOnL2.size());
207                 }
208 
209             }
210         }
211         /////////////////////////////////////////////////////////////////////////////////////
212         //Test on L1
213         final double[] thresholdL1 = new double[] {
214             0.1,0.30,2};
215         //The date have been computed with an excel spreadsheet.
216         final AbsoluteDate[] dateL1 = new AbsoluteDate[] {
217             new AbsoluteDate(2016, 2, 13 ,1, 43, 13.000, TimeScalesFactory.getUTC()),
218             new AbsoluteDate(2016, 2, 13, 1, 55, 43.000, TimeScalesFactory.getUTC()),
219             new AbsoluteDate(2016, 2, 13, 2, 8, 13.000, TimeScalesFactory.getUTC())
220           };
221         for (int i=0; i<thresholdL1.length; i++) {
222             PhaseMinusCodeCycleSlipDetector slipDetectorsL1 =
223                             new PhaseMinusCodeCycleSlipDetector(dt, thresholdL1[i], N, m+1);
224             final List<CycleSlipDetectorResults> resultsL1 = slipDetectorsL1.detect(obserDataSets);
225             for(CycleSlipDetectorResults d : resultsL1) {
226                 if(i == 0) {
227                     final List<AbsoluteDate> computedDateOnL1 = d.getCycleSlipMap().get(PredefinedGnssSignal.G01);
228                     Assertions.assertEquals(3, computedDateOnL1.size());
229                     int i1 = 0;
230                     for(AbsoluteDate date: computedDateOnL1) {
231                         Assertions.assertEquals(0.0, date.durationFrom(dateL1[i1]), 1e-9);
232                         i1++;
233                     }
234                 } else if (i == 1) {
235                     final List<AbsoluteDate> computedDateOnL1 = d.getCycleSlipMap().get(PredefinedGnssSignal.G01);
236                     Assertions.assertEquals(2, computedDateOnL1.size());
237                 } else {
238                     final List<AbsoluteDate> computedDateOnL1 = d.getCycleSlipMap().get(PredefinedGnssSignal.G01);
239                     Assertions.assertEquals(0, computedDateOnL1.size());
240                 }
241             }
242         }
243     }
244 
245     /** Getter on the PRN of the satellite. */
246     private int getPrn(final CycleSlipDetectorResults d) {
247 
248         if(d.getSatelliteName().substring(6).compareTo("1")==0) {return 1;}
249         if(d.getSatelliteName().substring(6).compareTo("2")==0) {return 2;}
250         if(d.getSatelliteName().substring(6).compareTo("3")==0) {return 3;}
251         if(d.getSatelliteName().substring(6).compareTo("4")==0) {return 4;}
252         if(d.getSatelliteName().substring(6).compareTo("5")==0) {return 5;}
253         if(d.getSatelliteName().substring(6).compareTo("6")==0) {return 6;}
254         if(d.getSatelliteName().substring(6).compareTo("7")==0) {return 7;}
255         if(d.getSatelliteName().substring(6).compareTo("8")==0) {return 8;}
256         if(d.getSatelliteName().substring(6).compareTo("9")==0) {return 9;}
257         if(d.getSatelliteName().substring(6).compareTo("10")==0) {return 10;}
258         if(d.getSatelliteName().substring(6).compareTo("11")==0) {return 11;}
259         if(d.getSatelliteName().substring(6).compareTo("12")==0) {return 12;}
260         if(d.getSatelliteName().substring(6).compareTo("13")==0) {return 13;}
261         if(d.getSatelliteName().substring(6).compareTo("14")==0) {return 14;}
262         if(d.getSatelliteName().substring(6).compareTo("15")==0) {return 15;}
263         if(d.getSatelliteName().substring(6).compareTo("16")==0) {return 16;}
264         if(d.getSatelliteName().substring(6).compareTo("17")==0) {return 17;}
265         if(d.getSatelliteName().substring(6).compareTo("18")==0) {return 18;}
266         if(d.getSatelliteName().substring(6).compareTo("19")==0) {return 19;}
267         if(d.getSatelliteName().substring(6).compareTo("20")==0) {return 20;}
268         if(d.getSatelliteName().substring(6).compareTo("21")==0) {return 21;}
269         if(d.getSatelliteName().substring(6).compareTo("22")==0) {return 22;}
270         if(d.getSatelliteName().substring(6).compareTo("23")==0) {return 23;}
271         if(d.getSatelliteName().substring(6).compareTo("24")==0) {return 24;}
272         if(d.getSatelliteName().substring(6).compareTo("25")==0) {return 25;}
273         if(d.getSatelliteName().substring(6).compareTo("26")==0) {return 26;}
274         if(d.getSatelliteName().substring(6).compareTo("27")==0) {return 27;}
275         if(d.getSatelliteName().substring(6).compareTo("28")==0) {return 28;}
276         if(d.getSatelliteName().substring(6).compareTo("29")==0) {return 29;}
277         if(d.getSatelliteName().substring(6).compareTo("30")==0) {return 30;}
278         if(d.getSatelliteName().substring(6).compareTo("31")==0) {return 31;}
279         else {return 32;}
280 
281     }
282 }