AbstractCycleSlipDetector.java
/* Copyright 2002-2024 CS GROUP
* Licensed to CS GROUP (CS) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* CS licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.orekit.estimation.measurements.gnss;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.orekit.files.rinex.observation.ObservationDataSet;
import org.orekit.gnss.Frequency;
import org.orekit.gnss.SatelliteSystem;
import org.orekit.time.AbsoluteDate;
/**
* Base class for cycle-slip detectors.
* @author David Soulard
* @since 10.2
*/
public abstract class AbstractCycleSlipDetector implements CycleSlipDetectors {
/** Separator for satellite name. */
private static final String SEPARATOR = " - ";
/** Minimum number of measurement needed before being able to figure out cycle-slip occurrence.*/
private int minMeasurementNumber;
/** Maximum time lapse between two measurements without considering a cycle-slip occurred [s]. */
private final double dt;
/** List which contains all the info regarding the cycle slip. */
private List<CycleSlipDetectorResults> data;
/** List of all the things use for cycle-slip detections. */
private List<Map<Frequency, DataForDetection>> stuff;
/**
* Cycle-slip detector Abstract Constructor.
* @param dt time gap between two consecutive measurements in seconds
* (if time between two consecutive measurement is greater than dt, a cycle slip is declared)
* @param n number of measures needed before starting test if a cycle-slip occurs
*/
AbstractCycleSlipDetector(final double dt, final int n) {
this.minMeasurementNumber = n;
this.dt = dt;
this.data = new ArrayList<>();
this.stuff = new ArrayList<>();
}
/** {@inheritDoc} */
@Override
public List<CycleSlipDetectorResults> detect(final List<ObservationDataSet> observations) {
// Loop on observation data set
for (ObservationDataSet observation: observations) {
// Manage data
manageData(observation);
}
// Return the results of the cycle-slip detection
return getResults();
}
/**
* The method is in charge of collecting the measurements, manage them, and call the detection method.
* @param observation observation data set
*/
protected abstract void manageData(ObservationDataSet observation);
/**
* Get the minimum number of measurement needed before being able to figure out cycle-slip occurrence.
* @return the minimum number of measurement needed before being able to figure out cycle-slip occurrence.
*/
protected int getMinMeasurementNumber() {
return minMeasurementNumber;
}
/**
* Get the maximum time lapse between 2 measurements without considering a cycle-slip has occurring between both.
* @return the maximum time lapse between 2 measurements
*/
protected double getMaxTimeBeetween2Measurement() {
return dt;
}
/**
* Get on all the results computed by the detector (e.g.: dates of cycle-slip).
* @return all the results computed by the detector (e.g.: dates of cycle-slip).
*/
protected List<CycleSlipDetectorResults> getResults() {
return data;
}
/**
* Get the stuff (all the things needed for, the detector).
* @return return stuff
*/
protected List<Map<Frequency, DataForDetection>> getStuffReference() {
return stuff;
}
/** 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.
* @param nameSat name of the satellite (e.g. "GPS - 7")
* @param date date of the measurement
* @param value measurement at the current date
* @param freq frequency used
*/
protected void cycleSlipDataSet(final String nameSat, final AbsoluteDate date,
final double value, final Frequency freq) {
// Check if cycle-slip data are empty
if (data.isEmpty()) {
data.add(new CycleSlipDetectorResults(nameSat, date, freq));
final Map<Frequency, DataForDetection> newMap = new HashMap<>();
newMap.put(freq, new DataForDetection(value, date));
stuff.add(newMap);
} else {
if (!alreadyExist(nameSat, freq)) {
// As the couple satellite-frequency, first possibility is that the satellite already exist within the data but not at this frequency
for (CycleSlipDetectorResults r: data) {
if (r.getSatelliteName().compareTo(nameSat) == 0) {
r.addAtOtherFrequency(freq, date);
final Map<Frequency, DataForDetection> newMap = stuff.get(data.indexOf(r));
newMap.put(freq, new DataForDetection(value, date));
stuff.set(data.indexOf(r), newMap);
return;
}
}
//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
data.add(new CycleSlipDetectorResults(nameSat, date, freq));
final Map<Frequency, DataForDetection> newMap = new HashMap<>();
newMap.put(freq, new DataForDetection(value, date));
stuff.add(newMap);
} else {
// We add the value of the combination of measurements
addValue(nameSat, date, value, freq);
}
}
}
/**
* Create the name of a satellite from its PRN number and satellite System it belongs to.
* @param numSat satellite PRN number
* @param sys Satellite System of the satellite
* @return the satellite name on a specified format (e.g.: "GPS - 7")
*/
protected String setName(final int numSat, final SatelliteSystem sys) {
return sys.name() + SEPARATOR + numSat;
}
/**
* Return true if the link (defined by a frequency and a satellite) has been already built.
* @param nameSat name of the satellite (e.g.: GPS - 07 for satelite 7 of GPS constellation).
* @param freq frequency used in the link
* @return true if it already exists within attribute data
*/
private boolean alreadyExist(final String nameSat, final Frequency freq) {
if (data != null) {
for (CycleSlipDetectorResults result: data) {
if (result.getSatelliteName().compareTo(nameSat) == 0) {
return result.getCycleSlipMap().containsKey(freq);
}
}
}
return false;
}
/**
* Add a value the data.
* @param nameSat name of the satellite (satellite system - PRN)
* @param date date of the measurement
* @param value phase measurement minus code measurement
* @param frequency frequency use
*/
private void addValue(final String nameSat, final AbsoluteDate date,
final double value, final Frequency frequency) {
// Loop on cycle-slip data
for (CycleSlipDetectorResults result: data) {
// Find the good position to add the data
if (result.getSatelliteName().compareTo(nameSat) == 0 && result.getCycleSlipMap().containsKey(frequency)) {
// The date is not to far away from the last one
final Map<Frequency, DataForDetection> valuesMap = stuff.get(data.indexOf(result));
final DataForDetection detect = valuesMap.get(frequency);
detect.write = (detect.write + 1) % minMeasurementNumber;
detect.figures[detect.write] = new SlipComputationData(value, date);
result.setDate(frequency, date);
detect.canBeComputed++;
break;
}
}
}
/**
* Container for computed if cycle-slip occurs.
* @author David Soulard
*/
static class SlipComputationData {
/** Value of the measurement. */
private double value;
/** Date of measurement. */
private AbsoluteDate date;
/**
* Simple constructor.
* @param value value of the measurement
* @param date date of the measurement
*/
SlipComputationData(final double value, final AbsoluteDate date) {
this.value = value;
this.date = date;
}
/**
* Get the value of the measurement.
* @return value of the measurement
*/
protected double getValue() {
return value;
}
/**
* Get the date of measurement saved within this.
* @return date of measurement saved within this
*/
protected AbsoluteDate getDate() {
return date;
}
}
/**
* Container for all the data need for doing cycle-slip detection.
* @author David Soulard
*/
class DataForDetection {
/** Array used to compute cycle slip. */
private SlipComputationData[] figures;
/** Integer to make the array above circular. */
private int write;
/** Integer to know how many data have been added since last cycle-slip. */
private int canBeComputed;
/**
* Constructor.
* @param value measurement
* @param date date at which measurements are taken.
*/
DataForDetection(final double value, final AbsoluteDate date) {
this.figures = new SlipComputationData[minMeasurementNumber];
this.figures[0] = new SlipComputationData(value, date);
this.canBeComputed = 1;
this.write = 0;
}
/**
* Get the array of values used for computation of cycle-slip detectors.
* @return SlipComputationDatat array
*/
protected SlipComputationData[] getFiguresReference() {
return figures;
}
/**
* Get the reference of the counter of position into the array.
* @return the position on which writing should occur within the circular array figures.
*/
protected int getWrite() {
return write;
}
/**
* Get the counter on the number of measurement which have been saved up to the current date.
* @return the number of measurement which have been saved up to the current date
*/
protected int getCanBeComputed() {
return canBeComputed;
}
/**
* Reset this to the initial value when a cycle slip occurs.
* The first element is already setting with a value and a date
* @param newF new SlipComputationData[] to be used within the detector
* @param value to be added in the first element of the array
* @param date at which the value is given.
*/
protected void resetFigures(final SlipComputationData[] newF, final double value, final AbsoluteDate date) {
this.figures = newF;
this.figures[0] = new SlipComputationData(value, date);
this.write = 0;
this.canBeComputed = 1;
}
}
}