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.RinexObservationHeader;
33  import org.orekit.gnss.SatelliteSystem;
34  
35  /** Base class for single frequency combination of measurements.
36   * @author Bryan Cazabonne
37   * @since 10.1
38   */
39  public abstract class AbstractSingleFrequencyCombination implements MeasurementCombination {
40  
41      /** Type of combination of measurements. */
42      private final CombinationType type;
43  
44      /** Satellite system used for the combination. */
45      private final SatelliteSystem system;
46  
47      /**
48       * Constructor.
49       * @param type combination of measurements type
50       * @param system satellite system
51       */
52      protected AbstractSingleFrequencyCombination(final CombinationType type, final SatelliteSystem system) {
53          this.type   = type;
54          this.system = system;
55      }
56  
57      /** {@inheritDoc} */
58      @Override
59      public String getName() {
60          return type.getName();
61      }
62  
63      /** {@inheritDoc} */
64      @Override
65      public CombinedObservationDataSet combine(final ObservationDataSet observations) {
66  
67          // Rinex file header
68          final RinexObservationHeader header = observations.getHeader();
69          // Rinex version to integer
70          final int version = (int) header.getRinexVersion();
71  
72          // Initialize list of measurements
73          final List<ObservationData> pseudoRanges = new ArrayList<>();
74          final List<ObservationData> phases       = new ArrayList<>();
75  
76          // Loop on observation data to fill lists
77          for (final ObservationData od : observations.getObservationData()) {
78              if (!Double.isNaN(od.getValue())) {
79                  if (od.getObservationType().getMeasurementType() == MeasurementType.PSEUDO_RANGE) {
80                      pseudoRanges.add(od);
81                  } else if (od.getObservationType().getMeasurementType() == MeasurementType.CARRIER_PHASE) {
82                      phases.add(od);
83                  }
84              }
85          }
86  
87          // Initialize list of combined observation data
88          final List<CombinedObservationData> combined = new ArrayList<>();
89  
90          for (int i = 0; i < phases.size(); i++) {
91              for (int j = 0; j < pseudoRanges.size(); j++) {
92                  final boolean combine = isCombinationPossible(version, phases.get(i), pseudoRanges.get(j));
93                  if (combine) {
94                      combined.add(combine(phases.get(i), pseudoRanges.get(j)));
95                  }
96              }
97          }
98  
99          return new CombinedObservationDataSet(observations.getHeader(), observations.getSatelliteSystem(),
100                                               observations.getPrnNumber(), observations.getDate(),
101                                               observations.getRcvrClkOffset(), combined);
102     }
103 
104     /**
105      * Combines observation data using a single frequency combination of measurements.
106      * @param phase phase measurement
107      * @param pseudoRange pseudoRange measurement
108      * @return a combined observation data
109      */
110     public CombinedObservationData combine(final ObservationData phase, final ObservationData pseudoRange) {
111 
112         // Observation types
113         final ObservationType obsType1 = phase.getObservationType();
114         final ObservationType obsType2 = pseudoRange.getObservationType();
115 
116         // Frequencies
117         final Frequency freq1 = obsType1.getFrequency(system);
118         final Frequency freq2 = obsType2.getFrequency(system);
119         // Check if the combination of measurements if performed for two different frequencies
120         if (freq1 != freq2) {
121             throw new OrekitException(OrekitMessages.INCOMPATIBLE_FREQUENCIES_FOR_COMBINATION_OF_MEASUREMENTS,
122                                       freq1, freq2, getName());
123         }
124 
125         // Measurements types
126         final MeasurementType measType1 = obsType1.getMeasurementType();
127         final MeasurementType measType2 = obsType2.getMeasurementType();
128 
129         // Check if measurement types are the same
130         if (measType1 == measType2) {
131             // If the measurement types are the same, an exception is thrown
132             throw new OrekitException(OrekitMessages.INVALID_MEASUREMENT_TYPES_FOR_COMBINATION_OF_MEASUREMENTS,
133                                       measType1, measType2, getName());
134         }
135 
136         // Frequency
137         final double f = freq1.getMHzFrequency();
138 
139         // Combined value
140         final double combinedValue = getCombinedValue(phase.getValue(), pseudoRange.getValue());
141 
142         // Combined observation data
143         return new CombinedObservationData(CombinationType.PHASE_MINUS_CODE, MeasurementType.COMBINED_RANGE_PHASE,
144                                            combinedValue, f, Arrays.asList(phase, pseudoRange));
145     }
146 
147     /**
148      * Get the combined observed value of two measurements.
149      * @param phase observed value of the phase measurement
150      * @param pseudoRange observed value of the range measurement
151      * @return combined observed value
152      */
153     protected abstract double getCombinedValue(double phase, double pseudoRange);
154 
155     /**
156      * Verifies if two observation data can be combine.
157      * @param version Rinex file version (integer part)
158      * @param phase phase measurement
159      * @param pseudoRange pseudoRange measurement
160      * @return true if observation data can be combined
161      */
162     private boolean isCombinationPossible(final int version, final ObservationData phase, final ObservationData pseudoRange) {
163         // Observation types
164         final ObservationType obsType1 = phase.getObservationType();
165         final ObservationType obsType2 = pseudoRange.getObservationType();
166         // Single-frequency combination is possible only if data frequencies are the same
167         if (obsType1.getFrequency(system) == obsType2.getFrequency(system)) {
168             // Switch on Rinex version
169             switch (version) {
170                 case 2 : return true;
171                 case 3 : return obsType1.getSignalCode() == obsType2.getSignalCode();
172                 default: return false;
173             }
174         } else {
175             // False because observation data have different frequency
176             return false;
177         }
178     }
179 
180 }