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.generation;
18  
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.Comparator;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.SortedSet;
27  import java.util.TreeSet;
28  
29  import org.orekit.estimation.measurements.EstimatedMeasurementBase;
30  import org.orekit.estimation.measurements.ObservableSatellite;
31  import org.orekit.estimation.measurements.ObservedMeasurement;
32  import org.orekit.propagation.Propagator;
33  import org.orekit.propagation.PropagatorsParallelizer;
34  import org.orekit.propagation.SpacecraftState;
35  import org.orekit.propagation.sampling.MultiSatStepHandler;
36  import org.orekit.propagation.sampling.OrekitStepHandler;
37  import org.orekit.propagation.sampling.OrekitStepInterpolator;
38  import org.orekit.propagation.sampling.StepHandlerMultiplexer;
39  import org.orekit.time.AbsoluteDate;
40  
41  
42  /** Main generator for {@link ObservedMeasurement observed measurements}.
43   * @author Luc Maisonobe
44   * @since 9.3
45   */
46  public class Generator {
47  
48      /** Observable satellites.
49       * @since 12.0
50       */
51      private final List<ObservableSatellite> observableSatellites;
52  
53      /** Propagators. */
54      private final List<Propagator> propagators;
55  
56      /** Schedulers for multiple satellites measurements. */
57      private final List<Scheduler<? extends ObservedMeasurement<?>>> multiSatSchedulers;
58  
59      /** Schedulers for single satellite measurements. */
60      private final Map<ObservableSatellite, List<Scheduler<? extends ObservedMeasurement<?>>>> singleSatSchedulers;
61  
62      /** Subscribers for generated measurements events.
63       * @since 12.0
64       */
65      private final List<GeneratedMeasurementSubscriber> subscribers;
66  
67      /** Build a generator with no sequences generator.
68       */
69      public Generator() {
70          this.observableSatellites = new ArrayList<>();
71          this.propagators          = new ArrayList<>();
72          this.multiSatSchedulers   = new ArrayList<>();
73          this.singleSatSchedulers  = new HashMap<>();
74          this.subscribers          = new ArrayList<>();
75      }
76  
77      /** Add a propagator.
78       * @param propagator to add
79       * @return satellite satellite propagated by the propagator
80       */
81      public ObservableSatellite addPropagator(final Propagator propagator) {
82          return addPropagator(propagator, null);
83      }
84  
85      /** Add a propagator.
86       * @param propagator to add
87       * @param name satellite name (if null, a default name built from index will be used)
88       * @return satellite satellite propagated by the propagator
89       * @since 13.0
90       */
91      public ObservableSatellite addPropagator(final Propagator propagator, final String name) {
92          final ObservableSatellite os = new ObservableSatellite(propagators.size(), name);
93          observableSatellites.add(os);
94          propagators.add(propagator);
95          return os;
96      }
97  
98      /** Get a registered propagator.
99       * @param satellite satellite propagated by the propagator {@link #addPropagator(Propagator)}
100      * @return propagator corresponding to satellite
101      */
102     public Propagator getPropagator(final ObservableSatellite satellite) {
103         return propagators.get(satellite.getPropagatorIndex());
104     }
105 
106     /** Add a sequences generator for a specific measurement type.
107      * @param scheduler sequences generator to add
108      * @param <T> the type of the measurement
109      */
110     public <T extends ObservedMeasurement<T>> void addScheduler(final Scheduler<T> scheduler) {
111         final ObservableSatellite[] satellites = scheduler.getBuilder().getSatellites();
112         if (satellites.length == 1) {
113             // this scheduler manages only one satellite
114             // we can let the individual propagator handle it
115             final List<Scheduler<? extends ObservedMeasurement<?>>> list =
116                 singleSatSchedulers.computeIfAbsent(satellites[0], k -> new ArrayList<>());
117             list.add(scheduler);
118         } else {
119             // this scheduler manages several satellites at once
120             // we need to handle it at top level
121             multiSatSchedulers.add(scheduler);
122         }
123     }
124 
125     /** Add a subscriber.
126      * @param subscriber to add
127      * @see GatheringSubscriber
128      * @since 12.0
129      */
130     public void addSubscriber(final GeneratedMeasurementSubscriber subscriber) {
131         subscribers.add(subscriber);
132     }
133 
134     /** Generate measurements.
135      * @param start start of the measurements time span
136      * @param end end of the measurements time span
137      */
138     public void generate(final AbsoluteDate start, final AbsoluteDate end) {
139 
140         // set up top level handler
141         final MultipleSatGeneratorHandler globalHandler =
142                         new MultipleSatGeneratorHandler(multiSatSchedulers, subscribers,
143                                                         observableSatellites, end.isAfterOrEqualTo(start));
144 
145         // set up low level handlers
146         for (final Map.Entry<ObservableSatellite, List<Scheduler<? extends ObservedMeasurement<?>>>> entry : singleSatSchedulers.entrySet()) {
147             final StepHandlerMultiplexer multiplexer = propagators.get(entry.getKey().getPropagatorIndex()).getMultiplexer();
148             for (final Scheduler<?> scheduler : entry.getValue()) {
149                 multiplexer.add(new SingleSatGeneratorHandler<>(scheduler, globalHandler));
150             }
151         }
152 
153         // prepare parallelized generation
154         final PropagatorsParallelizer parallelizer = new PropagatorsParallelizer(propagators, globalHandler);
155 
156         // generate the measurements
157         parallelizer.propagate(start, end);
158 
159         // clean up low level handlers
160         for (final Map.Entry<ObservableSatellite, List<Scheduler<? extends ObservedMeasurement<?>>>> entry : singleSatSchedulers.entrySet()) {
161             // we need to clean up the step handlers in two loops to avoid concurrent modification exception
162             final StepHandlerMultiplexer multiplexer = propagators.get(entry.getKey().getPropagatorIndex()).getMultiplexer();
163             final List<OrekitStepHandler> toBeRemoved = new ArrayList<>();
164             for (final OrekitStepHandler handler : multiplexer.getHandlers()) {
165                 if (handler instanceof SingleSatGeneratorHandler &&
166                     ((SingleSatGeneratorHandler<?>) handler).globalHandler == globalHandler) {
167                     toBeRemoved.add(handler);
168                 }
169             }
170             for (final OrekitStepHandler handler : toBeRemoved) {
171                 multiplexer.remove(handler);
172             }
173         }
174 
175     }
176 
177     /** Handler for measurements generation steps, single satellite case.
178      * <p>
179      * These handlers are called from the individual propagators threads.
180      * This means they generate measurements in parallel.
181      * </p>
182      * @param <T> the type of the measurement
183      * @since 12.0
184      */
185     private static class SingleSatGeneratorHandler<T extends ObservedMeasurement<T>> implements OrekitStepHandler {
186 
187         /** Scheduler. */
188         private final Scheduler<T> scheduler;
189 
190         /** Satellite related to this scheduler. */
191         private final ObservableSatellite satellite;
192 
193         /** Global handler. */
194         private final MultipleSatGeneratorHandler globalHandler;
195 
196         /** Simple constructor.
197          * @param scheduler scheduler
198          * @param globalHandler global handler
199          */
200         SingleSatGeneratorHandler(final Scheduler<T> scheduler, final MultipleSatGeneratorHandler globalHandler) {
201             this.scheduler     = scheduler;
202             this.satellite     = scheduler.getBuilder().getSatellites()[0];
203             this.globalHandler = globalHandler;
204         }
205 
206         /** {@inheritDoc} */
207         @Override
208         public void init(final SpacecraftState state0, final AbsoluteDate t) {
209             scheduler.init(state0.getDate(), t);
210         }
211 
212         /** {@inheritDoc} */
213         @Override
214         public void handleStep(final OrekitStepInterpolator interpolator) {
215             globalHandler.addMeasurements(scheduler.generate(Collections.singletonMap(satellite, interpolator)));
216         }
217 
218     }
219 
220     /** Handler for measurements generation steps.
221      * <p>
222      * This handler is called from the propagator parallelizer thread.
223      * The parallelizer thread is called after the individual propagators thread,
224      * which may already have produced measurements ahead of time, so we must
225      * take care than within each step we handle only the measurements that belong
226      * to this step.
227      * </p>
228      */
229     private static class MultipleSatGeneratorHandler implements MultiSatStepHandler {
230 
231         /** Sequences generators. */
232         private final List<Scheduler<? extends ObservedMeasurement<?>>> schedulers;
233 
234         /** Subscribers for generated measurements events.
235          * @since 12.0
236          */
237         private final List<GeneratedMeasurementSubscriber> subscribers;
238 
239         /** Observable satellites.
240          * @since 12.0
241          */
242         private final List<ObservableSatellite> observableSatellites;
243 
244         /** Storage for sorted measurements within one step.
245          * @since 12.0
246          */
247         private final SortedSet<EstimatedMeasurementBase<?>> generated;
248 
249         /** Forward generation indicator.
250          * @since 12.0
251          */
252         private final boolean forward;
253 
254         /** Simple constructor.
255          * @param schedulers sequences generators
256          * @param subscribers subscribers for generated measurements events
257          * @param observableSatellites observable satellites
258          * @param forward if true, generation is forward
259          * @since 12.0
260          */
261         MultipleSatGeneratorHandler(final List<Scheduler<? extends ObservedMeasurement<?>>> schedulers,
262                                     final List<GeneratedMeasurementSubscriber> subscribers,
263                                     final List<ObservableSatellite> observableSatellites, final boolean forward) {
264 
265             // measurements comparator, consistent with generation direction
266             final Comparator<EstimatedMeasurementBase<?>> comparator = forward ? Comparator.naturalOrder() : Comparator.reverseOrder();
267 
268             this.schedulers           = schedulers;
269             this.subscribers          = subscribers;
270             this.observableSatellites = observableSatellites;
271             this.generated            = new TreeSet<>(comparator);
272             this.forward              = forward;
273 
274         }
275 
276         /** {@inheritDoc} */
277         @Override
278         public void init(final List<SpacecraftState> states0, final AbsoluteDate t) {
279 
280             final AbsoluteDate start = states0.get(0).getDate();
281 
282             // initialize schedulers
283             for (final Scheduler<?> scheduler : schedulers) {
284                 scheduler.init(start, t);
285             }
286 
287             // initialize subscribers
288             for (final GeneratedMeasurementSubscriber subscriber : subscribers) {
289                 subscriber.init(start, t);
290             }
291 
292         }
293 
294         /** {@inheritDoc} */
295         @Override
296         public void handleStep(final List<OrekitStepInterpolator> interpolators) {
297 
298             // prepare interpolators map
299             final Map<ObservableSatellite, OrekitStepInterpolator> interpolatorsMap =
300                             new HashMap<>(interpolators.size());
301             for (int i = 0; i < interpolators.size(); ++i) {
302                 interpolatorsMap.put(observableSatellites.get(i), interpolators.get(i));
303             }
304             final AbsoluteDate lastDate = interpolators.get(0).getCurrentState().getDate();
305 
306             synchronized (generated) {
307 
308                 // generate measurements, looping over schedulers
309                 for (final Scheduler<? extends ObservedMeasurement<?>> scheduler : schedulers) {
310                     generated.addAll(scheduler.generate(interpolatorsMap));
311                 }
312 
313                 // now that we have all measurements properly sorted, we can feed them to subscribers
314                 for (final Iterator<EstimatedMeasurementBase<?>> iterator = generated.iterator(); iterator.hasNext();) {
315                     final EstimatedMeasurementBase<?> measurement = iterator.next();
316                     if (forward == lastDate.isAfterOrEqualTo(measurement)) {
317                         // this measurement belongs to the current step
318                         for (final GeneratedMeasurementSubscriber subscriber : subscribers) {
319                             subscriber.handleGeneratedMeasurement(measurement);
320                         }
321                         iterator.remove();
322                     } else {
323                         // this measurement belongs to an upcoming step ; we don't handle it yet as more
324                         // intermediate measurements may be produced by low level propagators threads
325                         break;
326                     }
327                 }
328 
329             }
330 
331         }
332 
333         /** {@inheritDoc} */
334         public void finish(final List<SpacecraftState> finalStates) {
335             synchronized (generated) {
336                 for (final EstimatedMeasurementBase<?> measurement : generated) {
337                     for (final GeneratedMeasurementSubscriber subscriber : subscribers) {
338                         subscriber.handleGeneratedMeasurement(measurement);
339                     }
340                 }
341                 generated.clear();
342             }
343         }
344 
345         /** Add measurements performed by a low level handler.
346          * @param measurements measurements to add
347          * @since 12.0
348          */
349         private void addMeasurements(final SortedSet<? extends EstimatedMeasurementBase<?>> measurements) {
350             synchronized (generated) {
351                 generated.addAll(measurements);
352             }
353         }
354 
355     }
356 
357 }