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.propagation.sampling;
18
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.Iterator;
22 import java.util.List;
23
24 import org.orekit.propagation.SpacecraftState;
25 import org.orekit.time.AbsoluteDate;
26
27 /** This class gathers several {@link OrekitStepHandler} instances into one.
28 *
29 * @author Luc Maisonobe
30 */
31 public class StepHandlerMultiplexer implements OrekitStepHandler {
32
33 /** Underlying step handlers. */
34 private final List<OrekitStepHandler> handlers;
35
36 /** Target time. */
37 private AbsoluteDate target;
38
39 /** Last known state. */
40 private SpacecraftState last;
41
42 /** Simple constructor.
43 */
44 public StepHandlerMultiplexer() {
45 handlers = new ArrayList<>();
46 }
47
48 /** Add a handler for variable size step.
49 * <p>
50 * If propagation is ongoing (i.e. global {@link #init(SpacecraftState, AbsoluteDate)
51 * init} already called and global {@link #finish(SpacecraftState) finish} not called
52 * yet), then the local {@link OrekitStepHandler#init(SpacecraftState, AbsoluteDate)
53 * OrekitStepHandler.init} method of the added handler will be called with the last
54 * known state, so the handler starts properly.
55 * </p>
56 * @param handler step handler to add
57 */
58 public void add(final OrekitStepHandler handler) {
59 handlers.add(handler);
60 if (last != null) {
61 // propagation is ongoing, we need to call init now for this handler
62 handler.init(last, target);
63 }
64 }
65
66 /** Add a handler for fixed size step.
67 * <p>
68 * If propagation is ongoing (i.e. global {@link #init(SpacecraftState, AbsoluteDate)
69 * init} already called and global {@link #finish(SpacecraftState) finish} not called
70 * yet), then the local {@link OrekitFixedStepHandler#init(SpacecraftState, AbsoluteDate, double)
71 * OrekitFixedStepHandler.init} method of the added handler will be called with the
72 * last known state, so the handler starts properly.
73 * </p>
74 * @param h fixed stepsize (s)
75 * @param handler handler called at the end of each finalized step
76 * @since 11.0
77 */
78 public void add(final double h, final OrekitFixedStepHandler handler) {
79 final OrekitStepHandler normalized = new OrekitStepNormalizer(h, handler);
80 handlers.add(normalized);
81 if (last != null) {
82 // propagation is ongoing, we need to call init now for this handler
83 normalized.init(last, target);
84 }
85 }
86
87 /** Get an unmodifiable view of all handlers.
88 * <p>
89 * Note that if {@link OrekitFixedStepHandler fixed step handlers} have
90 * been {@link #add(double, OrekitFixedStepHandler)}, then they will
91 * show up wrapped within {@link OrekitStepNormalizer step normalizers}.
92 * </p>
93 * @return an unmodifiable view of all handlers
94 * @since 11.0
95 */
96 public List<OrekitStepHandler> getHandlers() {
97 return Collections.unmodifiableList(handlers);
98 }
99
100 /** Remove a handler.
101 * <p>
102 * If propagation is ongoing (i.e. global {@link #init(SpacecraftState, AbsoluteDate)
103 * init} already called and global {@link #finish(SpacecraftState) finish} not called
104 * yet), then the local {@link OrekitStepHandler#finish(SpacecraftState)
105 * OrekitStepHandler.finish} method of the removed handler will be called with the last
106 * known state, so the handler stops properly.
107 * </p>
108 * @param handler step handler to remove
109 * @since 11.0
110 */
111 public void remove(final OrekitStepHandler handler) {
112 final Iterator<OrekitStepHandler> iterator = handlers.iterator();
113 while (iterator.hasNext()) {
114 if (iterator.next() == handler) {
115 if (last != null) {
116 // propagation is ongoing, we need to call finish now for this handler
117 handler.finish(last);
118 }
119 iterator.remove();
120 return;
121 }
122 }
123 }
124
125 /** Remove a handler.
126 * <p>
127 * If propagation is ongoing (i.e. global {@link #init(SpacecraftState, AbsoluteDate)
128 * init} already called and global {@link #finish(SpacecraftState) finish} not called
129 * yet), then the local {@link OrekitFixedStepHandler#finish(SpacecraftState)
130 * OrekitFixedStepHandler.finish} method of the removed handler will be called with the
131 * last known state, so the handler stops properly.
132 * </p>
133 * @param handler step handler to remove
134 * @since 11.0
135 */
136 public void remove(final OrekitFixedStepHandler handler) {
137 final Iterator<OrekitStepHandler> iterator = handlers.iterator();
138 while (iterator.hasNext()) {
139 final OrekitStepHandler current = iterator.next();
140 if (current instanceof OrekitStepNormalizer &&
141 ((OrekitStepNormalizer) current).getFixedStepHandler() == handler) {
142 if (last != null) {
143 // propagation is ongoing, we need to call finish now for this handler
144 current.finish(last);
145 }
146 iterator.remove();
147 return;
148 }
149 }
150 }
151
152 /** Remove all handlers managed by this multiplexer.
153 * <p>
154 * If propagation is ongoing (i.e. global {@link #init(SpacecraftState, AbsoluteDate)
155 * init} already called and global {@link #finish(SpacecraftState) finish} not called
156 * yet), then the local {@link OrekitStepHandler#finish(SpacecraftState)
157 * OrekitStepHandler.finish} and {@link OrekitFixedStepHandler#finish(SpacecraftState)
158 * OrekitFixedStepHandler.finish} methods of the removed handlers will be called with the
159 * last known state, so the handlers stop properly.
160 * </p>
161 * @since 11.0
162 */
163 public void clear() {
164 if (last != null) {
165 // propagation is ongoing, we need to call finish now for all handlers
166 handlers.forEach(h -> h.finish(last));
167 }
168 handlers.clear();
169 }
170
171 /** {@inheritDoc} */
172 public void init(final SpacecraftState s0, final AbsoluteDate t) {
173 this.target = t;
174 this.last = s0;
175 for (final OrekitStepHandler handler : handlers) {
176 handler.init(s0, t);
177 }
178 }
179
180 /** {@inheritDoc} */
181 public void handleStep(final OrekitStepInterpolator interpolator) {
182 this.last = interpolator.getCurrentState();
183 for (final OrekitStepHandler handler : handlers) {
184 handler.handleStep(interpolator);
185 }
186 }
187
188 /** {@inheritDoc} */
189 public void finish(final SpacecraftState finalState) {
190 this.target = null;
191 this.last = null;
192 for (final OrekitStepHandler handler : handlers) {
193 handler.finish(finalState);
194 }
195 }
196
197 }