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 addParameterDriver(local.getClockOffsetDriver());
93
94 // Initialise fields
95 this.wavelength = wavelength;
96 }
97
98 /** Get the wavelength.
99 * @return wavelength (m)
100 */
101 public double getWavelength() {
102 return wavelength;
103 }
104
105 /** Get the driver for phase ambiguity.
106 * @return the driver for phase ambiguity
107 */
108 public AmbiguityDriver getAmbiguityDriver() {
109 return ambiguityDriver;
110 }
111
112 /** {@inheritDoc} */
113 @Override
114 protected EstimatedMeasurementBase<OneWayGNSSPhase> theoreticalEvaluationWithoutDerivatives(final int iteration,
115 final int evaluation,
116 final SpacecraftState[] states) {
117
118 final OnBoardCommonParametersWithoutDerivatives common = computeCommonParametersWithout(states, false);
119
120 // prepare the evaluation
121 final EstimatedMeasurementBase<OneWayGNSSPhase> estimatedPhase =
122 new EstimatedMeasurementBase<>(this, iteration, evaluation,
123 new SpacecraftState[] {
124 common.getState()
125 }, new TimeStampedPVCoordinates[] {
126 common.getRemotePV(),
127 common.getTransitPV()
128 });
129
130 // Phase value
131 final double cOverLambda = Constants.SPEED_OF_LIGHT / wavelength;
132 final double ambiguity = ambiguityDriver.getValue(common.getState().getDate());
133 final double phase = (common.getTauD() + common.getLocalOffset() - common.getRemoteOffset()) * cOverLambda +
134 ambiguity;
135
136 // Set value of the estimated measurement
137 estimatedPhase.setEstimatedValue(phase);
138
139 // Return the estimated measurement
140 return estimatedPhase;
141
142 }
143
144 /** {@inheritDoc} */
145 @Override
146 protected EstimatedMeasurement<OneWayGNSSPhase> theoreticalEvaluation(final int iteration,
147 final int evaluation,
148 final SpacecraftState[] states) {
149
150 final OnBoardCommonParametersWithDerivatives common = computeCommonParametersWith(states, false);
151
152 // prepare the evaluation
153 final EstimatedMeasurement<OneWayGNSSPhase> estimatedPhase =
154 new EstimatedMeasurement<>(this, iteration, evaluation,
155 new SpacecraftState[] {
156 common.getState()
157 }, new TimeStampedPVCoordinates[] {
158 common.getRemotePV().toTimeStampedPVCoordinates(),
159 common.getTransitPV().toTimeStampedPVCoordinates()
160 });
161
162 // Phase value
163 final double cOverLambda = Constants.SPEED_OF_LIGHT / wavelength;
164 final Gradient ambiguity = ambiguityDriver.getValue(common.getTauD().getFreeParameters(), common.getIndices(),
165 common.getState().getDate());
166 final Gradient phase = common.getTauD().add(common.getLocalOffset()).subtract(common.getRemoteOffset()).
167 multiply(cOverLambda).
168 add(ambiguity);
169 final double[] phaseDerivatives = phase.getGradient();
170
171 // Set value and state first order derivatives of the estimated measurement
172 estimatedPhase.setEstimatedValue(phase.getValue());
173 estimatedPhase.setStateDerivatives(0, Arrays.copyOfRange(phaseDerivatives, 0, 6));
174
175 // Set first order derivatives with respect to parameters
176 for (final ParameterDriver phaseMeasurementDriver : getParametersDrivers()) {
177 for (Span<String> span = phaseMeasurementDriver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {
178
179 final Integer index = common.getIndices().get(span.getData());
180 if (index != null) {
181 estimatedPhase.setParameterDerivatives(phaseMeasurementDriver, span.getStart(), phaseDerivatives[index]);
182 }
183 }
184 }
185
186 // Return the estimated measurement
187 return estimatedPhase;
188
189 }
190
191 }