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