1   /* Copyright 2002-2021 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.utils.ParameterDriver;
28  import org.orekit.utils.ParameterDriversList;
29  import org.orekit.utils.TimeStampedPVCoordinates;
30  
31  /** Class multiplexing several measurements as one.
32   * <p>
33   * Date comes from the first measurement, observed and estimated
34   * values result from gathering all underlying measurements values.
35   *
36   * @author Luc Maisonobe
37   * @since 10.1
38   */
39  public class MultiplexedMeasurement extends AbstractMeasurement<MultiplexedMeasurement> {
40  
41      /** Multiplexed measurements. */
42      private final List<ObservedMeasurement<?>> observedMeasurements;
43  
44      /** Multiplexed measurements. */
45      private final List<EstimatedMeasurement<?>> estimatedMeasurements;
46  
47      /** Multiplexed parameters drivers. */
48      private ParameterDriversList parametersDrivers;
49  
50      /** Total dimension. */
51      private final int dimension;
52  
53      /** Total number of satellites involved. */
54      private final int nbSat;
55  
56      /** States mapping. */
57      private final int[][] mapping;
58  
59      /** Simple constructor.
60       * @param measurements measurements to multiplex
61       * @since 10.1
62       */
63      public MultiplexedMeasurement(final List<ObservedMeasurement<?>> measurements) {
64          super(measurements.get(0).getDate(),
65                multiplex(measurements, m -> m.getObservedValue()),
66                multiplex(measurements, m -> m.getTheoreticalStandardDeviation()),
67                multiplex(measurements, m -> m.getBaseWeight()),
68                multiplex(measurements));
69  
70          this.observedMeasurements  = measurements;
71          this.estimatedMeasurements = new ArrayList<>();
72          this.parametersDrivers     = new ParameterDriversList();
73  
74          // gather parameters drivers
75          int dim = 0;
76          for (final ObservedMeasurement<?> m : measurements) {
77              for (final ParameterDriver driver : m.getParametersDrivers()) {
78                  parametersDrivers.add(driver);
79              }
80              dim += m.getDimension();
81          }
82          parametersDrivers.sort();
83          for (final ParameterDriver driver : parametersDrivers.getDrivers()) {
84              addParameterDriver(driver);
85          }
86          this.dimension = dim;
87  
88          // set up states mappings for observed satellites
89          final List<ObservableSatellite> deduplicated = getSatellites();
90          this.nbSat   = deduplicated.size();
91          this.mapping = new int[measurements.size()][];
92          for (int i = 0; i < mapping.length; ++i) {
93              final List<ObservableSatellite> satellites = measurements.get(i).getSatellites();
94              mapping[i] = new int[satellites.size()];
95              for (int j = 0; j < mapping[i].length; ++j) {
96                  final int index = satellites.get(j).getPropagatorIndex();
97                  for (int k = 0; k < nbSat; ++k) {
98                      if (deduplicated.get(k).getPropagatorIndex() == index) {
99                          mapping[i][j] = k;
100                         break;
101                     }
102                 }
103             }
104         }
105 
106     }
107 
108     /** Get the underlying measurements.
109      * @return underlying measurements
110      */
111     public List<ObservedMeasurement<?>> getMeasurements() {
112         return observedMeasurements;
113     }
114 
115     /** Get the underlying estimated measurements.
116      * @return underlying estimated measurements
117      */
118     public List<EstimatedMeasurement<?>> getEstimatedMeasurements() {
119         return estimatedMeasurements;
120     }
121 
122     /** {@inheritDoc} */
123     @Override
124     protected EstimatedMeasurement<MultiplexedMeasurement> theoreticalEvaluation(final int iteration, final int evaluation,
125                                                                                  final SpacecraftState[] states) {
126 
127         final SpacecraftState[]              evaluationStates = new SpacecraftState[nbSat];
128         final List<TimeStampedPVCoordinates> participants     = new ArrayList<>();
129         final double[]                       value            = new double[dimension];
130 
131         // loop over all multiplexed measurements
132         estimatedMeasurements.clear();
133         int index = 0;
134         for (int i = 0; i < observedMeasurements.size(); ++i) {
135 
136             // filter states involved in the current measurement
137             final SpacecraftState[] filteredStates = new SpacecraftState[mapping[i].length];
138             for (int j = 0; j < mapping[i].length; ++j) {
139                 filteredStates[j] = states[mapping[i][j]];
140             }
141 
142             // perform evaluation
143             final EstimatedMeasurement<?> eI = observedMeasurements.get(i).estimate(iteration, evaluation, filteredStates);
144             estimatedMeasurements.add(eI);
145 
146             // extract results
147             final double[] valueI = eI.getEstimatedValue();
148             System.arraycopy(valueI, 0, value, index, valueI.length);
149             index += valueI.length;
150 
151             // extract states
152             final SpacecraftState[] statesI = eI.getStates();
153             for (int j = 0; j < mapping[i].length; ++j) {
154                 evaluationStates[mapping[i][j]] = statesI[j];
155             }
156 
157         }
158 
159         // create multiplexed estimation
160         final EstimatedMeasurement<MultiplexedMeasurement> multiplexed =
161                         new EstimatedMeasurement<>(this, iteration, evaluation,
162                                                    evaluationStates,
163                                                    participants.toArray(new TimeStampedPVCoordinates[0]));
164 
165         // copy multiplexed value
166         multiplexed.setEstimatedValue(value);
167 
168         // combine derivatives
169         final int                            stateSize             = estimatedMeasurements.get(0).getStateSize();
170         final double[]                       zeroDerivative        = new double[stateSize];
171         final double[][][]                   stateDerivatives      = new double[nbSat][dimension][];
172         for (final double[][] m : stateDerivatives) {
173             Arrays.fill(m, zeroDerivative);
174         }
175 
176         final Map<ParameterDriver, double[]> parametersDerivatives = new IdentityHashMap<>();
177         index = 0;
178         for (int i = 0; i < observedMeasurements.size(); ++i) {
179 
180             final EstimatedMeasurement<?> eI   = estimatedMeasurements.get(i);
181             final int                     idx  = index;
182             final int                     dimI = eI.getObservedMeasurement().getDimension();
183 
184             // state derivatives
185             for (int j = 0; j < mapping[i].length; ++j) {
186                 System.arraycopy(eI.getStateDerivatives(j), 0,
187                                  stateDerivatives[mapping[i][j]], index,
188                                  dimI);
189             }
190 
191             // parameters derivatives
192             eI.getDerivativesDrivers().forEach(driver -> {
193                 final ParameterDriversList.DelegatingDriver delegating = parametersDrivers.findByName(driver.getName());
194                 double[] derivatives = parametersDerivatives.get(delegating);
195                 if (derivatives == null) {
196                     derivatives = new double[dimension];
197                     parametersDerivatives.put(delegating, derivatives);
198                 }
199                 System.arraycopy(eI.getParameterDerivatives(driver), 0, derivatives, idx, dimI);
200             });
201 
202             index += dimI;
203 
204         }
205 
206         // set states derivatives
207         for (int i = 0; i < nbSat; ++i) {
208             multiplexed.setStateDerivatives(i, stateDerivatives[i]);
209         }
210 
211         // set parameters derivatives
212         parametersDerivatives.
213             entrySet().
214             stream().
215             forEach(e -> multiplexed.setParameterDerivatives(e.getKey(), e.getValue()));
216 
217         return multiplexed;
218 
219     }
220 
221     /** Multiplex measurements data.
222      * @param measurements measurements to multiplex
223      * @param extractor data extraction function
224      * @return multiplexed data
225      */
226     private static double[] multiplex(final List<ObservedMeasurement<?>> measurements,
227                                       final Function<ObservedMeasurement<?>, double[]> extractor) {
228 
229         // gather individual parts
230         final List<double[]> parts = new ArrayList<> (measurements.size());
231         int n = 0;
232         for (final ObservedMeasurement<?> measurement : measurements) {
233             final double[] p = extractor.apply(measurement);
234             parts.add(p);
235             n += p.length;
236         }
237 
238         // create multiplexed data
239         final double[] multiplexed = new double[n];
240         int index = 0;
241         for (final double[] p : parts) {
242             System.arraycopy(p, 0, multiplexed, index, p.length);
243             index += p.length;
244         }
245 
246         return multiplexed;
247 
248     }
249 
250     /** Multiplex satellites data.
251      * @param measurements measurements to multiplex
252      * @return multiplexed satellites data
253      */
254     private static List<ObservableSatellite> multiplex(final List<ObservedMeasurement<?>> measurements) {
255 
256         final List<ObservableSatellite> satellites = new ArrayList<>();
257 
258         // gather all satellites, removing duplicates
259         for (final ObservedMeasurement<?> measurement : measurements) {
260             for (final ObservableSatellite satellite : measurement.getSatellites()) {
261                 boolean searching = true;
262                 for (int i = 0; i < satellites.size() && searching; ++i) {
263                     // check if we already know this satellite
264                     searching = satellite.getPropagatorIndex() != satellites.get(i).getPropagatorIndex();
265                 }
266                 if (searching) {
267                     // this is a new satellite, add it to the global list
268                     satellites.add(satellite);
269                 }
270             }
271         }
272 
273         return satellites;
274 
275     }
276 
277 }