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.List;
21  
22  import org.hipparchus.util.FastMath;
23  import org.orekit.gnss.CombinedObservationData;
24  import org.orekit.gnss.CombinedObservationDataSet;
25  import org.orekit.gnss.MeasurementType;
26  import org.orekit.gnss.ObservationData;
27  import org.orekit.gnss.ObservationDataSet;
28  import org.orekit.gnss.SatelliteSystem;
29  
30  /**
31   * Melbourne-Wübbena combination.
32   * <p>
33   * This combination allows, thanks to the wide-lane combination, a larger wavelength
34   * than each signal individually. Moreover, the measurement noise is reduced by the
35   * narrow-lane combination of code measurements.
36   * </p>
37   * <pre>
38   *    mMW =  ΦWL- RNL
39   *    mMW =  λWL * NWL+ b + ε
40   * </pre>
41   * With:
42   * <ul>
43   * <li>mMW : Melbourne-Wübbena measurement.</li>
44   * <li>ΦWL : Wide-Lane phase measurement.</li>
45   * <li>RNL : Narrow-Lane code measurement.</li>
46   * <li>λWL : Wide-Lane wavelength.</li>
47   * <li>NWL : Wide-Lane ambiguity (Nf1 - Nf2).</li>
48   * <li>b   : Satellite and receiver instrumental delays.</li>
49   * <li>ε   : Measurement noise.</li>
50   * </ul>
51   * <p>
52   * {@link NarrowLaneCombination Narrow-Lane} and {@link WideLaneCombination Wide-Lane}
53   * combinations shall be performed with the same pair of frequencies.
54   * </p>
55   *
56   * @see "Detector based in code and carrier phase data: The Melbourne-Wübbena combination,
57   *       J. Sanz Subirana, J.M. Juan Zornoza and M. Hernández-Pajares, 2011"
58   *
59   * @author Bryan Cazabonne
60   * @since 10.1
61   */
62  public class MelbourneWubbenaCombination implements MeasurementCombination {
63  
64      /** Threshold for frequency comparison. */
65      private static final double THRESHOLD = 1.0e-10;
66  
67      /** Satellite system used for the combination. */
68      private final SatelliteSystem system;
69  
70      /**
71       * Package private constructor for the factory.
72       * @param system satellite system for which the combination is applied
73       */
74      MelbourneWubbenaCombination(final SatelliteSystem system) {
75          this.system = system;
76      }
77  
78      /** {@inheritDoc} */
79      @Override
80      public CombinedObservationDataSet combine(final ObservationDataSet observations) {
81  
82          // Wide-Lane combination
83          final WideLaneCombination        wideLane   = MeasurementCombinationFactory.getWideLaneCombination(system);
84          final CombinedObservationDataSet combinedWL = wideLane.combine(observations);
85  
86          // Narrow-Lane combination
87          final NarrowLaneCombination      narrowLane = MeasurementCombinationFactory.getNarrowLaneCombination(system);
88          final CombinedObservationDataSet combinedNL = narrowLane.combine(observations);
89  
90          // Initialize list of combined observation data
91          final List<CombinedObservationData> combined = new ArrayList<>();
92  
93          // Loop on Wide-Lane measurements
94          for (CombinedObservationData odWL : combinedWL.getObservationData()) {
95              // Only consider combined phase measurements
96              if (odWL.getMeasurementType() == MeasurementType.CARRIER_PHASE) {
97                  // Loop on Narrow-Lane measurements
98                  for (CombinedObservationData odNL : combinedNL.getObservationData()) {
99                      // Only consider combined range measurements
100                     if (odNL.getMeasurementType() == MeasurementType.PSEUDO_RANGE) {
101                         // Verify if the combinations have used the same frequencies
102                         final boolean isCombinationPossible = isCombinationPossible(odWL, odNL);
103                         if (isCombinationPossible) {
104                             // Combined value and frequency
105                             final double combinedValue     = odWL.getValue() - odNL.getValue();
106                             final double combinedFrequency = odWL.getCombinedMHzFrequency();
107                             // Used observation data to build the Melbourn-Wübbena measurement
108                             final List<ObservationData> usedData = new ArrayList<ObservationData>(4);
109                             usedData.add(0, odWL.getUsedObservationData().get(0));
110                             usedData.add(1, odWL.getUsedObservationData().get(1));
111                             usedData.add(2, odNL.getUsedObservationData().get(0));
112                             usedData.add(3, odNL.getUsedObservationData().get(1));
113                             // Update the combined observation data list
114                             combined.add(new CombinedObservationData(CombinationType.MELBOURNE_WUBBENA,
115                                                                      MeasurementType.COMBINED_RANGE_PHASE,
116                                                                      combinedValue, combinedFrequency, usedData));
117                         }
118                     }
119                 }
120             }
121         }
122 
123         return new CombinedObservationDataSet(observations.getHeader(), observations.getSatelliteSystem(),
124                                               observations.getPrnNumber(), observations.getDate(),
125                                               observations.getRcvrClkOffset(), combined);
126     }
127 
128     /**
129      * Verifies if the Melbourne-Wübbena combination is possible between both combined observation data.
130      * <p>
131      * This method compares the frequencies of the combined measurement to decide
132      * if the combination of measurements is possible.
133      * The combination is possible if :
134      * <pre>
135      *    abs(f1<sub>WL</sub> - f2<sub>WL</sub>) = abs(f1<sub>NL</sub> - f2<sub>NL</sub>)
136      * </pre>
137      * </p>
138      * @param odWL Wide-Lane measurement
139      * @param odNL Narrow-Lane measurement
140      * @return true if the Melbourne-Wübbena combination is possible
141      */
142     private boolean isCombinationPossible(final CombinedObservationData odWL, final CombinedObservationData odNL) {
143         // Frequencies
144         final double[] frequency = new double[4];
145         int j = 0;
146         for (int i = 0; i < odWL.getUsedObservationData().size(); i++) {
147             frequency[j++] = odWL.getUsedObservationData().get(i).getObservationType().getFrequency(system).getMHzFrequency();
148             frequency[j++] = odNL.getUsedObservationData().get(i).getObservationType().getFrequency(system).getMHzFrequency();
149         }
150         // Verify if used frequencies are the same.
151         // Possible numerical error is taken into account by using a threshold of acceptance
152         return (FastMath.abs(frequency[0] - frequency[2]) - FastMath.abs(frequency[1] - frequency[3])) < THRESHOLD;
153     }
154 
155     /** {@inheritDoc} */
156     @Override
157     public String getName() {
158         return CombinationType.MELBOURNE_WUBBENA.getName();
159     }
160 
161 }