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 }