1   /* Copyright 2022-2025 Luc Maisonobe
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.Collections;
20  
21  import org.hipparchus.analysis.differentiation.Gradient;
22  import org.orekit.estimation.measurements.ObservableSatellite;
23  import org.orekit.estimation.measurements.ObservedMeasurement;
24  import org.orekit.estimation.measurements.QuadraticClockModel;
25  import org.orekit.propagation.SpacecraftState;
26  import org.orekit.time.AbsoluteDate;
27  import org.orekit.utils.FieldPVCoordinatesProvider;
28  import org.orekit.utils.PVCoordinatesProvider;
29  import org.orekit.utils.TimeStampedFieldPVCoordinates;
30  import org.orekit.utils.TimeStampedPVCoordinates;
31  
32  /** Base class for one-way GNSS measurement.
33   * <p>
34   * This class can be used in precise orbit determination applications
35   * for modeling a range measurement between a GNSS satellite (emitter)
36   * and a LEO satellite (receiver).
37   * </p>
38   * <p>
39   * The one-way GNSS range measurement assumes knowledge of the orbit and
40   * the clock offset of the emitting GNSS satellite. For instance, it is
41   * possible to use a SP3 file or a GNSS navigation message to recover
42   * the satellite's orbit and clock.
43   * </p>
44   * <p>
45   * This class is very similar to {@link AbstractInterSatellitesMeasurement} measurement
46   * class. However, using the one-way GNSS range measurement, the orbit and clock
47   * of the emitting GNSS satellite are <b>NOT</b> estimated simultaneously with
48   * LEO satellite coordinates.
49   * </p>
50   *
51   * @param <T> type of the measurement
52   * @author Luc Maisonobe
53   * @since 12.1
54   */
55  public abstract class AbstractOneWayGNSSMeasurement<T extends ObservedMeasurement<T>>
56      extends AbstractOnBoardMeasurement<T> {
57  
58      /** Emitting satellite. */
59      private final PVCoordinatesProvider remotePV;
60  
61      /** Clock offset of the emitting satellite. */
62      private final QuadraticClockModel remoteClock;
63  
64      /** Simple constructor.
65       * @param remotePV provider for GNSS satellite which simply emits the signal
66       * @param remoteClock clock offset of the GNSS satellite
67       * @param date date of the measurement
68       * @param range observed value
69       * @param sigma theoretical standard deviation
70       * @param baseWeight base weight
71       * @param local satellite which receives the signal and perform the measurement
72       */
73      public AbstractOneWayGNSSMeasurement(final PVCoordinatesProvider remotePV,
74                                           final QuadraticClockModel remoteClock,
75                                           final AbsoluteDate date,
76                                           final double range, final double sigma,
77                                           final double baseWeight, final ObservableSatellite local) {
78          // Call super constructor
79          super(date, range, sigma, baseWeight, Collections.singletonList(local));
80          // The local satellite clock offset affects the measurement
81          addParameterDriver(local.getClockOffsetDriver());
82          addParameterDriver(local.getClockDriftDriver());
83          addParameterDriver(local.getClockAccelerationDriver());
84          // Initialise fields
85          this.remotePV    = remotePV;
86          this.remoteClock = remoteClock;
87      }
88  
89      /** {@inheritDoc} */
90      @Override
91      protected PVCoordinatesProvider getRemotePV(final SpacecraftState[] states) {
92          return remotePV;
93      }
94  
95      /** {@inheritDoc} */
96      @Override
97      protected QuadraticClockModel getRemoteClock() {
98          return remoteClock;
99      }
100 
101     /** {@inheritDoc} */
102     @Override
103     protected FieldPVCoordinatesProvider<Gradient> getRemotePV(final SpacecraftState[] states,
104                                                                final int freeParameters) {
105         // convert the PVCoordinatesProvider to a FieldPVCoordinatesProvider<Gradient>
106         return (date, frame) -> {
107 
108             // apply the raw (no derivatives) remote provider
109             final AbsoluteDate             dateBase = date.toAbsoluteDate();
110             final TimeStampedPVCoordinates pvBase   = remotePV.getPVCoordinates(dateBase, frame);
111             final TimeStampedFieldPVCoordinates<Gradient> pvWithoutDerivatives =
112                 new TimeStampedFieldPVCoordinates<>(date.getField(), pvBase);
113 
114             // add derivatives, using a trick: we shift the date by 0, with derivatives
115             final Gradient zeroWithDerivatives = date.durationFrom(dateBase);
116             return pvWithoutDerivatives.shiftedBy(zeroWithDerivatives);
117 
118         };
119     }
120 
121 }