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.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.GnssSignal;
28  import org.orekit.gnss.MeasurementType;
29  import org.orekit.gnss.ObservationType;
30  import org.orekit.gnss.SatelliteSystem;
31  
32  /** Base class for single frequency combination of measurements.
33   * @author Bryan Cazabonne
34   * @since 10.1
35   */
36  public abstract class AbstractSingleFrequencyCombination implements MeasurementCombination {
37  
38      /** Type of combination of measurements. */
39      private final CombinationType type;
40  
41      /** Satellite system used for the combination. */
42      private final SatelliteSystem system;
43  
44      /**
45       * Constructor.
46       * @param type combination of measurements type
47       * @param system satellite system
48       */
49      protected AbstractSingleFrequencyCombination(final CombinationType type, final SatelliteSystem system) {
50          this.type   = type;
51          this.system = system;
52      }
53  
54      /** {@inheritDoc} */
55      @Override
56      public String getName() {
57          return type.getName();
58      }
59  
60      /** {@inheritDoc} */
61      @Override
62      public CombinedObservationDataSet combine(final ObservationDataSet observations) {
63  
64          // Initialize list of measurements
65          final List<ObservationData> pseudoRanges = new ArrayList<>();
66          final List<ObservationData> phases       = new ArrayList<>();
67  
68          // Loop on observation data to fill lists
69          for (final ObservationData od : observations.getObservationData()) {
70              if (!Double.isNaN(od.getValue())) {
71                  if (od.getObservationType().getMeasurementType() == MeasurementType.PSEUDO_RANGE) {
72                      pseudoRanges.add(od);
73                  } else if (od.getObservationType().getMeasurementType() == MeasurementType.CARRIER_PHASE) {
74                      phases.add(od);
75                  }
76              }
77          }
78  
79          // Initialize list of combined observation data
80          final List<CombinedObservationData> combined = new ArrayList<>();
81  
82          for (final ObservationData phase : phases) {
83              for (final ObservationData pseudoRange : pseudoRanges) {
84                  // Single-frequency combination is possible only if data frequencies are the same
85                  if (phase.getObservationType().getSignal(system) == pseudoRange.getObservationType().getSignal(system) &&
86                      phase.getObservationType().getSignalCode()      == pseudoRange.getObservationType().getSignalCode()) {
87                      combined.add(combine(phase, pseudoRange));
88                  }
89              }
90          }
91  
92          return new CombinedObservationDataSet(observations.getSatellite().getSystem(),
93                                                observations.getSatellite().getPRN(),
94                                                observations.getDate(),
95                                                observations.getRcvrClkOffset(), combined);
96      }
97  
98      /**
99       * Combines observation data using a single frequency combination of measurements.
100      * @param phase phase measurement
101      * @param pseudoRange pseudoRange measurement
102      * @return a combined observation data
103      */
104     public CombinedObservationData combine(final ObservationData phase, final ObservationData pseudoRange) {
105 
106         // Observation types
107         final ObservationType obsType1 = phase.getObservationType();
108         final ObservationType obsType2 = pseudoRange.getObservationType();
109 
110         // Frequencies
111         final GnssSignal freq1 = obsType1.getSignal(system);
112         final GnssSignal freq2 = obsType2.getSignal(system);
113         // Check if the combination of measurements if performed for two different frequencies
114         if (freq1 != freq2) {
115             throw new OrekitException(OrekitMessages.INCOMPATIBLE_FREQUENCIES_FOR_COMBINATION_OF_MEASUREMENTS,
116                                       freq1, freq2, getName());
117         }
118 
119         // Measurements types
120         final MeasurementType measType1 = obsType1.getMeasurementType();
121         final MeasurementType measType2 = obsType2.getMeasurementType();
122 
123         // Check if measurement types are the same
124         if (measType1 == measType2) {
125             // If the measurement types are the same, an exception is thrown
126             throw new OrekitException(OrekitMessages.INVALID_MEASUREMENT_TYPES_FOR_COMBINATION_OF_MEASUREMENTS,
127                                       measType1, measType2, getName());
128         }
129 
130         // Frequency
131         final double f = freq1.getFrequency();
132 
133         // Combined value
134         final double combinedValue = getCombinedValue(phase.getValue(), pseudoRange.getValue());
135 
136         // Combined observation data
137         return new CombinedObservationData(combinedValue, f,
138                                            CombinationType.PHASE_MINUS_CODE, MeasurementType.COMBINED_RANGE_PHASE,
139                                            Arrays.asList(phase, pseudoRange));
140     }
141 
142     /**
143      * Get the combined observed value of two measurements.
144      * @param phase observed value of the phase measurement
145      * @param pseudoRange observed value of the range measurement
146      * @return combined observed value
147      */
148     protected abstract double getCombinedValue(double phase, double pseudoRange);
149 
150 }