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.Arrays;
20  import java.util.Collections;
21  import java.util.HashMap;
22  import java.util.Map;
23  
24  import org.hipparchus.analysis.differentiation.Gradient;
25  import org.orekit.estimation.measurements.AbstractMeasurement;
26  import org.orekit.estimation.measurements.EstimatedMeasurement;
27  import org.orekit.estimation.measurements.ObservableSatellite;
28  import org.orekit.propagation.SpacecraftState;
29  import org.orekit.time.AbsoluteDate;
30  import org.orekit.time.FieldAbsoluteDate;
31  import org.orekit.utils.Constants;
32  import org.orekit.utils.PVCoordinatesProvider;
33  import org.orekit.utils.ParameterDriver;
34  import org.orekit.utils.TimeStampedFieldPVCoordinates;
35  import org.orekit.utils.TimeStampedPVCoordinates;
36  
37  /** One-way GNSS phase measurement.
38   * <p>
39   * This class can be used in precise orbit determination applications
40   * for modeling a phase measurement between a GNSS satellite (emitter)
41   * and a LEO satellite (receiver).
42   * <p>
43   * The one-way GNSS phase measurement assumes knowledge of the orbit and
44   * the clock offset of the emitting GNSS satellite. For instance, it is
45   * possible to use a SP3 file or a GNSS navigation message to recover
46   * the satellite's orbit and clock.
47   * <p>
48   * This class is very similar to {@link InterSatellitesPhase} measurement
49   * class. However, using the one-way GNSS phase measurement, the orbit and clock
50   * of the emitting GNSS satellite are <b>NOT</b> estimated simultaneously with
51   * LEO satellite coordinates.
52   *
53   * @author Bryan Cazabonne
54   * @since 10.3
55   */
56  public class OneWayGNSSPhase extends AbstractMeasurement<OneWayGNSSPhase> {
57  
58      /** Name for ambiguity driver. */
59      public static final String AMBIGUITY_NAME = "ambiguity";
60  
61      /** Driver for ambiguity. */
62      private final ParameterDriver ambiguityDriver;
63  
64      /** Emitting satellite. */
65      private final PVCoordinatesProvider remote;
66  
67      /** Clock offset of the emitting satellite. */
68      private final double dtRemote;
69  
70      /** Wavelength of the phase observed value [m]. */
71      private final double wavelength;
72  
73      /** Simple constructor.
74       * @param remote provider for GNSS satellite which simply emits the signal
75       * @param dtRemote clock offset of the GNSS satellite, in seconds
76       * @param date date of the measurement
77       * @param phase observed value, in cycles
78       * @param wavelength phase observed value wavelength, in meters
79       * @param sigma theoretical standard deviation
80       * @param baseWeight base weight
81       * @param local satellite which receives the signal and perform the measurement
82       */
83      public OneWayGNSSPhase(final PVCoordinatesProvider remote,
84                             final double dtRemote,
85                             final AbsoluteDate date,
86                             final double phase, final double wavelength, final double sigma,
87                             final double baseWeight, final ObservableSatellite local) {
88          // Call super constructor
89          super(date, phase, sigma, baseWeight, Collections.singletonList(local));
90  
91          // Initialize phase ambiguity driver
92          ambiguityDriver = new ParameterDriver(AMBIGUITY_NAME, 0.0, 1.0,
93                                                Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
94  
95          // The local satellite clock offset affects the measurement
96          addParameterDriver(ambiguityDriver);
97          addParameterDriver(local.getClockOffsetDriver());
98  
99          // Initialise fields
100         this.dtRemote   = dtRemote;
101         this.remote     = remote;
102         this.wavelength = wavelength;
103     }
104 
105     /** Get the wavelength.
106      * @return wavelength (m)
107      */
108     public double getWavelength() {
109         return wavelength;
110     }
111 
112     /** Get the driver for phase ambiguity.
113      * @return the driver for phase ambiguity
114      */
115     public ParameterDriver getAmbiguityDriver() {
116         return ambiguityDriver;
117     }
118 
119     /** {@inheritDoc} */
120     @Override
121     protected EstimatedMeasurement<OneWayGNSSPhase> theoreticalEvaluation(final int iteration,
122                                                                           final int evaluation,
123                                                                           final SpacecraftState[] states) {
124 
125         // Phase derivatives are computed with respect to spacecrafts states in inertial frame
126         // Parameters:
127         //  - 0..2  - Position of the receiver satellite in inertial frame
128         //  - 3..5  - Velocity of the receiver satellite in inertial frame
129         //  - 6..n  - Measurement parameters: ambiguity and clock offset
130         int nbEstimatedParamsPhase = 6;
131         final Map<String, Integer> parameterIndicesPhase = new HashMap<>();
132         for (ParameterDriver phaseMeasurementDriver : getParametersDrivers()) {
133             if (phaseMeasurementDriver.isSelected()) {
134                 parameterIndicesPhase.put(phaseMeasurementDriver.getName(), nbEstimatedParamsPhase++);
135             }
136         }
137 
138         // Coordinates of both satellites
139         final SpacecraftState localState  = states[0];
140         final TimeStampedFieldPVCoordinates<Gradient> pvaLocal  = getCoordinates(localState, 0, nbEstimatedParamsPhase);
141         final TimeStampedPVCoordinates                pvaRemote = remote.getPVCoordinates(getDate(), localState.getFrame());
142 
143         // Downlink delay
144         final Gradient dtLocal = getSatellites().get(0).getClockOffsetDriver().getValue(nbEstimatedParamsPhase, parameterIndicesPhase);
145         final FieldAbsoluteDate<Gradient> arrivalDate = new FieldAbsoluteDate<>(getDate(), dtLocal.negate());
146 
147         final TimeStampedFieldPVCoordinates<Gradient> s1Downlink =
148                         pvaLocal.shiftedBy(arrivalDate.durationFrom(pvaLocal.getDate()));
149         final Gradient tauD = signalTimeOfFlight(new TimeStampedFieldPVCoordinates<>(pvaRemote.getDate(), dtLocal.getField().getOne(), pvaRemote),
150                                                  s1Downlink.getPosition(), arrivalDate);
151 
152         // Transit state
153         final double   delta      = getDate().durationFrom(pvaRemote.getDate());
154         final Gradient deltaMTauD = tauD.negate().add(delta);
155 
156         // prepare the evaluation
157         final EstimatedMeasurement<OneWayGNSSPhase> estimatedPhase =
158                         new EstimatedMeasurement<>(this, iteration, evaluation,
159                                                    new SpacecraftState[] {
160                                                        localState.shiftedBy(deltaMTauD.getValue())
161                                                    }, new TimeStampedPVCoordinates[] {
162                                                        pvaRemote.shiftedBy(delta - tauD.getValue()),
163                                                        localState.shiftedBy(delta).getPVCoordinates()
164                                                    });
165 
166         // Phase value
167         final double   cOverLambda      = Constants.SPEED_OF_LIGHT / wavelength;
168         final Gradient ambiguity        = ambiguityDriver.getValue(nbEstimatedParamsPhase, parameterIndicesPhase);
169         final Gradient phase            = tauD.add(dtLocal).subtract(dtRemote).multiply(cOverLambda).add(ambiguity);
170         final double[] phaseDerivatives = phase.getGradient();
171 
172         // Set value and state derivatives of the estimated measurement
173         estimatedPhase.setEstimatedValue(phase.getValue());
174         estimatedPhase.setStateDerivatives(0, Arrays.copyOfRange(phaseDerivatives, 0,  6));
175 
176         // Set partial derivatives with respect to parameters
177         for (final ParameterDriver phaseMeasurementDriver : getParametersDrivers()) {
178             final Integer index = parameterIndicesPhase.get(phaseMeasurementDriver.getName());
179             if (index != null) {
180                 estimatedPhase.setParameterDerivatives(phaseMeasurementDriver, phaseDerivatives[index]);
181             }
182         }
183 
184         // Return the estimated measurement
185         return estimatedPhase;
186 
187     }
188 
189 }