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;
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.hipparchus.analysis.differentiation.GradientField;
26  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
27  import org.orekit.frames.FieldTransform;
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.ParameterDriver;
33  import org.orekit.utils.TimeStampedFieldPVCoordinates;
34  import org.orekit.utils.TimeStampedPVCoordinates;
35  
36  /** Class modeling one-way or two-way range rate measurement between two vehicles.
37   * One-way range rate (or Doppler) measurements generally apply to specific satellites
38   * (e.g. GNSS, DORIS), where a signal is transmitted from a satellite to a
39   * measuring station.
40   * Two-way range rate measurements are applicable to any system. The signal is
41   * transmitted to the (non-spinning) satellite and returned by a transponder
42   * (or reflected back)to the same measuring station.
43   * The Doppler measurement can be obtained by multiplying the velocity by (fe/c), where
44   * fe is the emission frequency.
45   *
46   * @author Thierry Ceolin
47   * @author Joris Olympio
48   * @since 8.0
49   */
50  public class RangeRate extends AbstractMeasurement<RangeRate> {
51  
52      /** Ground station from which measurement is performed. */
53      private final GroundStation station;
54  
55      /** Flag indicating whether it is a two-way measurement. */
56      private final boolean twoway;
57  
58      /** Simple constructor.
59       * @param station ground station from which measurement is performed
60       * @param date date of the measurement
61       * @param rangeRate observed value, m/s
62       * @param sigma theoretical standard deviation
63       * @param baseWeight base weight
64       * @param twoway if true, this is a two-way measurement
65       * @param satellite satellite related to this measurement
66       * @since 9.3
67       */
68      public RangeRate(final GroundStation station, final AbsoluteDate date,
69                       final double rangeRate, final double sigma, final double baseWeight,
70                       final boolean twoway, final ObservableSatellite satellite) {
71          super(date, rangeRate, sigma, baseWeight, Collections.singletonList(satellite));
72          addParameterDriver(station.getClockOffsetDriver());
73          addParameterDriver(station.getClockDriftDriver());
74          addParameterDriver(satellite.getClockDriftDriver());
75          addParameterDriver(station.getEastOffsetDriver());
76          addParameterDriver(station.getNorthOffsetDriver());
77          addParameterDriver(station.getZenithOffsetDriver());
78          addParameterDriver(station.getPrimeMeridianOffsetDriver());
79          addParameterDriver(station.getPrimeMeridianDriftDriver());
80          addParameterDriver(station.getPolarOffsetXDriver());
81          addParameterDriver(station.getPolarDriftXDriver());
82          addParameterDriver(station.getPolarOffsetYDriver());
83          addParameterDriver(station.getPolarDriftYDriver());
84          this.station = station;
85          this.twoway  = twoway;
86      }
87  
88      /** Check if the instance represents a two-way measurement.
89       * @return true if the instance represents a two-way measurement
90       */
91      public boolean isTwoWay() {
92          return twoway;
93      }
94  
95      /** Get the ground station from which measurement is performed.
96       * @return ground station from which measurement is performed
97       */
98      public GroundStation getStation() {
99          return station;
100     }
101 
102     /** {@inheritDoc} */
103     @Override
104     protected EstimatedMeasurement<RangeRate> theoreticalEvaluation(final int iteration, final int evaluation,
105                                                                     final SpacecraftState[] states) {
106 
107         final SpacecraftState state = states[0];
108 
109         // Range-rate derivatives are computed with respect to spacecraft state in inertial frame
110         // and station position in station's offset frame
111         // -------
112         //
113         // Parameters:
114         //  - 0..2 - Position of the spacecraft in inertial frame
115         //  - 3..5 - Velocity of the spacecraft in inertial frame
116         //  - 6..n - station parameters (clock offset, clock drift, station offsets, pole, prime meridian...)
117         int nbParams = 6;
118         final Map<String, Integer> indices = new HashMap<>();
119         for (ParameterDriver driver : getParametersDrivers()) {
120             if (driver.isSelected()) {
121                 indices.put(driver.getName(), nbParams++);
122             }
123         }
124         final FieldVector3D<Gradient> zero = FieldVector3D.getZero(GradientField.getField(nbParams));
125 
126         // Coordinates of the spacecraft expressed as a gradient
127         final TimeStampedFieldPVCoordinates<Gradient> pvaDS = getCoordinates(state, 0, nbParams);
128 
129         // transform between station and inertial frame, expressed as a gradient
130         // The components of station's position in offset frame are the 3 last derivative parameters
131         final FieldTransform<Gradient> offsetToInertialDownlink =
132                         station.getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices);
133         final FieldAbsoluteDate<Gradient> downlinkDateDS =
134                         offsetToInertialDownlink.getFieldDate();
135 
136         // Station position in inertial frame at end of the downlink leg
137         final TimeStampedFieldPVCoordinates<Gradient> stationDownlink =
138                         offsetToInertialDownlink.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(downlinkDateDS,
139                                                                                                             zero, zero, zero));
140 
141         // Compute propagation times
142         // (if state has already been set up to pre-compensate propagation delay,
143         //  we will have delta == tauD and transitState will be the same as state)
144 
145         // Downlink delay
146         final Gradient tauD = signalTimeOfFlight(pvaDS, stationDownlink.getPosition(), downlinkDateDS);
147 
148         // Transit state
149         final Gradient        delta        = downlinkDateDS.durationFrom(state.getDate());
150         final Gradient        deltaMTauD   = tauD.negate().add(delta);
151         final SpacecraftState transitState = state.shiftedBy(deltaMTauD.getValue());
152 
153         // Transit state (re)computed with gradients
154         final TimeStampedFieldPVCoordinates<Gradient> transitPV = pvaDS.shiftedBy(deltaMTauD);
155 
156         // one-way (downlink) range-rate
157         final EstimatedMeasurement<RangeRate> evalOneWay1 =
158                         oneWayTheoreticalEvaluation(iteration, evaluation, true,
159                                                     stationDownlink, transitPV, transitState, indices, nbParams);
160         final EstimatedMeasurement<RangeRate> estimated;
161         if (twoway) {
162             // one-way (uplink) light time correction
163             final FieldTransform<Gradient> offsetToInertialApproxUplink =
164                             station.getOffsetToInertial(state.getFrame(),
165                                                         downlinkDateDS.shiftedBy(tauD.multiply(-2)), nbParams, indices);
166             final FieldAbsoluteDate<Gradient> approxUplinkDateDS =
167                             offsetToInertialApproxUplink.getFieldDate();
168 
169             final TimeStampedFieldPVCoordinates<Gradient> stationApproxUplink =
170                             offsetToInertialApproxUplink.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(approxUplinkDateDS,
171                                                                                                                     zero, zero, zero));
172 
173             final Gradient tauU = signalTimeOfFlight(stationApproxUplink, transitPV.getPosition(), transitPV.getDate());
174 
175             final TimeStampedFieldPVCoordinates<Gradient> stationUplink =
176                             stationApproxUplink.shiftedBy(transitPV.getDate().durationFrom(approxUplinkDateDS).subtract(tauU));
177 
178             final EstimatedMeasurement<RangeRate> evalOneWay2 =
179                             oneWayTheoreticalEvaluation(iteration, evaluation, false,
180                                                         stationUplink, transitPV, transitState, indices, nbParams);
181 
182             // combine uplink and downlink values
183             estimated = new EstimatedMeasurement<>(this, iteration, evaluation,
184                                                    evalOneWay1.getStates(),
185                                                    new TimeStampedPVCoordinates[] {
186                                                        evalOneWay2.getParticipants()[0],
187                                                        evalOneWay1.getParticipants()[0],
188                                                        evalOneWay1.getParticipants()[1]
189                                                    });
190             estimated.setEstimatedValue(0.5 * (evalOneWay1.getEstimatedValue()[0] + evalOneWay2.getEstimatedValue()[0]));
191 
192             // combine uplink and downlink partial derivatives with respect to state
193             final double[][] sd1 = evalOneWay1.getStateDerivatives(0);
194             final double[][] sd2 = evalOneWay2.getStateDerivatives(0);
195             final double[][] sd = new double[sd1.length][sd1[0].length];
196             for (int i = 0; i < sd.length; ++i) {
197                 for (int j = 0; j < sd[0].length; ++j) {
198                     sd[i][j] = 0.5 * (sd1[i][j] + sd2[i][j]);
199                 }
200             }
201             estimated.setStateDerivatives(0, sd);
202 
203             // combine uplink and downlink partial derivatives with respect to parameters
204             evalOneWay1.getDerivativesDrivers().forEach(driver -> {
205                 final double[] pd1 = evalOneWay1.getParameterDerivatives(driver);
206                 final double[] pd2 = evalOneWay2.getParameterDerivatives(driver);
207                 final double[] pd = new double[pd1.length];
208                 for (int i = 0; i < pd.length; ++i) {
209                     pd[i] = 0.5 * (pd1[i] + pd2[i]);
210                 }
211                 estimated.setParameterDerivatives(driver, pd);
212             });
213 
214         } else {
215             estimated = evalOneWay1;
216         }
217 
218         return estimated;
219 
220     }
221 
222     /** Evaluate measurement in one-way.
223      * @param iteration iteration number
224      * @param evaluation evaluations counter
225      * @param downlink indicator for downlink leg
226      * @param stationPV station coordinates when signal is at station
227      * @param transitPV spacecraft coordinates at onboard signal transit
228      * @param transitState orbital state at onboard signal transit
229      * @param indices indices of the estimated parameters in derivatives computations
230      * @param nbParams the number of estimated parameters in derivative computations
231      * @return theoretical value
232      * @see #evaluate(SpacecraftStatet)
233      */
234     private EstimatedMeasurement<RangeRate> oneWayTheoreticalEvaluation(final int iteration, final int evaluation, final boolean downlink,
235                                                                         final TimeStampedFieldPVCoordinates<Gradient> stationPV,
236                                                                         final TimeStampedFieldPVCoordinates<Gradient> transitPV,
237                                                                         final SpacecraftState transitState,
238                                                                         final Map<String, Integer> indices,
239                                                                         final int nbParams) {
240 
241         // prepare the evaluation
242         final EstimatedMeasurement<RangeRate> estimated =
243                         new EstimatedMeasurement<RangeRate>(this, iteration, evaluation,
244                                                             new SpacecraftState[] {
245                                                                 transitState
246                                                             }, new TimeStampedPVCoordinates[] {
247                                                                 (downlink ? transitPV : stationPV).toTimeStampedPVCoordinates(),
248                                                                 (downlink ? stationPV : transitPV).toTimeStampedPVCoordinates()
249                                                             });
250 
251         // range rate value
252         final FieldVector3D<Gradient> stationPosition  = stationPV.getPosition();
253         final FieldVector3D<Gradient> relativePosition = stationPosition.subtract(transitPV.getPosition());
254 
255         final FieldVector3D<Gradient> stationVelocity  = stationPV.getVelocity();
256         final FieldVector3D<Gradient> relativeVelocity = stationVelocity.subtract(transitPV.getVelocity());
257 
258         // radial direction
259         final FieldVector3D<Gradient> lineOfSight      = relativePosition.normalize();
260 
261         // line of sight velocity
262         final Gradient lineOfSightVelocity = FieldVector3D.dotProduct(relativeVelocity, lineOfSight);
263 
264         // range rate
265         Gradient rangeRate = lineOfSightVelocity;
266 
267         if (!twoway) {
268             // clock drifts, taken in account only in case of one way
269             final ObservableSatellite satellite    = getSatellites().get(0);
270             final Gradient            dtsDot       = satellite.getClockDriftDriver().getValue(nbParams, indices);
271             final Gradient            dtgDot       = station.getClockDriftDriver().getValue(nbParams, indices);
272 
273             final Gradient clockDriftBiais = dtgDot.subtract(dtsDot).multiply(Constants.SPEED_OF_LIGHT);
274 
275             rangeRate = rangeRate.add(clockDriftBiais);
276         }
277 
278         estimated.setEstimatedValue(rangeRate.getValue());
279 
280         // compute partial derivatives of (rr) with respect to spacecraft state Cartesian coordinates
281         final double[] derivatives = rangeRate.getGradient();
282         estimated.setStateDerivatives(0, Arrays.copyOfRange(derivatives, 0, 6));
283 
284         // set partial derivatives with respect to parameters
285         // (beware element at index 0 is the value, not a derivative)
286         for (final ParameterDriver driver : getParametersDrivers()) {
287             final Integer index = indices.get(driver.getName());
288             if (index != null) {
289                 estimated.setParameterDerivatives(driver, derivatives[index]);
290             }
291         }
292 
293         return estimated;
294 
295     }
296 
297 }