1   /* Copyright 2002-2021 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  
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.List;
22  
23  import org.orekit.errors.OrekitException;
24  import org.orekit.errors.OrekitMessages;
25  import org.orekit.gnss.CombinedObservationData;
26  import org.orekit.gnss.CombinedObservationDataSet;
27  import org.orekit.gnss.Frequency;
28  import org.orekit.gnss.MeasurementType;
29  import org.orekit.gnss.ObservationData;
30  import org.orekit.gnss.ObservationDataSet;
31  import org.orekit.gnss.ObservationType;
32  import org.orekit.gnss.SatelliteSystem;
33  import org.orekit.utils.Constants;
34  
35  /** Base class for dual frequency combination of measurements.
36   * @author Bryan Cazabonne
37   * @since 10.1
38   */
39  public abstract class AbstractDualFrequencyCombination implements MeasurementCombination {
40  
41      /** Mega Hertz to Hertz converter. */
42      public static final double MHZ_TO_HZ = 1.0e6;
43  
44      /** Type of combination of measurements. */
45      private final CombinationType type;
46  
47      /** Satellite system used for the combination. */
48      private final SatelliteSystem system;
49  
50      /**
51       * Constructor.
52       * @param type combination of measurements type
53       * @param system satellite system
54       */
55      protected AbstractDualFrequencyCombination(final CombinationType type, final SatelliteSystem system) {
56          this.type   = type;
57          this.system = system;
58      }
59  
60      /** {@inheritDoc} */
61      @Override
62      public String getName() {
63          return type.getName();
64      }
65  
66      /**
67       * Combines observation data using a dual frequency combination of measurements.
68       * @param od1 first observation data to combined
69       * @param od2 second observation data to combined
70       * @return a combined observation data
71       */
72      public CombinedObservationData combine(final ObservationData od1, final ObservationData od2) {
73  
74          // Observation types
75          final ObservationType obsType1 = od1.getObservationType();
76          final ObservationType obsType2 = od2.getObservationType();
77  
78          // Frequencies
79          final Frequency freq1 = obsType1.getFrequency(system);
80          final Frequency freq2 = obsType2.getFrequency(system);
81          // Check if the combination of measurements if performed for two different frequencies
82          if (freq1 == freq2) {
83              throw new OrekitException(OrekitMessages.INCOMPATIBLE_FREQUENCIES_FOR_COMBINATION_OF_MEASUREMENTS,
84                                        freq1, freq2, getName());
85          }
86  
87          // Measurements types
88          final MeasurementType measType1 = obsType1.getMeasurementType();
89          final MeasurementType measType2 = obsType2.getMeasurementType();
90  
91          // Check if measurement types are the same
92          if (measType1 != measType2) {
93              // If the measurement types are differents, an exception is thrown
94              throw new OrekitException(OrekitMessages.INVALID_MEASUREMENT_TYPES_FOR_COMBINATION_OF_MEASUREMENTS,
95                                        measType1, measType2, getName());
96          }
97  
98          // Combined frequency
99          final double combinedFrequency = getCombinedFrequency(freq1, freq2);
100 
101         // Combined value
102         final double combinedValue;
103         if (obsType1.getMeasurementType() == MeasurementType.CARRIER_PHASE && !Double.isNaN(combinedFrequency)) {
104             // Transform from cycle to meters measurements
105             final double obs1Meters = od1.getValue() * Constants.SPEED_OF_LIGHT / (freq1.getMHzFrequency() * MHZ_TO_HZ);
106             final double obs2Meters = od2.getValue() * Constants.SPEED_OF_LIGHT / (freq2.getMHzFrequency() * MHZ_TO_HZ);
107 
108             // Calculate the combined value and convert it in cycles using the combined frequency
109             combinedValue = getCombinedValue(obs1Meters, freq1, obs2Meters, freq2) * (combinedFrequency * MHZ_TO_HZ) / Constants.SPEED_OF_LIGHT;
110         } else {
111             combinedValue = getCombinedValue(od1.getValue(), freq1, od2.getValue(), freq2);
112         }
113 
114         // Combined observation data
115         return new CombinedObservationData(type, measType1, combinedValue, combinedFrequency, Arrays.asList(od1, od2));
116 
117     }
118 
119     /** {@inheritDoc} */
120     @Override
121     public CombinedObservationDataSet combine(final ObservationDataSet observations) {
122 
123         // Initialize list of measurements
124         final List<ObservationData> pseudoRanges = new ArrayList<>();
125         final List<ObservationData> phases       = new ArrayList<>();
126 
127         // Loop on observation data to fill lists
128         for (final ObservationData od : observations.getObservationData()) {
129             if (!Double.isNaN(od.getValue())) {
130                 if (od.getObservationType().getMeasurementType() == MeasurementType.PSEUDO_RANGE) {
131                     pseudoRanges.add(od);
132                 } else if (od.getObservationType().getMeasurementType() == MeasurementType.CARRIER_PHASE) {
133                     phases.add(od);
134                 }
135             }
136         }
137 
138         // Initialize list of combined observation data
139         final List<CombinedObservationData> combined = new ArrayList<>();
140         // Combine pseudo-ranges
141         for (int i = 0; i < pseudoRanges.size() - 1; i++) {
142             for (int j = 1; j < pseudoRanges.size(); j++) {
143                 final boolean combine = isCombinationPossible(pseudoRanges.get(i), pseudoRanges.get(j));
144                 if (combine) {
145                     combined.add(combine(pseudoRanges.get(i), pseudoRanges.get(j)));
146                 }
147             }
148         }
149         // Combine carrier-phases
150         for (int i = 0; i < phases.size() - 1; i++) {
151             for (int j = 1; j < phases.size(); j++) {
152                 final boolean combine = isCombinationPossible(phases.get(i), phases.get(j));
153                 if (combine) {
154                     combined.add(combine(phases.get(i), phases.get(j)));
155                 }
156             }
157         }
158 
159         return new CombinedObservationDataSet(observations.getHeader(), observations.getSatelliteSystem(),
160                                               observations.getPrnNumber(), observations.getDate(),
161                                               observations.getRcvrClkOffset(), combined);
162     }
163 
164     /**
165      * Get the combined observed value of two measurements.
166      * @param obs1 observed value of the first measurement
167      * @param f1 frequency of the first measurement
168      * @param obs2 observed value of the second measurement
169      * @param f2 frequency of the second measurement
170      * @return combined observed value
171      */
172     protected abstract double getCombinedValue(double obs1, Frequency f1, double obs2, Frequency f2);
173 
174     /**
175      * Get the combined frequency of two measurements.
176      * @param f1 frequency of the first measurement
177      * @param f2 frequency of the second measurement
178      * @return combined frequency in MHz
179      */
180     protected abstract double getCombinedFrequency(Frequency f1, Frequency f2);
181 
182     /**
183      * Verifies if two observation data can be combine.
184      * @param data1 first observation data
185      * @param data2 second observation data
186      * @return true if observation data can be combined
187      */
188     private boolean isCombinationPossible(final ObservationData data1, final ObservationData data2) {
189         // Observation types
190         final ObservationType obsType1 = data1.getObservationType();
191         final ObservationType obsType2 = data2.getObservationType();
192         // Dual-frequency combination is possible only if observation code is the same and data frequencies are different
193         return obsType1.getFrequency(system) != obsType2.getFrequency(system) &&
194                         obsType1.getSignalCode() == obsType2.getSignalCode();
195     }
196 
197 }