1   /* Copyright 2002-2024 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<ParameterDriver, TimeSpanMap<double[]>>();
61      }
62  
63      /** Get state size.
64       * <p>
65       * Warning, the {@link #setStateDerivatives(int, double[][])}
66       * method must have been called before this method is called.
67       * </p>
68       * @return state size
69       * @since 10.1
70       */
71      public int getStateSize() {
72          return stateDerivatives[0][0].length;
73      }
74  
75      /** Get the partial derivatives of the {@link #getEstimatedValue()
76       * simulated measurement} with respect to state Cartesian coordinates.
77       * @param index index of the state, according to the {@code states}
78       * passed at construction
79       * @return partial derivatives of the simulated value (array of size
80       * {@link ObservedMeasurement#getDimension() dimension} x 6)
81       */
82      public double[][] getStateDerivatives(final int index) {
83          final double[][] sd = new double[getObservedMeasurement().getDimension()][];
84          for (int i = 0; i < getObservedMeasurement().getDimension(); ++i) {
85              sd[i] = stateDerivatives[index][i].clone();
86          }
87          return sd;
88      }
89  
90      /** Set the partial derivatives of the {@link #getEstimatedValue()
91       * simulated measurement} with respect to state Cartesian coordinates.
92       * @param index index of the state, according to the {@code states}
93       * passed at construction
94       * @param derivatives partial derivatives with respect to state
95       */
96      public void setStateDerivatives(final int index, final double[]... derivatives) {
97          this.stateDerivatives[index] = new double[getObservedMeasurement().getDimension()][];
98          for (int i = 0; i < getObservedMeasurement().getDimension(); ++i) {
99              this.stateDerivatives[index][i] = derivatives[i].clone();
100         }
101     }
102 
103     /** Get all the drivers with set derivatives.
104      * @return all the drivers with set derivatives
105      * @since 9.0
106      */
107     public Stream<ParameterDriver> getDerivativesDrivers() {
108         return parametersDerivatives.entrySet().stream().map(entry -> entry.getKey());
109     }
110 
111     /** Get the partial derivatives of the {@link #getEstimatedValue()
112      * simulated measurement} with respect to a parameter.
113      * @param driver name of the span of the driver for the parameter for which
114      * the derivative wants to be known.
115      * @return partial derivatives of the simulated value
116      * @exception OrekitIllegalArgumentException if parameter is unknown or
117      * OrekitIllegalStateException if this function is used on a PDriver having several
118      * values driven, in this case the method
119      * {@link #getParameterDerivatives(ParameterDriver, AbsoluteDate)} must be called
120      */
121     public double[] getParameterDerivatives(final ParameterDriver driver)
122         throws OrekitIllegalArgumentException {
123         if (driver.getNbOfValues() == 1) {
124             final TimeSpanMap<double[]> p = parametersDerivatives.get(driver);
125             if (p == null) {
126                 final StringBuilder builder = new StringBuilder();
127                 for (final Map.Entry<ParameterDriver, TimeSpanMap<double[]>> entry : parametersDerivatives.entrySet()) {
128                     if (builder.length() > 0) {
129                         builder.append(",  ");
130                     }
131                     builder.append(entry.getKey());
132                 }
133                 throw new OrekitIllegalArgumentException(OrekitMessages.UNSUPPORTED_PARAMETER_NAME,
134                                                          driver,
135                                                          builder.length() > 0 ? builder.toString() : " <none>");
136             }
137             return p.get(AbsoluteDate.ARBITRARY_EPOCH);
138         } else {
139             throw new OrekitIllegalStateException(OrekitMessages.PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES, driver.getName(), "getParameterDerivatives(driver, date)");
140         }
141     }
142 
143     /** Get the partial derivatives of the {@link #getEstimatedValue()
144      * simulated measurement} with respect to a parameter.
145      * @param driver name of the span of the driver for the parameter for which
146      * the derivative wants to be known.
147      * @param date date at which the parameter derivatives wants to be known
148      * @return partial derivatives of the simulated value
149      * @exception OrekitIllegalArgumentException if parameter is unknown
150      */
151     public double[] getParameterDerivatives(final ParameterDriver driver, final AbsoluteDate date)
152         throws OrekitIllegalArgumentException {
153         final TimeSpanMap<double[]> p = parametersDerivatives.get(driver);
154         if (p == null) {
155             final StringBuilder builder = new StringBuilder();
156             for (final Map.Entry<ParameterDriver, TimeSpanMap<double[]>> entry : parametersDerivatives.entrySet()) {
157                 if (builder.length() > 0) {
158                     builder.append(", ");
159                 }
160                 builder.append(entry.getKey());
161             }
162             throw new OrekitIllegalArgumentException(OrekitMessages.UNSUPPORTED_PARAMETER_NAME,
163                                                      driver,
164                                                      builder.length() > 0 ? builder.toString() : "<none>");
165         }
166         return p.get(date);
167     }
168 
169     /** Set the partial derivatives of the {@link #getEstimatedValue()
170      * simulated measurement} with respect to parameter.
171      * @param driver name of the span of the driver for the parameter for which
172      * the derivative wants to be known.
173      * @param date date at which the parameterDerivative wants to be set
174      * @param parameterDerivatives partial derivatives with respect to parameter
175      */
176     public void setParameterDerivatives(final ParameterDriver driver, final AbsoluteDate date, final double... parameterDerivatives) {
177         if (!parametersDerivatives.containsKey(driver) || parametersDerivatives.get(driver) == null) {
178             final TimeSpanMap<double[]> derivativeSpanMap = new TimeSpanMap<double[]>(parameterDerivatives);
179             final TimeSpanMap<String> driverNameSpan = driver.getNamesSpanMap();
180             for (Span<String> span = driverNameSpan.getSpan(driverNameSpan.getFirstSpan().getEnd()); span != null; span = span.next()) {
181                 derivativeSpanMap.addValidAfter(parameterDerivatives, span.getStart(), false);
182             }
183             parametersDerivatives.put(driver, derivativeSpanMap);
184 
185         } else {
186 
187             AbsoluteDate dateToAddAfter = driver.getNamesSpanMap().getSpan(date).getStart();
188             if (dateToAddAfter.equals(AbsoluteDate.PAST_INFINITY)) {
189                 dateToAddAfter = driver.getNamesSpanMap().getSpan(date).getEnd();
190                 parametersDerivatives.get(driver).addValidBefore(parameterDerivatives, dateToAddAfter, false);
191             } else {
192                 parametersDerivatives.get(driver).addValidAfter(parameterDerivatives, dateToAddAfter, false);
193             }
194 
195         }
196 
197     }
198 
199     /** Set the partial derivatives of the {@link #getEstimatedValue()
200      * simulated measurement} with respect to parameter.
201      * @param driver driver for the parameter
202      * @param parameterDerivativesMap partial derivatives with respect to parameter
203      */
204     public void setParameterDerivatives(final ParameterDriver driver, final TimeSpanMap<double[]> parameterDerivativesMap) {
205         parametersDerivatives.put(driver, parameterDerivativesMap);
206     }
207 
208 }