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 java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23
24 import org.orekit.files.rinex.observation.ObservationDataSet;
25 import org.orekit.gnss.GnssSignal;
26 import org.orekit.gnss.SatelliteSystem;
27 import org.orekit.time.AbsoluteDate;
28
29 /**
30 * Base class for cycle-slip detectors.
31 * @author David Soulard
32 * @since 10.2
33 */
34 public abstract class AbstractCycleSlipDetector implements CycleSlipDetectors {
35
36 /** Separator for satellite name. */
37 private static final String SEPARATOR = " - ";
38
39 /** Minimum number of measurement needed before being able to figure out cycle-slip occurrence.*/
40 private final int minMeasurementNumber;
41
42 /** Maximum time lapse between two measurements without considering a cycle-slip occurred [s]. */
43 private final double dt;
44
45 /** List which contains all the info regarding the cycle slip. */
46 private final List<CycleSlipDetectorResults> data;
47
48 /** List of all the things use for cycle-slip detections. */
49 private final List<Map<GnssSignal, DataForDetection>> stuff;
50
51 /**
52 * Cycle-slip detector Abstract Constructor.
53 * @param dt time gap between two consecutive measurements in seconds
54 * (if time between two consecutive measurement is greater than dt, a cycle slip is declared)
55 * @param n number of measures needed before starting test if a cycle-slip occurs
56 */
57 AbstractCycleSlipDetector(final double dt, final int n) {
58 this.minMeasurementNumber = n;
59 this.dt = dt;
60 this.data = new ArrayList<>();
61 this.stuff = new ArrayList<>();
62 }
63
64 /** {@inheritDoc} */
65 @Override
66 public List<CycleSlipDetectorResults> detect(final List<ObservationDataSet> observations) {
67 // Loop on observation data set
68 for (ObservationDataSet observation: observations) {
69 // Manage data
70 manageData(observation);
71 }
72 // Return the results of the cycle-slip detection
73 return getResults();
74 }
75
76 /**
77 * The method is in charge of collecting the measurements, manage them, and call the detection method.
78 * @param observation observation data set
79 */
80 protected abstract void manageData(ObservationDataSet observation);
81
82 /**
83 * Get the minimum number of measurement needed before being able to figure out cycle-slip occurrence.
84 * @return the minimum number of measurement needed before being able to figure out cycle-slip occurrence.
85 */
86 protected int getMinMeasurementNumber() {
87 return minMeasurementNumber;
88 }
89
90 /**
91 * Get the maximum time lapse between 2 measurements without considering a cycle-slip has occurring between both.
92 * @return the maximum time lapse between 2 measurements
93 */
94 protected double getMaxTimeBeetween2Measurement() {
95 return dt;
96 }
97
98 /**
99 * Get on all the results computed by the detector (e.g.: dates of cycle-slip).
100 * @return all the results computed by the detector (e.g.: dates of cycle-slip).
101 */
102 protected List<CycleSlipDetectorResults> getResults() {
103 return data;
104 }
105
106 /**
107 * Get the stuff (all the things needed for, the detector).
108 * @return return stuff
109 */
110 protected List<Map<GnssSignal, DataForDetection>> getStuffReference() {
111 return stuff;
112 }
113
114 /** Set the data: collect data at the current Date, at the current frequency, for a given satellite, add it within the attributes data and stuff.
115 * @param nameSat name of the satellite (e.g. "GPS - 7")
116 * @param date date of the measurement
117 * @param value measurement at the current date
118 * @param signal signal used
119 */
120 protected void cycleSlipDataSet(final String nameSat, final AbsoluteDate date,
121 final double value, final GnssSignal signal) {
122 // Check if cycle-slip data are empty
123 if (data.isEmpty()) {
124 data.add(new CycleSlipDetectorResults(nameSat, date, signal));
125 final Map<GnssSignal, DataForDetection> newMap = new HashMap<>();
126 newMap.put(signal, new DataForDetection(value, date));
127 stuff.add(newMap);
128 } else {
129 if (!alreadyExist(nameSat, signal)) {
130 // As the couple satellite-frequency, first possibility is that the satellite already exist within the data but not at this frequency
131 for (CycleSlipDetectorResults r: data) {
132 if (r.getSatelliteName().compareTo(nameSat) == 0) {
133 r.addAtOtherFrequency(signal, date);
134 final Map<GnssSignal, DataForDetection> newMap = stuff.get(data.indexOf(r));
135 newMap.put(signal, new DataForDetection(value, date));
136 stuff.set(data.indexOf(r), newMap);
137 return;
138 }
139 }
140 //If w've reach this point is because the name does not exist, in this case another element in the two list should be added
141 data.add(new CycleSlipDetectorResults(nameSat, date, signal));
142 final Map<GnssSignal, DataForDetection> newMap = new HashMap<>();
143 newMap.put(signal, new DataForDetection(value, date));
144 stuff.add(newMap);
145 } else {
146 // We add the value of the combination of measurements
147 addValue(nameSat, date, value, signal);
148 }
149 }
150
151 }
152
153 /**
154 * Create the name of a satellite from its PRN number and satellite System it belongs to.
155 * @param numSat satellite PRN number
156 * @param sys Satellite System of the satellite
157 * @return the satellite name on a specified format (e.g.: "GPS - 7")
158 */
159 protected String setName(final int numSat, final SatelliteSystem sys) {
160 return sys.name() + SEPARATOR + numSat;
161 }
162
163 /**
164 * Return true if the link (defined by a frequency and a satellite) has been already built.
165 * @param nameSat name of the satellite (e.g.: GPS - 07 for satelite 7 of GPS constellation).
166 * @param signal signal used in the link
167 * @return true if it already exists within attribute data
168 */
169 private boolean alreadyExist(final String nameSat, final GnssSignal signal) {
170 if (data != null) {
171 for (CycleSlipDetectorResults result: data) {
172 if (result.getSatelliteName().compareTo(nameSat) == 0) {
173 return result.getCycleSlipMap().containsKey(signal);
174 }
175 }
176 }
177 return false;
178 }
179
180 /**
181 * Add a value the data.
182 * @param nameSat name of the satellite (satellite system - PRN)
183 * @param date date of the measurement
184 * @param value phase measurement minus code measurement
185 * @param signal signal used
186 */
187 private void addValue(final String nameSat, final AbsoluteDate date,
188 final double value, final GnssSignal signal) {
189 // Loop on cycle-slip data
190 for (CycleSlipDetectorResults result: data) {
191 // Find the good position to add the data
192 if (result.getSatelliteName().compareTo(nameSat) == 0 && result.getCycleSlipMap().containsKey(signal)) {
193 // The date is not to far away from the last one
194 final Map<GnssSignal, DataForDetection> valuesMap = stuff.get(data.indexOf(result));
195 final DataForDetection detect = valuesMap.get(signal);
196 detect.write = (detect.write + 1) % minMeasurementNumber;
197 detect.figures[detect.write] = new SlipComputationData(value, date);
198 result.setDate(signal, date);
199 detect.canBeComputed++;
200 break;
201 }
202 }
203 }
204
205 /**
206 * Container for computed if cycle-slip occurs.
207 * @author David Soulard
208 */
209 static class SlipComputationData {
210
211 /** Value of the measurement. */
212 private final double value;
213
214 /** Date of measurement. */
215 private final AbsoluteDate date;
216
217 /**
218 * Simple constructor.
219 * @param value value of the measurement
220 * @param date date of the measurement
221 */
222 SlipComputationData(final double value, final AbsoluteDate date) {
223 this.value = value;
224 this.date = date;
225 }
226
227 /**
228 * Get the value of the measurement.
229 * @return value of the measurement
230 */
231 protected double getValue() {
232 return value;
233 }
234
235 /**
236 * Get the date of measurement saved within this.
237 * @return date of measurement saved within this
238 */
239 protected AbsoluteDate getDate() {
240 return date;
241 }
242 }
243
244 /**
245 * Container for all the data need for doing cycle-slip detection.
246 * @author David Soulard
247 */
248 class DataForDetection {
249
250 /** Array used to compute cycle slip. */
251 private SlipComputationData[] figures;
252
253 /** Integer to make the array above circular. */
254 private int write;
255
256 /** Integer to know how many data have been added since last cycle-slip. */
257 private int canBeComputed;
258
259 /**
260 * Constructor.
261 * @param value measurement
262 * @param date date at which measurements are taken.
263 */
264 DataForDetection(final double value, final AbsoluteDate date) {
265 this.figures = new SlipComputationData[minMeasurementNumber];
266 this.figures[0] = new SlipComputationData(value, date);
267 this.canBeComputed = 1;
268 this.write = 0;
269 }
270
271 /**
272 * Get the array of values used for computation of cycle-slip detectors.
273 * @return SlipComputationDatat array
274 */
275 protected SlipComputationData[] getFiguresReference() {
276 return figures;
277 }
278
279 /**
280 * Get the reference of the counter of position into the array.
281 * @return the position on which writing should occur within the circular array figures.
282 */
283 protected int getWrite() {
284 return write;
285 }
286
287 /**
288 * Get the counter on the number of measurement which have been saved up to the current date.
289 * @return the number of measurement which have been saved up to the current date
290 */
291 protected int getCanBeComputed() {
292 return canBeComputed;
293 }
294
295 /**
296 * Reset this to the initial value when a cycle slip occurs.
297 * The first element is already setting with a value and a date
298 * @param newF new SlipComputationData[] to be used within the detector
299 * @param value to be added in the first element of the array
300 * @param date at which the value is given.
301 */
302 protected void resetFigures(final SlipComputationData[] newF, final double value, final AbsoluteDate date) {
303 this.figures = newF;
304 this.figures[0] = new SlipComputationData(value, date);
305 this.write = 0;
306 this.canBeComputed = 1;
307 }
308
309 }
310 }