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 org.hipparchus.analysis.differentiation.Gradient;
20  import org.hipparchus.analysis.differentiation.GradientField;
21  import org.orekit.estimation.measurements.AbstractMeasurement;
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.estimation.measurements.QuadraticFieldClockModel;
26  import org.orekit.frames.Frame;
27  import org.orekit.propagation.SpacecraftState;
28  import org.orekit.time.AbsoluteDate;
29  import org.orekit.time.ClockOffset;
30  import org.orekit.time.FieldAbsoluteDate;
31  import org.orekit.time.FieldClockOffset;
32  import org.orekit.utils.FieldPVCoordinatesProvider;
33  import org.orekit.utils.PVCoordinatesProvider;
34  import org.orekit.utils.ParameterDriver;
35  import org.orekit.utils.TimeSpanMap.Span;
36  import org.orekit.utils.TimeStampedFieldPVCoordinates;
37  import org.orekit.utils.TimeStampedPVCoordinates;
38  
39  import java.util.HashMap;
40  import java.util.List;
41  import java.util.Map;
42  
43  /** Base class modeling a measurement where receiver is a satellite.
44   * @param <T> type of the measurement
45   * @author Luc Maisonobe
46   * @since 12.1
47   */
48  public abstract class AbstractOnBoardMeasurement<T extends ObservedMeasurement<T>> extends AbstractMeasurement<T> {
49  
50      /** Constructor.
51       * @param date date of the measurement
52       * @param observed observed value
53       * @param sigma theoretical standard deviation
54       * @param baseWeight base weight
55       * @param satellites satellites related to this measurement
56       */
57      public AbstractOnBoardMeasurement(final AbsoluteDate date, final double observed,
58                                        final double sigma, final double baseWeight,
59                                        final List<ObservableSatellite> satellites) {
60          // Call to super constructor
61          super(date, observed, sigma, baseWeight, satellites);
62  
63          // Add parameter drivers
64          satellites.forEach(s -> {
65              addParameterDriver(s.getClockOffsetDriver());
66              addParameterDriver(s.getClockDriftDriver());
67              addParameterDriver(s.getClockAccelerationDriver());
68          });
69  
70      }
71  
72      /** Get emitting satellite clock provider.
73       * @return emitting satellite clock provider
74       */
75      protected abstract QuadraticClockModel getRemoteClock();
76  
77      /** Get emitting satellite position/velocity provider.
78       * @param states states of all spacecraft involved in the measurement
79       * @return emitting satellite position/velocity provider
80       */
81      protected abstract PVCoordinatesProvider getRemotePV(SpacecraftState[] states);
82  
83      /** Get emitting satellite position/velocity provider.
84       * @param states states of all spacecraft involved in the measurement
85       * @param freeParameters total number of free parameters in the gradient
86       * @return emitting satellite position/velocity provider
87       */
88      protected abstract FieldPVCoordinatesProvider<Gradient> getRemotePV(SpacecraftState[] states,
89                                                                          int freeParameters);
90  
91      /** Get emitting satellite clock provider.
92       * @param freeParameters total number of free parameters in the gradient
93       * @param indices indices of the differentiation parameters in derivatives computations,
94       * must be span name and not driver name
95       * @return emitting satellite clock provider
96       */
97      protected QuadraticFieldClockModel<Gradient> getRemoteClock(final int freeParameters,
98                                                                  final Map<String, Integer> indices) {
99          return getRemoteClock().toGradientModel(freeParameters, indices, getDate());
100     }
101 
102     /** Compute common estimation parameters.
103      * @param states states of all spacecraft involved in the measurement
104      * @param clockOffsetAlreadyApplied if true, the specified {@code date} is as read
105      * by the receiver clock (i.e. clock offset <em>not</em> compensated), if false,
106      * the specified {@code date} was already compensated and is a physical absolute date
107      * @return common parameters
108      */
109     protected OnBoardCommonParametersWithoutDerivatives computeCommonParametersWithout(final SpacecraftState[] states,
110                                                                                        final boolean clockOffsetAlreadyApplied) {
111 
112         // local and remote satellites
113         final Frame                    frame            = states[0].getFrame();
114         final TimeStampedPVCoordinates pvaLocal         = states[0].getPVCoordinates(frame);
115         final ClockOffset              localClock       = getSatellites().
116                                                           get(0).
117                                                           getQuadraticClockModel().
118             getOffset(getDate());
119         final double                   localClockOffset = localClock.getOffset();
120         final double                   localClockRate   = localClock.getRate();
121         final PVCoordinatesProvider    remotePV         = getRemotePV(states);
122 
123         // take clock offset into account
124         final AbsoluteDate arrivalDate = clockOffsetAlreadyApplied ? getDate() : getDate().shiftedBy(-localClockOffset);
125 
126         // Downlink delay
127         final double deltaT = arrivalDate.durationFrom(states[0]);
128         final TimeStampedPVCoordinates pvaDownlink = pvaLocal.shiftedBy(deltaT);
129         final double tauD = signalTimeOfFlightAdjustableEmitter(remotePV, arrivalDate, pvaDownlink.getPosition(),
130                                                                 arrivalDate, frame);
131 
132         // Remote satellite at signal emission
133         final AbsoluteDate        emissionDate      = arrivalDate.shiftedBy(-tauD);
134         final ClockOffset         remoteClock       = getRemoteClock().getOffset(emissionDate);
135         final double              remoteClockOffset = remoteClock.getOffset();
136         final double              remoteClockRate   = remoteClock.getRate();
137         return new OnBoardCommonParametersWithoutDerivatives(states[0],
138                                                              localClockOffset, localClockRate,
139                                                              remoteClockOffset, remoteClockRate,
140                                                              tauD, pvaDownlink,
141                                                              remotePV.getPVCoordinates(emissionDate, frame));
142 
143     }
144 
145     /** Compute common estimation parameters.
146      * @param states states of all spacecraft involved in the measurement
147      * @param clockOffsetAlreadyApplied if true, the specified {@code date} is as read
148      * by the receiver clock (i.e. clock offset <em>not</em> compensated), if false,
149      * the specified {@code date} was already compensated and is a physical absolute date
150      * @return common parameters
151      */
152     protected OnBoardCommonParametersWithDerivatives computeCommonParametersWith(final SpacecraftState[] states,
153                                                                                  final boolean clockOffsetAlreadyApplied) {
154 
155         final Frame frame = states[0].getFrame();
156 
157         // measurement derivatives are computed with respect to spacecraft state in inertial frame
158         // Parameters:
159         //  - 6k..6k+2 - Position of spacecraft k (counting k from 0 to nbSat-1) in inertial frame
160         //  - 6k+3..6k+5 - Velocity of spacecraft k (counting k from 0 to nbSat-1) in inertial frame
161         //  - 6nbSat..n - measurements parameters (clock offset, etc)
162         int nbEstimatedParams = 6 * states.length;
163         final Map<String, Integer> parameterIndices = new HashMap<>();
164         for (ParameterDriver measurementDriver : getParametersDrivers()) {
165             if (measurementDriver.isSelected()) {
166                 for (Span<String> span = measurementDriver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {
167                     parameterIndices.put(span.getData(), nbEstimatedParams++);
168                 }
169             }
170         }
171         final FieldAbsoluteDate<Gradient> gDate = new FieldAbsoluteDate<>(GradientField.getField(nbEstimatedParams),
172                                                                           getDate());
173 
174         // local and remote satellites
175         final TimeStampedFieldPVCoordinates<Gradient> pvaLocal         = getCoordinates(states[0], 0, nbEstimatedParams);
176         final QuadraticFieldClockModel<Gradient>      localClock       = getSatellites().get(0).getQuadraticClockModel().
177                                                                          toGradientModel(nbEstimatedParams, parameterIndices, getDate());
178         final FieldClockOffset<Gradient>              localClockOffset = localClock.getOffset(gDate);
179         final FieldPVCoordinatesProvider<Gradient>    remotePV         = getRemotePV(states, nbEstimatedParams);
180 
181         // take clock offset into account
182         final FieldAbsoluteDate<Gradient> arrivalDate = clockOffsetAlreadyApplied ?
183                                                         gDate : gDate.shiftedBy(localClockOffset.getOffset().negate());
184 
185         // Downlink delay
186         final Gradient deltaT = arrivalDate.durationFrom(states[0].getDate());
187         final TimeStampedFieldPVCoordinates<Gradient> pvaDownlink = pvaLocal.shiftedBy(deltaT);
188         final Gradient tauD = signalTimeOfFlightAdjustableEmitter(remotePV, arrivalDate,
189                                                                   pvaDownlink.getPosition(), arrivalDate,
190                                                                   frame);
191 
192         // Remote satellite at signal emission
193         final FieldAbsoluteDate<Gradient>        emissionDate      = arrivalDate.shiftedBy(tauD.negate());
194         final QuadraticFieldClockModel<Gradient> remoteClock       = getRemoteClock(nbEstimatedParams, parameterIndices);
195         final FieldClockOffset<Gradient>         remoteClockOffset = remoteClock.getOffset(emissionDate);
196         return new OnBoardCommonParametersWithDerivatives(states[0], parameterIndices,
197                                                           localClockOffset.getOffset(), localClockOffset.getRate(),
198                                                           remoteClockOffset.getOffset(), remoteClockOffset.getRate(),
199                                                           tauD, pvaDownlink,
200                                                           remotePV.getPVCoordinates(emissionDate, frame));
201 
202     }
203 
204 }