1   /* Copyright 2002-2026 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.ArrayList;
20  import java.util.Collections;
21  import java.util.List;
22  import java.util.Map;
23  
24  import org.hipparchus.analysis.differentiation.Gradient;
25  import org.hipparchus.exception.LocalizedCoreFormats;
26  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
27  import org.hipparchus.geometry.euclidean.threed.Vector3D;
28  import org.orekit.errors.OrekitException;
29  import org.orekit.propagation.SpacecraftState;
30  import org.orekit.time.AbsoluteDate;
31  import org.orekit.utils.ParameterDriver;
32  import org.orekit.utils.TimeStampedFieldPVCoordinates;
33  import org.orekit.utils.TimeStampedPVCoordinates;
34  
35  /** Abstract class handling measurements boilerplate.
36   * @param <T> the type of the measurement
37   * @author Luc Maisonobe
38   * @since 8.0
39   */
40  public abstract class AbstractMeasurement<T extends ObservedMeasurement<T>> implements ObservedMeasurement<T> {
41  
42      /** List of the supported parameters. */
43      private final List<ParameterDriver> supportedParameters;
44  
45      /** Satellites related to this measurement.
46       * @since 9.3
47       */
48      private final List<ObservableSatellite> satellites;
49  
50      /** Date of the measurement. */
51      private final AbsoluteDate date;
52  
53      /** Observed value. */
54      private double[] observed;
55  
56      /** Measurement data. */
57      private final MeasurementQuality measurementQuality;
58  
59      /** Modifiers that apply to the measurement.*/
60      private final List<EstimationModifier<T>> modifiers;
61  
62      /** Enabling status. */
63      private boolean enabled;
64  
65      /** Simple constructor for mono-dimensional measurements.
66       * <p>
67       * At construction, a measurement is enabled.
68       * </p>
69       * @param date date of the measurement
70       * @param observed observed value
71       * @param sigma theoretical standard deviation
72       * @param baseWeight base weight
73       * @param satellites satellites related to this measurement
74       * @since 14.0
75       */
76      protected AbstractMeasurement(final AbsoluteDate date, final double observed,
77                                    final double sigma, final double baseWeight,
78                                    final List<ObservableSatellite> satellites) {
79          this(date, new double[] {observed}, new double[] {sigma}, new double[] {baseWeight}, satellites);
80      }
81  
82      /** Simple constructor, for multi-dimensional measurements.
83       * <p>
84       * At construction, a measurement is enabled.
85       * </p>
86       * @param date date of the measurement
87       * @param observed observed value
88       * @param sigma theoretical standard deviation
89       * @param baseWeight base weight
90       * @param satellites satellites related to this measurement
91       * @since 14.0
92       */
93      protected AbstractMeasurement(final AbsoluteDate date, final double[] observed,
94                                    final double[] sigma, final double[] baseWeight,
95                                    final List<ObservableSatellite> satellites) {
96          this(date, observed, new MeasurementQuality(sigma, baseWeight), satellites);
97      }
98  
99      /** Simple constructor, for multi-dimensional measurements.
100      * <p>
101      * At construction, a measurement is enabled.
102      * </p>
103      * @param date date of the measurement
104      * @param observed observed value
105      * @param measurementQuality measurement quality data
106      * @param satellites satellites related to this measurement
107      * @since 14.0
108      */
109     protected AbstractMeasurement(final AbsoluteDate date, final double[] observed,
110                                   final MeasurementQuality measurementQuality,
111                                   final List<ObservableSatellite> satellites) {
112         if (measurementQuality.getDimension() != observed.length) {
113             throw new OrekitException(LocalizedCoreFormats.DIMENSIONS_MISMATCH, measurementQuality.getDimension(), observed.length);
114         }
115         this.supportedParameters = new ArrayList<>();
116 
117         this.date       = date;
118         this.observed   = observed.clone();
119         this.measurementQuality = measurementQuality;
120         this.satellites = satellites;
121 
122         // Add parameter drivers
123         satellites.forEach(s -> addParametersDrivers(s.getParametersDrivers()));
124 
125         this.modifiers = new ArrayList<>();
126         setEnabled(true);
127     }
128 
129     /** {@inheritDoc} */
130     @Override
131     public void setObservedValue(final double[] newObserved) {
132         this.observed = newObserved.clone();
133     }
134 
135     /** Add a parameter driver.
136      * @param driver parameter driver to add
137      * @since 9.3
138      */
139     protected void addParameterDriver(final ParameterDriver driver) {
140         supportedParameters.add(driver);
141     }
142 
143     /** Add a list of parameter drivers all at once.
144      * @param drivers list of parameter drivers to add
145      * @since 14.0
146      */
147     protected void addParametersDrivers(final List<ParameterDriver> drivers) {
148         for (final ParameterDriver driver : drivers) {
149             addParameterDriver(driver);
150         }
151     }
152 
153     /** {@inheritDoc} */
154     @Override
155     public List<ParameterDriver> getParametersDrivers() {
156         return Collections.unmodifiableList(supportedParameters);
157     }
158 
159     /** {@inheritDoc} */
160     @Override
161     public final void setEnabled(final boolean enabled) {
162         this.enabled = enabled;
163     }
164 
165     /** {@inheritDoc} */
166     @Override
167     public boolean isEnabled() {
168         return enabled;
169     }
170 
171     /** {@inheritDoc} */
172     @Override
173     public MeasurementQuality getMeasurementQuality() {
174         return measurementQuality;
175     }
176 
177     /** {@inheritDoc} */
178     @Override
179     public List<ObservableSatellite> getSatellites() {
180         return satellites;
181     }
182 
183     /** Estimate the theoretical value without derivatives.
184      * The default implementation uses the computation with derivatives and ought to be overwritten for performance.
185      * <p>
186      * The theoretical value does not have <em>any</em> modifiers applied.
187      * </p>
188      * @param iteration iteration number
189      * @param evaluation evaluation number
190      * @param states orbital states at measurement date
191      * @return theoretical value
192      * @see #estimate(int, int, SpacecraftState[])
193      * @since 12.0
194      */
195     protected EstimatedMeasurementBase<T> theoreticalEvaluationWithoutDerivatives(final int iteration,
196                                                                                   final int evaluation,
197                                                                                   final SpacecraftState[] states) {
198         return theoreticalEvaluationWithoutDerivatives(iteration, evaluation, states, true);
199     }
200 
201     /** Estimate the theoretical value without derivatives.
202      * The default implementation uses the computation with derivatives and ought to be overwritten for performance.
203      * <p>
204      * The theoretical value does not have <em>any</em> modifiers applied.
205      * </p>
206      * @param iteration iteration number
207      * @param evaluation evaluation number
208      * @param states orbital states at measurement date
209      * @param fillParticipants flag to compute and store participants dynamical states at measurement date and along signal path if applicable
210      * @return theoretical value
211      * @see #estimate(int, int, SpacecraftState[])
212      * @since 14.0
213      */
214     protected EstimatedMeasurementBase<T> theoreticalEvaluationWithoutDerivatives(final int iteration,
215                                                                                   final int evaluation,
216                                                                                   final SpacecraftState[] states,
217                                                                                   final boolean fillParticipants) {
218         final EstimatedMeasurement<T> estimatedMeasurement = theoreticalEvaluation(iteration, evaluation, states);
219         final TimeStampedPVCoordinates[] participants = fillParticipants ? estimatedMeasurement.getParticipants() :
220                 new TimeStampedPVCoordinates[0];
221         final EstimatedMeasurementBase<T> estimatedMeasurementBase = new EstimatedMeasurementBase<>(estimatedMeasurement.getObservedMeasurement(),
222                 iteration, evaluation, states, participants);
223         estimatedMeasurementBase.setEstimatedValue(estimatedMeasurement.getEstimatedValue());
224         estimatedMeasurementBase.setStatus(estimatedMeasurement.getStatus());
225         return estimatedMeasurementBase;
226     }
227 
228     /** Estimate the theoretical value.
229      * <p>
230      * The theoretical value does not have <em>any</em> modifiers applied.
231      * </p>
232      * @param iteration iteration number
233      * @param evaluation evaluation number
234      * @param states orbital states at measurement date
235      * @return theoretical value
236      * @see #estimate(int, int, SpacecraftState[])
237      */
238     protected abstract EstimatedMeasurement<T> theoreticalEvaluation(int iteration, int evaluation, SpacecraftState[] states);
239 
240     /** {@inheritDoc}
241      * <p>
242      * For the sake of computational performance, the output will contain the participant's states only if at least
243      * one measurement modifier explicitly requires it (note that the estimated states can still be accessed {@link EstimatedMeasurementBase#getStates()}).
244      * </p>
245      */
246     @Override
247     public EstimatedMeasurementBase<T> estimateWithoutDerivatives(final int iteration, final int evaluation,
248                                                                   final SpacecraftState[] states) {
249 
250         // compute the theoretical value
251         final boolean fillParticipants = modifiers.stream().anyMatch(EstimationModifier::dependsOnParticipantsStates);
252         final EstimatedMeasurementBase<T> estimation = theoreticalEvaluationWithoutDerivatives(iteration, evaluation, states, fillParticipants);
253 
254         // apply the modifiers
255         for (final EstimationModifier<T> modifier : modifiers) {
256             modifier.modifyWithoutDerivatives(estimation);
257         }
258 
259         return estimation;
260 
261     }
262 
263     /** {@inheritDoc} */
264     @Override
265     public EstimatedMeasurement<T> estimate(final int iteration, final int evaluation, final SpacecraftState[] states) {
266 
267         // compute the theoretical value
268         final EstimatedMeasurement<T> estimation = theoreticalEvaluation(iteration, evaluation, states);
269 
270         // apply the modifiers
271         for (final EstimationModifier<T> modifier : modifiers) {
272             modifier.modify(estimation);
273         }
274 
275         return estimation;
276 
277     }
278 
279     /** {@inheritDoc} */
280     @Override
281     public AbsoluteDate getDate() {
282         return date;
283     }
284 
285     /** {@inheritDoc} */
286     @Override
287     public double[] getObservedValue() {
288         return observed.clone();
289     }
290 
291     /** {@inheritDoc} */
292     @Override
293     public void addModifier(final EstimationModifier<T> modifier) {
294 
295         // combine the measurement parameters and the modifier parameters
296         supportedParameters.addAll(modifier.getParametersDrivers());
297 
298         modifiers.add(modifier);
299 
300     }
301 
302     /** {@inheritDoc} */
303     @Override
304     public List<EstimationModifier<T>> getModifiers() {
305         return Collections.unmodifiableList(modifiers);
306     }
307 
308     /** Get Cartesian coordinates as derivatives.
309      * <p>
310      * The position will correspond to variables {@code firstDerivative},
311      * {@code firstDerivative + 1} and {@code firstDerivative + 2}.
312      * The velocity will correspond to variables {@code firstDerivative + 3},
313      * {@code firstDerivative + 4} and {@code firstDerivative + 5}.
314      * The acceleration will correspond to constants.
315      * </p>
316      * @param state state of the satellite considered
317      * @param firstDerivative index of the first derivative
318      * @param freeParameters total number of free parameters in the gradient
319      * @return Cartesian coordinates as derivatives
320      * @since 10.2
321      */
322     public static TimeStampedFieldPVCoordinates<Gradient> getCoordinates(final SpacecraftState state,
323                                                                          final int firstDerivative,
324                                                                          final int freeParameters) {
325 
326         // Position of the satellite expressed as a gradient
327         // The components of the position are the 3 first derivative parameters
328         final Vector3D p = state.getPosition();
329         final FieldVector3D<Gradient> pDS =
330                         new FieldVector3D<>(Gradient.variable(freeParameters, firstDerivative,     p.getX()),
331                                             Gradient.variable(freeParameters, firstDerivative + 1, p.getY()),
332                                             Gradient.variable(freeParameters, firstDerivative + 2, p.getZ()));
333 
334         // Velocity of the satellite expressed as a gradient
335         // The components of the velocity are the 3 second derivative parameters
336         final Vector3D v = state.getVelocity();
337         final FieldVector3D<Gradient> vDS =
338                         new FieldVector3D<>(Gradient.variable(freeParameters, firstDerivative + 3, v.getX()),
339                                             Gradient.variable(freeParameters, firstDerivative + 4, v.getY()),
340                                             Gradient.variable(freeParameters, firstDerivative + 5, v.getZ()));
341 
342         // Acceleration of the satellite
343         // The components of the acceleration are not derivative parameters
344         final Vector3D a = state.getPVCoordinates().getAcceleration();
345         final FieldVector3D<Gradient> aDS =
346                         new FieldVector3D<>(Gradient.constant(freeParameters, a.getX()),
347                                             Gradient.constant(freeParameters, a.getY()),
348                                             Gradient.constant(freeParameters, a.getZ()));
349 
350         return new TimeStampedFieldPVCoordinates<>(state.getDate(), pDS, vDS, aDS);
351 
352     }
353 
354     /**
355      * Form the mapping between parameters' names and derivatives' indices.
356      * @param states observables
357      * @return map
358      * @since 14.0
359      */
360     protected Map<String, Integer> getParameterIndices(final SpacecraftState[] states) {
361         return Observer.getParameterIndices(states, getParametersDrivers());
362     }
363 
364 }