1   /* Copyright 2002-2025 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.IdentityHashMap;
20  import java.util.Map;
21  import java.util.stream.Stream;
22  
23  import org.orekit.errors.OrekitIllegalArgumentException;
24  import org.orekit.errors.OrekitIllegalStateException;
25  import org.orekit.errors.OrekitMessages;
26  import org.orekit.propagation.SpacecraftState;
27  import org.orekit.time.AbsoluteDate;
28  import org.orekit.utils.ParameterDriver;
29  import org.orekit.utils.TimeSpanMap;
30  import org.orekit.utils.TimeStampedPVCoordinates;
31  import org.orekit.utils.TimeSpanMap.Span;
32  
33  /** Class holding an estimated theoretical value associated to an {@link ObservedMeasurement observed measurement}.
34   * @param <T> the type of the measurement
35   * @author Luc Maisonobe
36   * @since 8.0
37   */
38  public class EstimatedMeasurement<T extends ObservedMeasurement<T>> extends EstimatedMeasurementBase<T> {
39  
40      /** Partial derivatives with respect to states. */
41      private double[][][] stateDerivatives;
42  
43      /** Partial derivatives with respect to parameters. */
44      private final Map<ParameterDriver, TimeSpanMap<double[]>> parametersDerivatives;
45  
46      /** Simple constructor.
47       * @param observedMeasurement associated observed measurement
48       * @param iteration iteration number
49       * @param count evaluations counter
50       * @param states states of the spacecrafts
51       * @param participants coordinates of the participants in signal travel order
52       * in inertial frame
53       */
54      public EstimatedMeasurement(final T observedMeasurement,
55                                  final int iteration, final int count,
56                                  final SpacecraftState[] states,
57                                  final TimeStampedPVCoordinates[] participants) {
58          super(observedMeasurement, iteration, count, states, participants);
59          this.stateDerivatives      = new double[states.length][][];
60          this.parametersDerivatives = new IdentityHashMap<>();
61      }
62  
63      /** Constructor from measurement base.
64       * @param estimatedMeasurementBase estimated measurement base
65       * @since 12.1.3
66       */
67      public EstimatedMeasurement(final EstimatedMeasurementBase<T> estimatedMeasurementBase) {
68          this(estimatedMeasurementBase.getObservedMeasurement(), estimatedMeasurementBase.getIteration(),
69              estimatedMeasurementBase.getCount(), estimatedMeasurementBase.getStates(),
70              estimatedMeasurementBase.getParticipants());
71          this.setEstimatedValue(estimatedMeasurementBase.getEstimatedValue());
72          this.setStatus(estimatedMeasurementBase.getStatus());
73      }
74  
75      /** Get state size.
76       * <p>
77       * Warning, the {@link #setStateDerivatives(int, double[][])}
78       * method must have been called before this method is called.
79       * </p>
80       * @return state size
81       * @since 10.1
82       */
83      public int getStateSize() {
84          return stateDerivatives[0][0].length;
85      }
86  
87      /** Get the partial derivatives of the {@link #getEstimatedValue()
88       * simulated measurement} with respect to state Cartesian coordinates.
89       * @param index index of the state, according to the {@code states}
90       * passed at construction
91       * @return partial derivatives of the simulated value (array of size
92       * {@link ObservedMeasurement#getDimension() dimension} x 6)
93       */
94      public double[][] getStateDerivatives(final int index) {
95          final double[][] sd = new double[getObservedMeasurement().getDimension()][];
96          for (int i = 0; i < getObservedMeasurement().getDimension(); ++i) {
97              sd[i] = stateDerivatives[index][i].clone();
98          }
99          return sd;
100     }
101 
102     /** Set the partial derivatives of the {@link #getEstimatedValue()
103      * simulated measurement} with respect to state Cartesian coordinates.
104      * @param index index of the state, according to the {@code states}
105      * passed at construction
106      * @param derivatives partial derivatives with respect to state
107      */
108     public void setStateDerivatives(final int index, final double[]... derivatives) {
109         this.stateDerivatives[index] = new double[getObservedMeasurement().getDimension()][];
110         for (int i = 0; i < getObservedMeasurement().getDimension(); ++i) {
111             this.stateDerivatives[index][i] = derivatives[i].clone();
112         }
113     }
114 
115     /** Get all the drivers with set derivatives.
116      * @return all the drivers with set derivatives
117      * @since 9.0
118      */
119     public Stream<ParameterDriver> getDerivativesDrivers() {
120         return parametersDerivatives.entrySet().stream().map(Map.Entry::getKey);
121     }
122 
123     /** Get the partial derivatives of the {@link #getEstimatedValue()
124      * simulated measurement} with respect to a parameter.
125      * @param driver name of the span of the driver for the parameter for which
126      * the derivative wants to be known.
127      * @return partial derivatives of the simulated value
128      * @exception OrekitIllegalArgumentException if parameter is unknown or
129      * OrekitIllegalStateException if this function is used on a PDriver having several
130      * values driven, in this case the method
131      * {@link #getParameterDerivatives(ParameterDriver, AbsoluteDate)} must be called
132      */
133     public double[] getParameterDerivatives(final ParameterDriver driver)
134         throws OrekitIllegalArgumentException {
135         if (driver.getNbOfValues() == 1) {
136             final TimeSpanMap<double[]> p = parametersDerivatives.get(driver);
137             if (p == null) {
138                 final StringBuilder builder = new StringBuilder();
139                 for (final Map.Entry<ParameterDriver, TimeSpanMap<double[]>> entry : parametersDerivatives.entrySet()) {
140                     if (builder.length() > 0) {
141                         builder.append(",  ");
142                     }
143                     builder.append(entry.getKey());
144                 }
145                 throw new OrekitIllegalArgumentException(OrekitMessages.UNSUPPORTED_PARAMETER_NAME,
146                                                          driver,
147                                                          builder.length() > 0 ? builder.toString() : " <none>");
148             }
149             return p.get(AbsoluteDate.ARBITRARY_EPOCH);
150         } else {
151             throw new OrekitIllegalStateException(OrekitMessages.PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES, driver.getName(), "getParameterDerivatives(driver, date)");
152         }
153     }
154 
155     /** Get the partial derivatives of the {@link #getEstimatedValue()
156      * simulated measurement} with respect to a parameter.
157      * @param driver name of the span of the driver for the parameter for which
158      * the derivative wants to be known.
159      * @param date date at which the parameter derivatives wants to be known
160      * @return partial derivatives of the simulated value
161      * @exception OrekitIllegalArgumentException if parameter is unknown
162      */
163     public double[] getParameterDerivatives(final ParameterDriver driver, final AbsoluteDate date)
164         throws OrekitIllegalArgumentException {
165         final TimeSpanMap<double[]> p = parametersDerivatives.get(driver);
166         if (p == null) {
167             final StringBuilder builder = new StringBuilder();
168             for (final Map.Entry<ParameterDriver, TimeSpanMap<double[]>> entry : parametersDerivatives.entrySet()) {
169                 if (builder.length() > 0) {
170                     builder.append(", ");
171                 }
172                 builder.append(entry.getKey());
173             }
174             throw new OrekitIllegalArgumentException(OrekitMessages.UNSUPPORTED_PARAMETER_NAME,
175                                                      driver,
176                                                      builder.length() > 0 ? builder.toString() : "<none>");
177         }
178         return p.get(date);
179     }
180 
181     /** Set the partial derivatives of the {@link #getEstimatedValue()
182      * simulated measurement} with respect to parameter.
183      * @param driver name of the span of the driver for the parameter for which
184      * the derivative wants to be known.
185      * @param date date at which the parameterDerivative wants to be set
186      * @param parameterDerivatives partial derivatives with respect to parameter
187      */
188     public void setParameterDerivatives(final ParameterDriver driver, final AbsoluteDate date, final double... parameterDerivatives) {
189         if (!parametersDerivatives.containsKey(driver) || parametersDerivatives.get(driver) == null) {
190             final TimeSpanMap<double[]> derivativeSpanMap = new TimeSpanMap<>(parameterDerivatives);
191             final TimeSpanMap<String> driverNameSpan = driver.getNamesSpanMap();
192             for (Span<String> span = driverNameSpan.getSpan(driverNameSpan.getFirstSpan().getEnd()); span != null; span = span.next()) {
193                 derivativeSpanMap.addValidAfter(parameterDerivatives, span.getStart(), false);
194             }
195             parametersDerivatives.put(driver, derivativeSpanMap);
196 
197         } else {
198 
199             AbsoluteDate dateToAddAfter = driver.getNamesSpanMap().getSpan(date).getStart();
200             if (dateToAddAfter.equals(AbsoluteDate.PAST_INFINITY)) {
201                 dateToAddAfter = driver.getNamesSpanMap().getSpan(date).getEnd();
202                 parametersDerivatives.get(driver).addValidBefore(parameterDerivatives, dateToAddAfter, false);
203             } else {
204                 parametersDerivatives.get(driver).addValidAfter(parameterDerivatives, dateToAddAfter, false);
205             }
206 
207         }
208 
209     }
210 
211     /** Set the partial derivatives of the {@link #getEstimatedValue()
212      * simulated measurement} with respect to parameter.
213      * @param driver driver for the parameter
214      * @param parameterDerivativesMap partial derivatives with respect to parameter
215      */
216     public void setParameterDerivatives(final ParameterDriver driver, final TimeSpanMap<double[]> parameterDerivativesMap) {
217         parametersDerivatives.put(driver, parameterDerivativesMap);
218     }
219 
220 }