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