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 }