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.InterSatellitesRange;
28  import org.orekit.estimation.measurements.ObservableSatellite;
29  import org.orekit.propagation.SpacecraftState;
30  import org.orekit.time.AbsoluteDate;
31  import org.orekit.time.FieldAbsoluteDate;
32  import org.orekit.utils.Constants;
33  import org.orekit.utils.PVCoordinatesProvider;
34  import org.orekit.utils.ParameterDriver;
35  import org.orekit.utils.TimeStampedFieldPVCoordinates;
36  import org.orekit.utils.TimeStampedPVCoordinates;
37  
38  /** One-way GNSS range measurement.
39   * <p>
40   * This class can be used in precise orbit determination applications
41   * for modeling a range measurement between a GNSS satellite (emitter)
42   * and a LEO satellite (receiver).
43   * <p>
44   * The one-way GNSS range measurement assumes knowledge of the orbit and
45   * the clock offset of the emitting GNSS satellite. For instance, it is
46   * possible to use a SP3 file or a GNSS navigation message to recover
47   * the satellite's orbit and clock.
48   * <p>
49   * This class is very similar to {@link InterSatellitesRange} measurement
50   * class. However, using the one-way GNSS range measurement, the orbit and clock
51   * of the emitting GNSS satellite are <b>NOT</b> estimated simultaneously with
52   * LEO satellite coordinates.
53   *
54   * @author Bryan Cazabonne
55   * @since 10.3
56   */
57  public class OneWayGNSSRange extends AbstractMeasurement<OneWayGNSSRange> {
58  
59      /** Emitting satellite. */
60      private final PVCoordinatesProvider remote;
61  
62      /** Clock offset of the emitting satellite. */
63      private final double dtRemote;
64  
65      /** Simple constructor.
66       * @param remote provider for GNSS satellite which simply emits the signal
67       * @param dtRemote clock offset of the GNSS satellite, in seconds
68       * @param date date of the measurement
69       * @param range observed value
70       * @param sigma theoretical standard deviation
71       * @param baseWeight base weight
72       * @param local satellite which receives the signal and perform the measurement
73       */
74      public OneWayGNSSRange(final PVCoordinatesProvider remote,
75                             final double dtRemote,
76                             final AbsoluteDate date,
77                             final double range, final double sigma,
78                             final double baseWeight, final ObservableSatellite local) {
79          // Call super constructor
80          super(date, range, sigma, baseWeight, Collections.singletonList(local));
81          // The local satellite clock offset affects the measurement
82          addParameterDriver(local.getClockOffsetDriver());
83          // Initialise fields
84          this.dtRemote = dtRemote;
85          this.remote   = remote;
86      }
87  
88      /** {@inheritDoc} */
89      @Override
90      protected EstimatedMeasurement<OneWayGNSSRange> theoreticalEvaluation(final int iteration,
91                                                                            final int evaluation,
92                                                                            final SpacecraftState[] states) {
93  
94          // Range derivatives are computed with respect to spacecraft state in inertial frame
95          // Parameters:
96          //  - 0..2 - Position of the spacecraft in inertial frame
97          //  - 3..5 - Velocity of the spacecraft in inertial frame
98          //  - 6..n - measurements parameters (clock offset, etc)
99          int nbEstimatedParams = 6;
100         final Map<String, Integer> parameterIndices = new HashMap<>();
101         for (ParameterDriver measurementDriver : getParametersDrivers()) {
102             if (measurementDriver.isSelected()) {
103                 parameterIndices.put(measurementDriver.getName(), nbEstimatedParams++);
104             }
105         }
106 
107         // Coordinates of both satellites in local satellite frame
108         final SpacecraftState localState  = states[0];
109         final TimeStampedFieldPVCoordinates<Gradient> pvaLocal  = getCoordinates(localState, 0, nbEstimatedParams);
110         final TimeStampedPVCoordinates                pvaRemote = remote.getPVCoordinates(getDate(), localState.getFrame());
111 
112         // Downlink delay
113         final Gradient dtLocal = getSatellites().get(0).getClockOffsetDriver().getValue(nbEstimatedParams, parameterIndices);
114         final FieldAbsoluteDate<Gradient> arrivalDate = new FieldAbsoluteDate<>(getDate(), dtLocal.negate());
115 
116         final TimeStampedFieldPVCoordinates<Gradient> s1Downlink = pvaLocal.shiftedBy(arrivalDate.durationFrom(pvaLocal.getDate()));
117         final Gradient tauD = signalTimeOfFlight(new TimeStampedFieldPVCoordinates<>(pvaRemote.getDate(), dtLocal.getField().getOne(), pvaRemote),
118                                                  s1Downlink.getPosition(), arrivalDate);
119 
120         // Transit state
121         final double   delta      = getDate().durationFrom(pvaRemote.getDate());
122         final Gradient deltaMTauD = tauD.negate().add(delta);
123 
124         // Estimated measurement
125         final EstimatedMeasurement<OneWayGNSSRange> estimatedRange =
126                         new EstimatedMeasurement<>(this, iteration, evaluation,
127                                                    new SpacecraftState[] {
128                                                        localState.shiftedBy(deltaMTauD.getValue())
129                                                    }, new TimeStampedPVCoordinates[] {
130                                                        pvaRemote.shiftedBy(delta - tauD.getValue()),
131                                                        localState.shiftedBy(delta).getPVCoordinates()
132                                                    });
133 
134         // Range value
135         final Gradient range            = tauD.add(dtLocal).subtract(dtRemote).multiply(Constants.SPEED_OF_LIGHT);
136         final double[] rangeDerivatives = range.getGradient();
137 
138         // Set value and state derivatives of the estimated measurement
139         estimatedRange.setEstimatedValue(range.getValue());
140         estimatedRange.setStateDerivatives(0, Arrays.copyOfRange(rangeDerivatives, 0,  6));
141 
142         // Set partial derivatives with respect to parameters
143         for (final ParameterDriver measurementDriver : getParametersDrivers()) {
144             final Integer index = parameterIndices.get(measurementDriver.getName());
145             if (index != null) {
146                 estimatedRange.setParameterDerivatives(measurementDriver, rangeDerivatives[index]);
147             }
148         }
149 
150         // Return the estimated measurement
151         return estimatedRange;
152 
153     }
154 
155 }