SingleFrequencySmoother.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.filtering;

  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.ObservationData;
  23. import org.orekit.files.rinex.observation.ObservationDataSet;
  24. import org.orekit.gnss.MeasurementType;
  25. import org.orekit.gnss.ObservationType;
  26. import org.orekit.gnss.SatelliteSystem;
  27. import org.orekit.time.ChronologicalComparator;

  28. /**
  29.  * Handler to perform pseudo-range smoothing using single frequency measurements.
  30.  *
  31.  * @author Louis Aucouturier
  32.  * @since 11.2
  33.  */
  34. public class SingleFrequencySmoother {

  35.     /** Window size for the hatch filter. */
  36.     private final int N;

  37.     /** Interval time between two measurements.*/
  38.     private final double integrationTime;

  39.     /** Threshold for the difference between smoothed and measured values. */
  40.     private final double threshold;

  41.     /** Type of the smoothing measurements. */
  42.     private final MeasurementType type;

  43.     /**
  44.      * Map storing the filters for each observation type.
  45.      * Observation types should not overlap for a single RINEX file.
  46.      */
  47.     private final HashMap<ObservationType, SingleFrequencyHatchFilter> mapFilters;

  48.     /**
  49.      * Map storing the filtered data for each pseudo range.
  50.      * The data is stored in the form of a list of ObservationDataSetUpdate, which itself
  51.      * stores a pseudo-range ObservationData object with the filtered value, and the initial ObservationDataSet,
  52.      * needed for further processing.
  53.      */
  54.     private final HashMap<ObservationType, List<SmoothedObservationDataSet>> mapFilteredData;

  55.     /**
  56.      * Simple constructor.
  57.      * @param type            type of the smoothing measurements
  58.      * @param threshold       threshold for loss of lock detection
  59.      *                        (represents the maximum difference between smoothed
  60.      *                        and measured values for loss of lock detection)
  61.      * @param N               window size of the Hatch Filter
  62.      * @param integrationTime time interval between two measurements (s)
  63.      */
  64.     public SingleFrequencySmoother(final MeasurementType type,
  65.                                    final double threshold, final int N,
  66.                                    final double integrationTime) {
  67.         this.mapFilteredData = new HashMap<>();
  68.         this.mapFilters      = new HashMap<>();
  69.         this.type            = type;
  70.         this.N               = N;
  71.         this.integrationTime = integrationTime;
  72.         this.threshold       = threshold;
  73.     }

  74.     /**
  75.      * Creates an Hatch filter given initial data.
  76.      * @param codeData      input code observation data
  77.      * @param smoothingData input smoothing observation data
  78.      * @param system        satellite system corresponding to the observations
  79.      * @return an Hatch filter for the input data
  80.      */
  81.     public SingleFrequencyHatchFilter createFilter(final ObservationData codeData,
  82.                                                    final ObservationData smoothingData,
  83.                                                    final SatelliteSystem system) {
  84.         // Wavelength in meters
  85.         final double wavelength = smoothingData.getObservationType().getSignal(system).getWavelength();
  86.         // Return a Single Frequency Hatch Filter
  87.         return new SingleFrequencyHatchFilter(codeData, smoothingData, type, wavelength, threshold, N, integrationTime);
  88.     }

  89.     /**
  90.      * Get the map of the filtered data.
  91.      * @return a map containing the filtered data.
  92.      */
  93.     public Map<ObservationType, List<SmoothedObservationDataSet>> getFilteredDataMap() {
  94.         return mapFilteredData;
  95.     }

  96.     /**
  97.      * Get the map storing the filters for each observation type.
  98.      * @return the map storing the filters for each observation type
  99.      */
  100.     public final Map<ObservationType, SingleFrequencyHatchFilter> getMapFilters() {
  101.         return mapFilters;
  102.     }

  103.     /**
  104.      * Copy an ObservationData object.
  105.      * @param obsData observation data to copy
  106.      * @return a copy of the input observation data
  107.      */
  108.     public ObservationData copyObservationData(final ObservationData obsData) {
  109.         return new ObservationData(obsData.getObservationType(), obsData.getValue(),
  110.                                    obsData.getLossOfLockIndicator(), obsData.getSignalStrength());
  111.     }

  112.     /**
  113.      * Applies a Single Frequency Hatch filter to a list of {@link ObservationDataSet}.
  114.      * @param listODS   input observation data sets
  115.      * @param satSystem satellite System from which to filter the pseudo-range values
  116.      * @param prnNumber PRN identifier to identify the satellite from which to filter the pseudo-range values
  117.      * @param obsType   observation type to use for filtering
  118.      */
  119.     public void filterDataSet(final List<ObservationDataSet> listODS, final SatelliteSystem satSystem,
  120.                               final int prnNumber, final ObservationType obsType) {

  121.         // Sort the list in chronological way to ensure the filter work on time ordered data.
  122.         final List<ObservationDataSet> sortedListODS = new ArrayList<>(listODS);
  123.         sortedListODS.sort(new ChronologicalComparator());

  124.         // For each data set, work on those corresponding to the PRN and Satellite system.
  125.         for (ObservationDataSet obsSet : sortedListODS) {
  126.             if (obsSet.getSatellite().getSystem() == satSystem  && obsSet.getSatellite().getPRN() == prnNumber) {
  127.                 // Get all observation data
  128.                 final List<ObservationData> listObsData = obsSet.getObservationData();
  129.                 // For each ObservationData check if usable (SNR and !(isNaN))
  130.                 for (final ObservationData obsData : listObsData) {
  131.                     final double snr = obsData.getSignalStrength();
  132.                     if (!Double.isNaN(obsData.getValue()) && (snr == 0 || snr >= 4)) {

  133.                         // Check measurement type, and if range check for the chosen smooting measurement
  134.                         final ObservationType obsTypeData = obsData.getObservationType();
  135.                         if (obsTypeData.getMeasurementType() == MeasurementType.PSEUDO_RANGE) {

  136.                             ObservationData obsDataSmoothing = null;

  137.                             for (final ObservationData obsDataSmoothingCurr : listObsData) {

  138.                                 // Iterate to find the required smoothing measurement corresponding to the observationType.
  139.                                 // Then copy the observation data to store them.
  140.                                 final ObservationType obsTypeSmoothingCurr = obsDataSmoothingCurr.getObservationType();

  141.                                 if (!Double.isNaN(obsDataSmoothingCurr.getValue()) && obsTypeSmoothingCurr == obsType) {
  142.                                     obsDataSmoothing = copyObservationData(obsDataSmoothingCurr);
  143.                                 }

  144.                             }

  145.                             // Check if the filter exist in the filter map
  146.                             SingleFrequencyHatchFilter filterObject = mapFilters.get(obsTypeData);

  147.                             // If the filter does not exist and the phase object are not null, initialize a new filter and
  148.                             // store it in the map, initialize a new list of observationDataSetUpdate, and store it in the map.
  149.                             if (filterObject == null && obsDataSmoothing != null) {
  150.                                 filterObject = createFilter(obsData, obsDataSmoothing, satSystem);
  151.                                 mapFilters.put(obsTypeData, filterObject);
  152.                                 final List<SmoothedObservationDataSet> odList = new ArrayList<>();
  153.                                 odList.add(new SmoothedObservationDataSet(obsData, obsSet));
  154.                                 mapFilteredData.put(obsTypeData, odList);
  155.                             // If filter exist, check if a phase object is null, then reset the filter at the next step,
  156.                             // else, filter the data.
  157.                             } else if (filterObject != null) {
  158.                                 if (obsDataSmoothing == null ) {
  159.                                     filterObject.resetFilterNext(obsData.getValue());
  160.                                 } else {
  161.                                     final ObservationData filteredRange = filterObject.filterData(obsData, obsDataSmoothing);
  162.                                     mapFilteredData.get(obsTypeData).add(new SmoothedObservationDataSet(filteredRange, obsSet));
  163.                                 }
  164.                             } else {
  165.                                 // If the filter does not exist and one of the phase is equal to NaN or absent
  166.                                 // just skip to the next ObservationDataSet.
  167.                             }

  168.                         }
  169.                     }
  170.                 }
  171.             }
  172.         }
  173.     }

  174. }