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.Arrays;
20
21 import org.hipparchus.analysis.differentiation.Gradient;
22 import org.orekit.estimation.measurements.EstimatedMeasurement;
23 import org.orekit.estimation.measurements.EstimatedMeasurementBase;
24 import org.orekit.estimation.measurements.ObservableSatellite;
25 import org.orekit.estimation.measurements.QuadraticClockModel;
26 import org.orekit.propagation.SpacecraftState;
27 import org.orekit.time.AbsoluteDate;
28 import org.orekit.utils.Constants;
29 import org.orekit.utils.PVCoordinatesProvider;
30 import org.orekit.utils.ParameterDriver;
31 import org.orekit.utils.TimeSpanMap.Span;
32 import org.orekit.utils.TimeStampedPVCoordinates;
33
34 /** One-way GNSS phase measurement.
35 * <p>
36 * This class can be used in precise orbit determination applications
37 * for modeling a phase measurement between a GNSS satellite (emitter)
38 * and a LEO satellite (receiver).
39 * <p>
40 * The one-way GNSS phase measurement assumes knowledge of the orbit and
41 * the clock offset of the emitting GNSS satellite. For instance, it is
42 * possible to use a SP3 file or a GNSS navigation message to recover
43 * the satellite's orbit and clock.
44 * <p>
45 * This class is very similar to {@link InterSatellitesPhase} measurement
46 * class. However, using the one-way GNSS phase measurement, the orbit and clock
47 * of the emitting GNSS satellite are <b>NOT</b> estimated simultaneously with
48 * LEO satellite coordinates.
49 *
50 * @author Bryan Cazabonne
51 * @since 10.3
52 */
53 public class OneWayGNSSPhase extends AbstractOneWayGNSSMeasurement<OneWayGNSSPhase> {
54
55 /** Type of the measurement. */
56 public static final String MEASUREMENT_TYPE = "OneWayGNSSPhase";
57
58 /** Driver for ambiguity. */
59 private final AmbiguityDriver ambiguityDriver;
60
61 /** Wavelength of the phase observed value [m]. */
62 private final double wavelength;
63
64 /** Simple constructor.
65 * @param remote provider for GNSS satellite which simply emits the signal
66 * @param remoteName name of the remote
67 * @param remoteClock clock offset of the GNSS satellite
68 * @param date date of the measurement
69 * @param phase observed value, in cycles
70 * @param wavelength phase observed value wavelength, in meters
71 * @param sigma theoretical standard deviation
72 * @param baseWeight base weight
73 * @param local satellite which receives the signal and perform the measurement
74 * @param cache from which ambiguity drive should come
75 * @since 12.1
76 */
77 public OneWayGNSSPhase(final PVCoordinatesProvider remote,
78 final String remoteName,
79 final QuadraticClockModel remoteClock,
80 final AbsoluteDate date,
81 final double phase, final double wavelength, final double sigma,
82 final double baseWeight, final ObservableSatellite local,
83 final AmbiguityCache cache) {
84 // Call super constructor
85 super(remote, remoteClock, date, phase, sigma, baseWeight, local);
86
87 // Initialize phase ambiguity driver
88 ambiguityDriver = cache.getAmbiguity(remoteName, local.getName(), wavelength);
89
90 // The local satellite clock offset affects the measurement
91 addParameterDriver(ambiguityDriver);
92
93 // Initialise fields
94 this.wavelength = wavelength;
95 }
96
97 /** Get the wavelength.
98 * @return wavelength (m)
99 */
100 public double getWavelength() {
101 return wavelength;
102 }
103
104 /** Get the driver for phase ambiguity.
105 * @return the driver for phase ambiguity
106 */
107 public AmbiguityDriver getAmbiguityDriver() {
108 return ambiguityDriver;
109 }
110
111 /** {@inheritDoc} */
112 @Override
113 protected EstimatedMeasurementBase<OneWayGNSSPhase> theoreticalEvaluationWithoutDerivatives(final int iteration,
114 final int evaluation,
115 final SpacecraftState[] states) {
116
117 final OnBoardCommonParametersWithoutDerivatives common = computeCommonParametersWithout(states, false);
118
119 // prepare the evaluation
120 final EstimatedMeasurementBase<OneWayGNSSPhase> estimatedPhase =
121 new EstimatedMeasurementBase<>(this, iteration, evaluation,
122 new SpacecraftState[] {
123 common.getState()
124 }, new TimeStampedPVCoordinates[] {
125 common.getRemotePV(),
126 common.getTransitPV()
127 });
128
129 // Phase value
130 final double cOverLambda = Constants.SPEED_OF_LIGHT / wavelength;
131 final double ambiguity = ambiguityDriver.getValue(common.getState().getDate());
132 final double phase = (common.getTauD() + common.getLocalOffset() - common.getRemoteOffset()) * cOverLambda +
133 ambiguity;
134
135 // Set value of the estimated measurement
136 estimatedPhase.setEstimatedValue(phase);
137
138 // Return the estimated measurement
139 return estimatedPhase;
140
141 }
142
143 /** {@inheritDoc} */
144 @Override
145 protected EstimatedMeasurement<OneWayGNSSPhase> theoreticalEvaluation(final int iteration,
146 final int evaluation,
147 final SpacecraftState[] states) {
148
149 final OnBoardCommonParametersWithDerivatives common = computeCommonParametersWith(states, false);
150
151 // prepare the evaluation
152 final EstimatedMeasurement<OneWayGNSSPhase> estimatedPhase =
153 new EstimatedMeasurement<>(this, iteration, evaluation,
154 new SpacecraftState[] {
155 common.getState()
156 }, new TimeStampedPVCoordinates[] {
157 common.getRemotePV().toTimeStampedPVCoordinates(),
158 common.getTransitPV().toTimeStampedPVCoordinates()
159 });
160
161 // Phase value
162 final double cOverLambda = Constants.SPEED_OF_LIGHT / wavelength;
163 final Gradient ambiguity = ambiguityDriver.getValue(common.getTauD().getFreeParameters(), common.getIndices(),
164 common.getState().getDate());
165 final Gradient phase = common.getTauD().add(common.getLocalOffset()).subtract(common.getRemoteOffset()).
166 multiply(cOverLambda).
167 add(ambiguity);
168 final double[] phaseDerivatives = phase.getGradient();
169
170 // Set value and state first order derivatives of the estimated measurement
171 estimatedPhase.setEstimatedValue(phase.getValue());
172 estimatedPhase.setStateDerivatives(0, Arrays.copyOfRange(phaseDerivatives, 0, 6));
173
174 // Set first order derivatives with respect to parameters
175 for (final ParameterDriver phaseMeasurementDriver : getParametersDrivers()) {
176 for (Span<String> span = phaseMeasurementDriver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {
177
178 final Integer index = common.getIndices().get(span.getData());
179 if (index != null) {
180 estimatedPhase.setParameterDerivatives(phaseMeasurementDriver, span.getStart(), phaseDerivatives[index]);
181 }
182 }
183 }
184
185 // Return the estimated measurement
186 return estimatedPhase;
187
188 }
189
190 }