DualFrequencySmoother.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 java.util.HashMap;
  20. import java.util.List;

  21. import org.orekit.files.rinex.observation.ObservationData;
  22. import org.orekit.files.rinex.observation.ObservationDataSet;
  23. import org.orekit.gnss.MeasurementType;
  24. import org.orekit.gnss.ObservationType;
  25. import org.orekit.gnss.SatelliteSystem;
  26. import org.orekit.time.ChronologicalComparator;

  27. /**
  28.  * Handler to perform pseudo-range smoothing using Divergence-Free phase combinations.
  29.  *
  30.  * @author Louis Aucouturier
  31.  * @since 11.2
  32.  */
  33. public class DualFrequencySmoother {

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

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

  38.     /**
  39.      * Map storing the filters for each observation type.
  40.      * Observation types should not overlap for a single RINEX file.
  41.      */
  42.     private HashMap<ObservationType, DualFrequencyHatchFilter> mapFilters;


  43.     /**
  44.      * Map storing the filtered data for each observation type of pseudo range.
  45.      * The data is stored in the form of a list of ObservationDataSetUpdate, which itself
  46.      * stores a pseudo-range ObservationData object with the filtered value, and the initial ObservationDataSet,
  47.      * needed for further processing.
  48.      */
  49.     private HashMap<ObservationType, List<SmoothedObservationDataSet>> mapFilteredData;

  50.     /**
  51.      * Simple constructor.
  52.      * @param threshold threshold for loss of lock detection
  53.      *                  (represents the maximum difference between smoothed
  54.      *                  and measured values for loss of lock detection)
  55.      * @param N         window size of the Hatch Filter
  56.      */
  57.     public DualFrequencySmoother(final double threshold, final int N) {
  58.         this.mapFilteredData = new HashMap<>();
  59.         this.mapFilters      = new HashMap<>();
  60.         this.N               = N;
  61.         this.threshold       = threshold;
  62.     }

  63.     /**
  64.      * Creates an Hatch filter given initial data.
  65.      *
  66.      * @param codeData    input code observation data
  67.      * @param phaseDataF1 input phase observation data for the first frequency
  68.      * @param phaseDataF2 input phase observation data for the second frequency
  69.      * @param satSystem   satellite system corresponding to the observations
  70.      * @return an Hatch filter for the input data
  71.      */
  72.     public DualFrequencyHatchFilter createFilter(final ObservationData codeData,
  73.                                                  final ObservationData phaseDataF1,
  74.                                                  final ObservationData phaseDataF2,
  75.                                                  final SatelliteSystem satSystem) {
  76.         // Wavelengths in meters
  77.         final double wavelengthF1 = phaseDataF1.getObservationType().getFrequency(satSystem).getWavelength();
  78.         final double wavelengthF2 = phaseDataF2.getObservationType().getFrequency(satSystem).getWavelength();
  79.         // Return a Dual Frequency Hatch Filter
  80.         return new DualFrequencyHatchFilter(codeData, phaseDataF1, phaseDataF2, wavelengthF1, wavelengthF2, threshold, N);
  81.     }

  82.     /**
  83.      * Get the map of the filtered data.
  84.      * @return a map containing the filtered data.
  85.      */
  86.     public HashMap<ObservationType, List<SmoothedObservationDataSet>> getFilteredDataMap() {
  87.         return mapFilteredData;
  88.     }

  89.     /**
  90.      * Get the map storing the filters for each observation type.
  91.      * @return the map storing the filters for each observation type
  92.      */
  93.     public final HashMap<ObservationType, DualFrequencyHatchFilter> getMapFilters() {
  94.         return mapFilters;
  95.     }

  96.     /**
  97.      * Copy an ObservationData object.
  98.      * @param obsData observation data to copy
  99.      * @return a copy of the input observation data
  100.      */
  101.     public ObservationData copyObservationData(final ObservationData obsData) {
  102.         return new ObservationData(obsData.getObservationType(), obsData.getValue(),
  103.                                    obsData.getLossOfLockIndicator(), obsData.getSignalStrength());
  104.     }

  105.     /**
  106.      * Applies a Dual Frequency Hatch filter to a list of {@link ObservationDataSet}.
  107.      *
  108.      * @param listODS input observation data sets
  109.      * @param satSystem satellite System from which to filter the pseudo-range values
  110.      * @param prnNumber PRN identifier to identify the satellite from which to filter the pseudo-range values
  111.      * @param obsTypeF1 observation type to be used as the first frequency for filtering
  112.      * @param obsTypeF2 observation type to be used as the second frequency for filtering
  113.      */
  114.     public void filterDataSet(final List<ObservationDataSet> listODS, final SatelliteSystem satSystem, final int prnNumber,
  115.                               final ObservationType obsTypeF1, final ObservationType obsTypeF2) {

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

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

  128.                         // Check measurement type, and if range check for a phase carrier measurement of the chosen observationTypes
  129.                         final ObservationType obsTypeRange = obsData.getObservationType();
  130.                         if (obsTypeRange.getMeasurementType() == MeasurementType.PSEUDO_RANGE) {

  131.                             ObservationData obsDataPhaseF1 = null;
  132.                             ObservationData obsDataPhaseF2 = null;

  133.                             for (ObservationData obsDataPhase : listObsData) {

  134.                                 // Iterate to find the required carrier phases corresponding to the observation types.
  135.                                 // Then copy the observation data to store them.
  136.                                 final ObservationType obsTypePhase = obsDataPhase.getObservationType();

  137.                                 if (!Double.isNaN(obsDataPhase.getValue()) && obsTypePhase == obsTypeF1) {
  138.                                     obsDataPhaseF1 = copyObservationData(obsDataPhase);
  139.                                 }

  140.                                 if (!Double.isNaN(obsDataPhase.getValue()) && obsTypePhase == obsTypeF2) {
  141.                                     obsDataPhaseF2 = copyObservationData(obsDataPhase);
  142.                                 }
  143.                             }

  144.                             // Check if the filter exist in the filter map
  145.                             DualFrequencyHatchFilter filterObject = mapFilters.get(obsTypeRange);

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


  167.                         }
  168.                     }
  169.                 }
  170.             }

  171.         }
  172.     }
  173. }