HatchFilter.java

  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.estimation.measurements.filtering;

  18. import java.util.ArrayList;

  19. import org.hipparchus.util.FastMath;

  20. /** Base class for Hatch filters
  21.  * <p>
  22.  * Hatch filters are generally used to smooth the pseudo-range measurements with a set
  23.  * of measurements, that might be combined or not, in order to mitigate the effect of multipath.
  24.  * <p>
  25.  * The original Hatch filter uses a single carrier-phase measurement as a smoothing value,
  26.  * while a divergence free combination of carrier phases can be used, as well as Doppler measurements.
  27.  * </p>
  28.  * @see "Subirana, J. S., Hernandez-Pajares, M., and José Miguel Juan Zornoza. (2013).
  29.  *       GNSS Data Processing: Fundamentals and Algorithms. European Space Agency."
  30.  *
  31.  * @see "Zhou, Z., and Li, B. (2017). Optimal Doppler-aided smoothing strategy for GNSS navigation.
  32.  *       GPS solutions, 21(1), 197-210."
  33.  *
  34.  * @author Louis Aucouturier
  35.  * @since 11.2
  36.  */
  37. abstract class HatchFilter {

  38.     /** Current index for the filter. */
  39.     private int k;

  40.     /** Boolean used to delay the reset of the filter. */
  41.     private boolean resetNextBoolean;

  42.     /** Last smoothed value for the pseudo-range. */
  43.     private double previousSmoothedCode;

  44.     /** Last smoothing value. */
  45.     private double previousSmoothingValue;

  46.     /** History of the pseudo-range value, appended at each filter iteration. */
  47.     private ArrayList<Double> codeHistory;

  48.     /** History of the smoothed pseudo-range value, appended at each filter iteration. */
  49.     private ArrayList<Double> smoothedCodeHistory;

  50.     /** Threshold for the difference between smoothed and measured values. */
  51.     private double threshold;

  52.     /** Window size of the Hatch Filter. */
  53.     private final int N;

  54.     /**
  55.      * Constructor for the Abstract Hatch Filter.
  56.      * <p>
  57.      * Initialize the variables and set the initial pseudo-range state.
  58.      * </p>
  59.      * @param threshold threshold for loss of lock detection
  60.      *                  (it represents the maximum difference between smoothed
  61.      *                  and measured values for loss of lock detection)
  62.      * @param N         window size of the Hatch Filter
  63.      */
  64.     HatchFilter(final double threshold, final int N) {
  65.         // Initialize fields
  66.         this.codeHistory         = new ArrayList<>();
  67.         this.smoothedCodeHistory = new ArrayList<>();
  68.         this.threshold           = threshold;
  69.         this.resetNextBoolean    = false;
  70.         this.k                   = 1;
  71.         this.N                   = N;
  72.     }

  73.     /**
  74.      * Get the history of the pseudo-range values used by the filter.
  75.      * @return the history of the pseudo-range values used by the filter
  76.      */
  77.     public ArrayList<Double> getCodeHistory() {
  78.         return codeHistory;
  79.     }

  80.     /**
  81.      * Get the history of the smoothed pseudo-range values used by the filter.
  82.      * @return the smoothedCodeHistory
  83.      */
  84.     public ArrayList<Double> getSmoothedCodeHistory() {
  85.         return smoothedCodeHistory;
  86.     }

  87.     /**
  88.      * Get the threshold for loss of lock detection.
  89.      * <p>
  90.      * It represents the maximum difference between smoothed
  91.      * and measured values for loss of lock detection.
  92.      * </p>
  93.      * @return the threshold for loss of lock detection
  94.      */
  95.     public double getThreshold() {
  96.         return threshold;
  97.     }

  98.     /**
  99.      * Update the previous smoothed value for the pseudo-range.
  100.      * @param smoothedCode the previous smoothed value for the pseudo-range
  101.      */
  102.     public void updatePreviousSmoothedCode(final double smoothedCode) {
  103.         this.previousSmoothedCode = smoothedCode;
  104.     }

  105.     /**
  106.      * Update the previous smoothing value.
  107.      * @param smoothingValue the previous smoothing value
  108.      */
  109.     public void updatePreviousSmoothingValue(final double smoothingValue) {
  110.         this.previousSmoothingValue = smoothingValue;
  111.     }

  112.     /**
  113.      * Add a value to the history of the pseudo-range value.
  114.      * @param codeValue the value to add to the history
  115.      */
  116.     public void addToCodeHistory(final double codeValue) {
  117.         codeHistory.add(codeValue);
  118.     }

  119.     /**
  120.      * Add a value to the history of the smoothed pseudo-range value.
  121.      * @param smoothedCodeValue the value to add to the history
  122.      */
  123.     public void addToSmoothedCodeHistory(final double smoothedCodeValue) {
  124.         smoothedCodeHistory.add(smoothedCodeValue);
  125.     }

  126.     /**
  127.      * Reset the filter in the case of a NaN phase value, skipping the smoothing at the present instant
  128.      * and initializing at the next one, keeping the current code value.
  129.      *
  130.      * @param codeValue pseudo range value before the reset.
  131.      * */
  132.     public void resetFilterNext(final double codeValue) {
  133.         resetK();
  134.         resetNextBoolean();
  135.         updatePreviousSmoothingValue(Double.NaN);
  136.         updatePreviousSmoothedCode(codeValue);
  137.     }

  138.     /**
  139.      * Set the flag for resetting the filter to true.
  140.      */
  141.     protected void resetNextBoolean() {
  142.         this.resetNextBoolean = true;
  143.     }

  144.     /**
  145.      * Reset the current index of the filter to 1.
  146.      */
  147.     protected void resetK() {
  148.         this.k = 1;
  149.     }

  150.     /**
  151.      * Computes the smoothed code value.
  152.      * <p>
  153.      * This method corresponds to Eq. (4.23) of
  154.      * "Subirana, J. S., Hernandez-Pajares, M., and José Miguel Juan Zornoza.
  155.      *  GNSS Data Processing: Fundamentals and Algorithms.
  156.      *  European Space Agency."
  157.      * </p>
  158.      * @param codeValue value of the input code measurement (non-smoothed)
  159.      * @param smoothingValue value of the measurement used to smooth the code measurement
  160.      * @return the smoothed code value
  161.      */
  162.     protected double smoothedCode(final double codeValue,
  163.                                   final double smoothingValue) {
  164.         // Equation 4.23
  165.         return (codeValue + (k - 1) * (previousSmoothedCode + (smoothingValue - previousSmoothingValue))) / k;
  166.     }

  167.     /**
  168.      * Checks if need to reset the filter or if cycle slip occurred.
  169.      * <p>
  170.      * If yes, the smoothed value is reinitialized to the last code value.
  171.      * If no, the smoothed value value is not changed and the filter counter is updated.
  172.      * </p>
  173.      * @param codeValue value of the input code measurement (non-smoothed)
  174.      * @param smoothedValue the smoothed code value
  175.      * @param cycleSlip true if cycle slip is detected
  176.      * @return the smoothed or non-smoothed value depending if the filter must be reseted
  177.      */
  178.     protected double checkValidData(final double codeValue, final double smoothedValue, final boolean cycleSlip) {

  179.         // Initialize the returned value to the smoothed value
  180.         double validValue = smoothedValue;

  181.         // This algorithm must be initialised every time that a carrier phase cycle slip occurs
  182.         // Check if need to reset the filter
  183.         if (resetNextBoolean || cycleSlip || FastMath.abs(smoothedValue - codeValue) > threshold) {
  184.             // The smoothed value is updated to the non smoothed value, and reset the filter counter to 1
  185.             resetK();
  186.             validValue = codeValue;
  187.         } else {
  188.             // No reset, just increase the counter
  189.             k = (k > N) ? N : k + 1;
  190.         }

  191.         // Return
  192.         return validValue;

  193.     }

  194. }