AbstractCycleSlipDetector.java

  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. import java.util.ArrayList;
  19. import java.util.HashMap;
  20. import java.util.List;
  21. import java.util.Map;

  22. import org.orekit.files.rinex.observation.ObservationDataSet;
  23. import org.orekit.gnss.GnssSignal;
  24. import org.orekit.gnss.SatelliteSystem;
  25. import org.orekit.time.AbsoluteDate;

  26. /**
  27.  * Base class for cycle-slip detectors.
  28.  * @author David Soulard
  29.  * @since 10.2
  30.  */
  31. public abstract class AbstractCycleSlipDetector implements CycleSlipDetectors {

  32.     /** Separator for satellite name. */
  33.     private static final String SEPARATOR = " - ";

  34.     /** Minimum number of measurement needed before being able to figure out cycle-slip occurrence.*/
  35.     private final int minMeasurementNumber;

  36.     /** Maximum time lapse between two measurements without considering a cycle-slip occurred [s]. */
  37.     private final double dt;

  38.     /** List which contains all the info regarding the cycle slip. */
  39.     private final List<CycleSlipDetectorResults> data;

  40.     /** List of all the things use for cycle-slip detections. */
  41.     private final List<Map<GnssSignal, DataForDetection>> stuff;

  42.     /**
  43.      * Cycle-slip detector Abstract Constructor.
  44.      * @param dt time gap between two consecutive measurements in seconds
  45.      *        (if time between two consecutive measurement is greater than dt, a cycle slip is declared)
  46.      * @param n number of measures needed before starting test if a cycle-slip occurs
  47.      */
  48.     AbstractCycleSlipDetector(final double dt, final int n) {
  49.         this.minMeasurementNumber = n;
  50.         this.dt                   = dt;
  51.         this.data                 = new ArrayList<>();
  52.         this.stuff                = new ArrayList<>();
  53.     }

  54.     /** {@inheritDoc} */
  55.     @Override
  56.     public List<CycleSlipDetectorResults> detect(final List<ObservationDataSet> observations) {
  57.         // Loop on observation data set
  58.         for (ObservationDataSet observation: observations) {
  59.             // Manage data
  60.             manageData(observation);
  61.         }
  62.         // Return the results of the cycle-slip detection
  63.         return getResults();
  64.     }

  65.     /**
  66.      * The method is in charge of collecting the measurements, manage them, and call the detection method.
  67.      * @param observation observation data set
  68.      */
  69.     protected abstract void manageData(ObservationDataSet observation);

  70.     /**
  71.      * Get the minimum number of measurement needed before being able to figure out cycle-slip occurrence.
  72.      * @return the minimum number of measurement needed before being able to figure out cycle-slip occurrence.
  73.      */
  74.     protected int getMinMeasurementNumber() {
  75.         return minMeasurementNumber;
  76.     }

  77.     /**
  78.      * Get the maximum time lapse between 2 measurements without considering a cycle-slip has occurring between both.
  79.      * @return the maximum time lapse between 2 measurements
  80.      */
  81.     protected double getMaxTimeBeetween2Measurement() {
  82.         return dt;
  83.     }

  84.     /**
  85.      * Get on all the results computed by the detector (e.g.: dates of cycle-slip).
  86.      * @return  all the results computed by the detector (e.g.: dates of cycle-slip).
  87.      */
  88.     protected List<CycleSlipDetectorResults> getResults() {
  89.         return data;
  90.     }

  91.     /**
  92.      * Get the stuff (all the things needed for, the detector).
  93.      * @return return stuff
  94.      */
  95.     protected List<Map<GnssSignal, DataForDetection>> getStuffReference() {
  96.         return stuff;
  97.     }

  98.     /** 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.
  99.      * @param nameSat name of the satellite (e.g. "GPS - 7")
  100.      * @param date date of the measurement
  101.      * @param value measurement at the current date
  102.      * @param signal signal used
  103.      */
  104.     protected void cycleSlipDataSet(final String nameSat, final AbsoluteDate date,
  105.                                     final double value, final GnssSignal signal)  {
  106.         // Check if cycle-slip data are empty
  107.         if (data.isEmpty()) {
  108.             data.add(new CycleSlipDetectorResults(nameSat, date, signal));
  109.             final Map<GnssSignal, DataForDetection> newMap = new HashMap<>();
  110.             newMap.put(signal, new DataForDetection(value, date));
  111.             stuff.add(newMap);
  112.         } else {
  113.             if (!alreadyExist(nameSat, signal)) {
  114.                 // As the couple satellite-frequency, first possibility is that the satellite already exist within the data but not at this frequency
  115.                 for (CycleSlipDetectorResults r: data) {
  116.                     if (r.getSatelliteName().compareTo(nameSat) == 0) {
  117.                         r.addAtOtherFrequency(signal, date);
  118.                         final Map<GnssSignal, DataForDetection> newMap = stuff.get(data.indexOf(r));
  119.                         newMap.put(signal, new DataForDetection(value, date));
  120.                         stuff.set(data.indexOf(r), newMap);
  121.                         return;
  122.                     }
  123.                 }
  124.                 //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
  125.                 data.add(new CycleSlipDetectorResults(nameSat, date, signal));
  126.                 final Map<GnssSignal, DataForDetection> newMap = new HashMap<>();
  127.                 newMap.put(signal, new DataForDetection(value, date));
  128.                 stuff.add(newMap);
  129.             } else {
  130.                 // We add the value of the combination of measurements
  131.                 addValue(nameSat, date, value, signal);
  132.             }
  133.         }

  134.     }

  135.     /**
  136.      * Create the name of a satellite from its PRN number and satellite System it belongs to.
  137.      * @param numSat satellite PRN number
  138.      * @param sys Satellite System of the satellite
  139.      * @return the satellite name on a specified format (e.g.: "GPS - 7")
  140.      */
  141.     protected String setName(final int numSat, final SatelliteSystem sys) {
  142.         return sys.name() + SEPARATOR + numSat;
  143.     }

  144.     /**
  145.      * Return true if the link (defined by a frequency and a satellite) has been already built.
  146.      * @param nameSat name of the satellite (e.g.: GPS - 07 for satelite 7 of GPS constellation).
  147.      * @param signal signal used in the link
  148.      * @return true if it already exists within attribute data
  149.      */
  150.     private boolean alreadyExist(final String nameSat, final GnssSignal signal) {
  151.         if (data != null) {
  152.             for (CycleSlipDetectorResults result: data) {
  153.                 if (result.getSatelliteName().compareTo(nameSat) == 0) {
  154.                     return result.getCycleSlipMap().containsKey(signal);
  155.                 }
  156.             }
  157.         }
  158.         return false;
  159.     }

  160.     /**
  161.      * Add a value the data.
  162.      * @param nameSat name of the satellite (satellite system - PRN)
  163.      * @param date date of the measurement
  164.      * @param value phase measurement minus code measurement
  165.      * @param signal signal used
  166.      */
  167.     private void addValue(final String nameSat, final AbsoluteDate date,
  168.                           final double value, final GnssSignal signal) {
  169.         // Loop on cycle-slip data
  170.         for (CycleSlipDetectorResults result: data) {
  171.             // Find the good position to add the data
  172.             if (result.getSatelliteName().compareTo(nameSat) == 0 && result.getCycleSlipMap().containsKey(signal)) {
  173.                 // The date is not to far away from the last one
  174.                 final Map<GnssSignal, DataForDetection> valuesMap = stuff.get(data.indexOf(result));
  175.                 final DataForDetection detect = valuesMap.get(signal);
  176.                 detect.write                  = (detect.write + 1) % minMeasurementNumber;
  177.                 detect.figures[detect.write]  = new SlipComputationData(value, date);
  178.                 result.setDate(signal, date);
  179.                 detect.canBeComputed++;
  180.                 break;
  181.             }
  182.         }
  183.     }

  184.     /**
  185.      * Container for computed if cycle-slip occurs.
  186.      * @author David Soulard
  187.      */
  188.     static class SlipComputationData {

  189.         /** Value of the measurement. */
  190.         private final double value;

  191.         /** Date of measurement. */
  192.         private final AbsoluteDate date;

  193.         /**
  194.          * Simple constructor.
  195.          * @param value value of the measurement
  196.          * @param date date of the measurement
  197.          */
  198.         SlipComputationData(final double value, final AbsoluteDate date) {
  199.             this.value  = value;
  200.             this.date   = date;
  201.         }

  202.         /**
  203.          * Get the value of the measurement.
  204.          * @return value of the measurement
  205.          */
  206.         protected double getValue() {
  207.             return value;
  208.         }

  209.         /**
  210.          * Get the date of measurement saved within this.
  211.          * @return date of measurement saved within this
  212.          */
  213.         protected AbsoluteDate getDate() {
  214.             return date;
  215.         }
  216.     }

  217.     /**
  218.      * Container for all the data need for doing cycle-slip detection.
  219.      * @author David Soulard
  220.      */
  221.     class DataForDetection {

  222.         /** Array used to compute cycle slip. */
  223.         private SlipComputationData[] figures;

  224.         /** Integer to make the array above circular. */
  225.         private int write;

  226.         /** Integer to know how many data have been added since last cycle-slip. */
  227.         private int canBeComputed;

  228.         /**
  229.          * Constructor.
  230.          * @param value measurement
  231.          * @param date date at which measurements are taken.
  232.          */
  233.         DataForDetection(final double value, final AbsoluteDate date) {
  234.             this.figures       = new SlipComputationData[minMeasurementNumber];
  235.             this.figures[0]    = new SlipComputationData(value, date);
  236.             this.canBeComputed = 1;
  237.             this.write         = 0;
  238.         }

  239.         /**
  240.          * Get the array of values used for computation of cycle-slip detectors.
  241.          * @return SlipComputationDatat array
  242.          */
  243.         protected SlipComputationData[] getFiguresReference() {
  244.             return figures;
  245.         }

  246.         /**
  247.          * Get the reference of the counter of position into the array.
  248.          * @return the position on which writing should occur within the circular array figures.
  249.          */
  250.         protected int getWrite() {
  251.             return write;
  252.         }

  253.         /**
  254.          * Get the counter on the number of measurement which have been saved up to the current date.
  255.          * @return the number of measurement which have been saved up to the current date
  256.          */
  257.         protected int getCanBeComputed() {
  258.             return canBeComputed;
  259.         }

  260.         /**
  261.          * Reset this to the initial value when a cycle slip occurs.
  262.          * The first element is already setting with a value and a date
  263.          * @param newF new SlipComputationData[] to be used within the detector
  264.          * @param value to be added in the first element of the array
  265.          * @param date at which the value is given.
  266.          */
  267.         protected void resetFigures(final SlipComputationData[] newF, final double value, final AbsoluteDate date) {
  268.             this.figures        = newF;
  269.             this.figures[0]     = new SlipComputationData(value, date);
  270.             this.write          = 0;
  271.             this.canBeComputed  = 1;
  272.         }

  273.     }
  274. }