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.ArrayList;
20  import java.util.Arrays;
21  import java.util.IdentityHashMap;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.function.Function;
25  
26  import org.orekit.propagation.SpacecraftState;
27  import org.orekit.time.AbsoluteDate;
28  import org.orekit.utils.ParameterDriver;
29  import org.orekit.utils.ParameterDriversList;
30  import org.orekit.utils.TimeSpanMap;
31  import org.orekit.utils.TimeStampedPVCoordinates;
32  import org.orekit.utils.TimeSpanMap.Span;
33  
34  /** Class multiplexing several measurements as one.
35   * <p>
36   * Date comes from the first measurement, observed and estimated
37   * values result from gathering all underlying measurements values.
38   *
39   * @author Luc Maisonobe
40   * @since 10.1
41   */
42  public class MultiplexedMeasurement extends AbstractMeasurement<MultiplexedMeasurement> {
43  
44      /** Type of the measurement. */
45      public static final String MEASUREMENT_TYPE = "MultiplexedMeasurement";
46  
47      /** Multiplexed measurements. */
48      private final List<ObservedMeasurement<?>> observedMeasurements;
49  
50      /** Multiplexed measurements without derivatives.
51       */
52      private final List<EstimatedMeasurementBase<?>> estimatedMeasurementsWithoutDerivatives;
53  
54      /** Multiplexed measurements. */
55      private final List<EstimatedMeasurement<?>> estimatedMeasurements;
56  
57      /** Multiplexed parameters drivers. */
58      private final ParameterDriversList parametersDrivers;
59  
60      /** Total dimension. */
61      private final int dimension;
62  
63      /** Total number of satellites involved. */
64      private final int nbSat;
65  
66      /** States mapping. */
67      private final int[][] multiplexedToUnderlying;
68  
69      /** States mapping. */
70      private final int[][] underlyingToMultiplexed;
71  
72      /** Simple constructor.
73       * @param measurements measurements to multiplex
74       * @since 10.1
75       */
76      public MultiplexedMeasurement(final List<ObservedMeasurement<?>> measurements) {
77          super(measurements.get(0).getDate(),
78                multiplex(measurements, ComparableMeasurement::getObservedValue),
79                multiplex(measurements, ObservedMeasurement::getTheoreticalStandardDeviation),
80                multiplex(measurements, ObservedMeasurement::getBaseWeight),
81                multiplex(measurements));
82  
83          this.observedMeasurements                    = measurements;
84          this.estimatedMeasurementsWithoutDerivatives = new ArrayList<>();
85          this.estimatedMeasurements                   = new ArrayList<>();
86          this.parametersDrivers                       = new ParameterDriversList();
87  
88          // gather parameters drivers
89          int dim = 0;
90          for (final ObservedMeasurement<?> m : measurements) {
91              for (final ParameterDriver driver : m.getParametersDrivers()) {
92                  parametersDrivers.add(driver);
93              }
94              dim += m.getDimension();
95          }
96          parametersDrivers.sort();
97          for (final ParameterDriver driver : parametersDrivers.getDrivers()) {
98              addParameterDriver(driver);
99          }
100         this.dimension = dim;
101 
102         // set up states mappings for observed satellites
103         final List<ObservableSatellite> deduplicated = getSatellites();
104         this.nbSat   = deduplicated.size();
105         this.multiplexedToUnderlying = new int[measurements.size()][];
106         this.underlyingToMultiplexed = new int[measurements.size()][deduplicated.size()];
107         for (int i = 0; i < multiplexedToUnderlying.length; ++i) {
108             final List<ObservableSatellite> satellites = measurements.get(i).getSatellites();
109             multiplexedToUnderlying[i] = new int[satellites.size()];
110             for (int j = 0; j < multiplexedToUnderlying[i].length; ++j) {
111                 final int index = satellites.get(j).getPropagatorIndex();
112                 for (int k = 0; k < nbSat; ++k) {
113                     if (deduplicated.get(k).getPropagatorIndex() == index) {
114                         multiplexedToUnderlying[i][j] = k;
115                         underlyingToMultiplexed[i][k] = j;
116                         break;
117                     }
118                 }
119             }
120         }
121 
122     }
123 
124     /** Get the underlying measurements.
125      * @return underlying measurements
126      */
127     public List<ObservedMeasurement<?>> getMeasurements() {
128         return observedMeasurements;
129     }
130 
131     /** Get the underlying estimated measurements without derivatives.
132      * @return underlying estimated measurements without derivatives
133      * @since 12.0
134      */
135     public List<EstimatedMeasurementBase<?>> getEstimatedMeasurementsWithoutDerivatives() {
136         return estimatedMeasurementsWithoutDerivatives;
137     }
138 
139     /** Get the underlying estimated measurements.
140      * @return underlying estimated measurements
141      */
142     public List<EstimatedMeasurement<?>> getEstimatedMeasurements() {
143         return estimatedMeasurements;
144     }
145 
146     /** Get the spacecraft state index in the underlying measurement.
147      * @param measurementIndex index of the underlying measurement
148      * @param multiplexedStateIndex index of the spacecraft state in the multiplexed array
149      * @return spacecraft state index in the underlying measurement
150      * @since 13.0
151      */
152     public int getUnderlyingStateIndex(final int measurementIndex, final int multiplexedStateIndex) {
153         return multiplexedToUnderlying[measurementIndex][multiplexedStateIndex];
154     }
155 
156     /** Get the spacecraft state index in the multiplexed measurement.
157      * @param measurementIndex index of the underlying measurement
158      * @param underlyingStateIndex index of the spacecraft state in the underlying array
159      * @return spacecraft state index in the multiplexed measurement
160      * @since 13.0
161      */
162     public int getMultiplexedStateIndex(final int measurementIndex, final int underlyingStateIndex) {
163         return underlyingToMultiplexed[measurementIndex][underlyingStateIndex];
164     }
165 
166     /** {@inheritDoc} */
167     @Override
168     protected EstimatedMeasurementBase<MultiplexedMeasurement> theoreticalEvaluationWithoutDerivatives(final int iteration,
169                                                                                                        final int evaluation,
170                                                                                                        final SpacecraftState[] states) {
171 
172         final SpacecraftState[]              evaluationStates = new SpacecraftState[nbSat];
173         final double[]                       value            = new double[dimension];
174 
175         // loop over all multiplexed measurements
176         estimatedMeasurementsWithoutDerivatives.clear();
177         int index = 0;
178         for (int i = 0; i < observedMeasurements.size(); ++i) {
179 
180             // filter states involved in the current measurement
181             final SpacecraftState[] filteredStates = new SpacecraftState[multiplexedToUnderlying[i].length];
182             for (int j = 0; j < multiplexedToUnderlying[i].length; ++j) {
183                 filteredStates[j] = states[getUnderlyingStateIndex(i, j)];
184             }
185 
186             // perform evaluation
187             final EstimatedMeasurementBase<?> eI = observedMeasurements.get(i).estimateWithoutDerivatives(iteration, evaluation, filteredStates);
188             estimatedMeasurementsWithoutDerivatives.add(eI);
189 
190             // extract results
191             final double[] valueI = eI.getEstimatedValue();
192             System.arraycopy(valueI, 0, value, index, valueI.length);
193             index += valueI.length;
194 
195             // extract states
196             final SpacecraftState[] statesI = eI.getStates();
197             for (int j = 0; j < multiplexedToUnderlying[i].length; ++j) {
198                 evaluationStates[multiplexedToUnderlying[i][j]] = statesI[j];
199             }
200 
201         }
202 
203         // create multiplexed estimation
204         final EstimatedMeasurementBase<MultiplexedMeasurement> multiplexed =
205                         new EstimatedMeasurementBase<>(this, iteration, evaluation,
206                                                        evaluationStates,
207                                                        new TimeStampedPVCoordinates[0]);
208 
209         // copy multiplexed value
210         multiplexed.setEstimatedValue(value);
211 
212         return multiplexed;
213 
214     }
215 
216     /** {@inheritDoc} */
217     @Override
218     protected EstimatedMeasurement<MultiplexedMeasurement> theoreticalEvaluation(final int iteration, final int evaluation,
219                                                                                  final SpacecraftState[] states) {
220 
221         final SpacecraftState[]              evaluationStates = new SpacecraftState[nbSat];
222         final double[]                       value            = new double[dimension];
223 
224         // loop over all multiplexed measurements
225         estimatedMeasurements.clear();
226         int index = 0;
227         for (int i = 0; i < observedMeasurements.size(); ++i) {
228 
229             // filter states involved in the current measurement
230             final SpacecraftState[] filteredStates = new SpacecraftState[multiplexedToUnderlying[i].length];
231             for (int j = 0; j < multiplexedToUnderlying[i].length; ++j) {
232                 filteredStates[j] = states[multiplexedToUnderlying[i][j]];
233             }
234 
235             // perform evaluation
236             final EstimatedMeasurement<?> eI = observedMeasurements.get(i).estimate(iteration, evaluation, filteredStates);
237             estimatedMeasurements.add(eI);
238 
239             // extract results
240             final double[] valueI = eI.getEstimatedValue();
241             System.arraycopy(valueI, 0, value, index, valueI.length);
242             index += valueI.length;
243 
244             // extract states
245             final SpacecraftState[] statesI = eI.getStates();
246             for (int j = 0; j < multiplexedToUnderlying[i].length; ++j) {
247                 evaluationStates[multiplexedToUnderlying[i][j]] = statesI[j];
248             }
249 
250         }
251 
252         // create multiplexed estimation
253         final EstimatedMeasurement<MultiplexedMeasurement> multiplexed =
254                         new EstimatedMeasurement<>(this, iteration, evaluation,
255                                                    evaluationStates,
256                                                    new TimeStampedPVCoordinates[0]);
257 
258         // copy multiplexed value
259         multiplexed.setEstimatedValue(value);
260 
261         // combine derivatives
262         final int                            stateSize             = estimatedMeasurements.get(0).getStateSize();
263         final double[]                       zeroDerivative        = new double[stateSize];
264         final double[][][]                   stateDerivatives      = new double[nbSat][dimension][];
265         for (final double[][] m : stateDerivatives) {
266             Arrays.fill(m, zeroDerivative);
267         }
268 
269         final Map<ParameterDriver, TimeSpanMap<double[]>> parametersDerivatives = new IdentityHashMap<>();
270         index = 0;
271         for (int i = 0; i < observedMeasurements.size(); ++i) {
272 
273             final EstimatedMeasurement<?> eI   = estimatedMeasurements.get(i);
274             final int                     idx  = index;
275             final int                     dimI = eI.getObservedMeasurement().getDimension();
276 
277             // state derivatives
278             for (int j = 0; j < multiplexedToUnderlying[i].length; ++j) {
279                 System.arraycopy(eI.getStateDerivatives(j), 0,
280                                  stateDerivatives[multiplexedToUnderlying[i][j]], index,
281                                  dimI);
282             }
283 
284             // parameters derivatives
285             eI.getDerivativesDrivers().forEach(driver -> {
286                 final ParameterDriversList.DelegatingDriver delegating = parametersDrivers.findByName(driver.getName());
287 
288                 if (parametersDerivatives.get(delegating) == null) {
289                     final TimeSpanMap<double[]> derivativeSpanMap = new TimeSpanMap<>(new double[dimension]);
290                     parametersDerivatives.put(delegating, derivativeSpanMap);
291                 }
292 
293                 final TimeSpanMap<Double> driverNameSpan = delegating.getValueSpanMap();
294                 for (Span<Double> span = driverNameSpan.getSpan(driverNameSpan.getFirstSpan().getEnd()); span != null; span = span.next()) {
295 
296                     double[] derivatives = parametersDerivatives.get(delegating).get(span.getStart());
297                     if (derivatives == null) {
298                         derivatives = new double[dimension];
299                     }
300                     if (!parametersDerivatives.get(delegating).getSpan(span.getStart()).getStart().equals(span.getStart())) {
301                         if ((span.getStart()).equals(AbsoluteDate.PAST_INFINITY)) {
302                             parametersDerivatives.get(delegating).addValidBefore(derivatives, span.getEnd(), false);
303                         } else {
304                             parametersDerivatives.get(delegating).addValidAfter(derivatives, span.getStart(), false);
305                         }
306 
307                     }
308 
309                     System.arraycopy(eI.getParameterDerivatives(driver, span.getStart()), 0, derivatives, idx, dimI);
310 
311                 }
312 
313             });
314 
315             index += dimI;
316 
317         }
318 
319         // set states derivatives
320         for (int i = 0; i < nbSat; ++i) {
321             multiplexed.setStateDerivatives(i, stateDerivatives[i]);
322         }
323 
324         // set parameters derivatives
325         parametersDerivatives.
326             entrySet().
327             forEach(e -> multiplexed.setParameterDerivatives(e.getKey(), e.getValue()));
328 
329         return multiplexed;
330 
331     }
332 
333     /** Multiplex measurements data.
334      * @param measurements measurements to multiplex
335      * @param extractor data extraction function
336      * @return multiplexed data
337      */
338     private static double[] multiplex(final List<ObservedMeasurement<?>> measurements,
339                                       final Function<ObservedMeasurement<?>, double[]> extractor) {
340 
341         // gather individual parts
342         final List<double[]> parts = new ArrayList<> (measurements.size());
343         int n = 0;
344         for (final ObservedMeasurement<?> measurement : measurements) {
345             final double[] p = extractor.apply(measurement);
346             parts.add(p);
347             n += p.length;
348         }
349 
350         // create multiplexed data
351         final double[] multiplexed = new double[n];
352         int index = 0;
353         for (final double[] p : parts) {
354             System.arraycopy(p, 0, multiplexed, index, p.length);
355             index += p.length;
356         }
357 
358         return multiplexed;
359 
360     }
361 
362     /** Multiplex satellites data.
363      * @param measurements measurements to multiplex
364      * @return multiplexed satellites data
365      */
366     private static List<ObservableSatellite> multiplex(final List<ObservedMeasurement<?>> measurements) {
367 
368         final List<ObservableSatellite> satellites = new ArrayList<>();
369 
370         // gather all satellites, removing duplicates
371         for (final ObservedMeasurement<?> measurement : measurements) {
372             for (final ObservableSatellite satellite : measurement.getSatellites()) {
373                 boolean searching = true;
374                 for (int i = 0; i < satellites.size() && searching; ++i) {
375                     // check if we already know this satellite
376                     searching = satellite.getPropagatorIndex() != satellites.get(i).getPropagatorIndex();
377                 }
378                 if (searching) {
379                     // this is a new satellite, add it to the global list
380                     satellites.add(satellite);
381                 }
382             }
383         }
384 
385         return satellites;
386 
387     }
388 
389 }