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.time;
18  
19  import java.util.ArrayList;
20  import java.util.List;
21  
22  import org.hipparchus.util.FastMath;
23  
24  
25  /** Selector generating high rate bursts of dates separated by some rest period.
26   * <p>
27   * The dates can be aligned to whole steps in some time scale. So for example
28   * if a rest period of 3600s is used and the alignment time scale is set to
29   * {@link org.orekit.time.TimeScales#getUTC() UTC}, the earliest date of
30   * each burst will occur at whole hours in UTC time.
31   * </p>
32   * <p>
33   * BEWARE! This class stores internally the last selected dates, so it is <em>neither</em>
34   * reusable across several {@link org.orekit.estimation.measurements.generation.EventBasedScheduler
35   * fixed step} or {@link org.orekit.estimation.measurements.generation.ContinuousScheduler
36   * continuous} schedulers, <em>nor</em> thread-safe. A separate selector should be used for each
37   * scheduler and for each thread in multi-threading context.
38   * </p>
39   * @author Luc Maisonobe
40   * @since 9.3
41   */
42  public class BurstSelector implements DatesSelector {
43  
44      /** Maximum number of selected dates in a burst. */
45      private final int maxBurstSize;
46  
47      /** Step between two consecutive dates within a burst. */
48      private final double highRateStep;
49  
50      /** Period between the start of each burst. */
51      private final double burstPeriod;
52  
53      /** Alignment time scale (null is alignment is not needed). */
54      private final TimeScale alignmentTimeScale;
55  
56      /** First date in last burst. */
57      private AbsoluteDate first;
58  
59      /** Last selected date. */
60      private AbsoluteDate last;
61  
62      /** Index of last selected date in current burst. */
63      private int index;
64  
65      /** Simple constructor.
66       * <p>
67       * The {@code burstPeriod} ignores the duration of the burst itself. This
68       * means that if burst of {@code maxBurstSize=256} dates each separated by
69       * {@code highRateStep=100ms} should be selected with {@code burstPeriod=300s},
70       * then the first burst would contain 256 dates from {@code t0} to {@code t0+25.5s}
71       * and the second burst would start at {@code t0+300s}, <em>not</em> at
72       * {@code t0+325.5s}.
73       * </p>
74       * <p>
75       * If alignment to some time scale is needed, it applies only to the first date in
76       * each burst.
77       * </p>
78       * @param maxBurstSize maximum number of selected dates in a burst
79       * @param highRateStep step between two consecutive dates within a burst (s)
80       * @param burstPeriod period between the start of each burst (s)
81       * @param alignmentTimeScale alignment time scale for first date in burst
82       * (null is alignment is not needed)
83       */
84      public BurstSelector(final int maxBurstSize, final double highRateStep,
85                           final double burstPeriod, final TimeScale alignmentTimeScale) {
86          this.maxBurstSize       = maxBurstSize;
87          this.highRateStep       = highRateStep;
88          this.burstPeriod        = burstPeriod;
89          this.alignmentTimeScale = alignmentTimeScale;
90          this.last               = null;
91          this.first              = null;
92          this.index              = 0;
93      }
94  
95      /** {@inheritDoc} */
96      @Override
97      public List<AbsoluteDate> selectDates(final AbsoluteDate start, final AbsoluteDate end) {
98  
99          final int    increment          = end.durationFrom(start) > 0 ? +1 : -1;
100         final int    firstIndex         = increment > 0 ? 0 : maxBurstSize - 1;
101         final int    lastIndex          = maxBurstSize - 1 - firstIndex;
102         final double signedHighRateStep = FastMath.copySign(highRateStep, increment);
103         final double signedBurstPeriod  = FastMath.copySign(burstPeriod, increment);
104 
105         final List<AbsoluteDate> selected = new ArrayList<>();
106 
107         final boolean reset = first == null || increment * start.durationFrom(first) > burstPeriod;
108         if (reset) {
109             first = null;
110             index = firstIndex;
111         }
112 
113         for (AbsoluteDate next = reset ? start : last.shiftedBy(signedHighRateStep);
114              increment * next.durationFrom(end) <= 0;
115              next = last.shiftedBy(signedHighRateStep)) {
116 
117             if (index == lastIndex + increment) {
118                 // we have exceeded burst size, jump to next burst
119                 next  = first.shiftedBy(signedBurstPeriod);
120                 first = null;
121                 index = firstIndex;
122                 if (increment * next.durationFrom(end) > 0) {
123                     // next burst is out of current interval
124                     break;
125                 }
126             }
127 
128             if (first == null && alignmentTimeScale != null) {
129                 // align earliest burst date to time scale
130                 final double offset = firstIndex * highRateStep;
131                 final double t      = next.getComponents(alignmentTimeScale).getTime().getSecondsInLocalDay() - offset;
132                 final double dt     = burstPeriod * FastMath.round(t / burstPeriod) - t;
133                 next = next.shiftedBy(dt);
134                 while (index != lastIndex && increment * next.durationFrom(start) < 0) {
135                     next = next.shiftedBy(signedHighRateStep);
136                     index += increment;
137                 }
138                 if (increment * next.durationFrom(start) < 0) {
139                     // alignment shifted date out of interval
140                     next  = next.shiftedBy(signedBurstPeriod - (maxBurstSize - 1) * signedHighRateStep);
141                     index = firstIndex;
142                 }
143             }
144 
145             if (increment * next.durationFrom(start) >= 0) {
146                 if (increment * next.durationFrom(end) <= 0) {
147                     // the date is within range, select it
148                     if (first == null) {
149                         first = next.shiftedBy(-signedHighRateStep * index);
150                     }
151                     selected.add(next);
152                 } else {
153                     // we have exceeded date range
154                     break;
155                 }
156             }
157             last   = next;
158             index += increment;
159 
160         }
161 
162         return selected;
163 
164     }
165 
166 }