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.forces.maneuvers.trigger;
18  
19  import java.util.stream.Stream;
20  
21  import org.hipparchus.Field;
22  import org.hipparchus.CalculusFieldElement;
23  import org.hipparchus.ode.events.Action;
24  import org.orekit.propagation.FieldSpacecraftState;
25  import org.orekit.propagation.SpacecraftState;
26  import org.orekit.propagation.events.DateDetector;
27  import org.orekit.propagation.events.EventDetector;
28  import org.orekit.propagation.events.FieldDateDetector;
29  import org.orekit.propagation.events.FieldEventDetector;
30  import org.orekit.time.AbsoluteDate;
31  import org.orekit.time.FieldAbsoluteDate;
32  
33  /** Maneuver triggers based on a start and end date, with no parameter drivers.
34   * @author Maxime Journot
35   * @since 10.2
36   */
37  public class DateBasedManeuverTriggers implements ManeuverTriggers {
38  
39      /** Start of the maneuver. */
40      private final AbsoluteDate startDate;
41  
42      /** End of the maneuver. */
43      private final AbsoluteDate endDate;
44  
45      /** Triggered date of engine start. */
46      private AbsoluteDate triggeredStart;
47  
48      /** Triggered date of engine stop. */
49      private AbsoluteDate triggeredEnd;
50  
51      /** Propagation direction. */
52      private boolean forward;
53  
54      public DateBasedManeuverTriggers(final AbsoluteDate date,
55                                       final double duration) {
56          if (duration >= 0) {
57              this.startDate = date;
58              this.endDate   = date.shiftedBy(duration);
59          } else {
60              this.endDate   = date;
61              this.startDate = endDate.shiftedBy(duration);
62          }
63          this.triggeredStart    = null;
64          this.triggeredEnd      = null;
65  
66      }
67  
68      /** Get the start date.
69       * @return the start date
70       */
71      public AbsoluteDate getStartDate() {
72          return startDate;
73      }
74  
75      /** Get the end date.
76       * @return the end date
77       */
78      public AbsoluteDate getEndDate() {
79          return endDate;
80      }
81  
82      /** Get the duration of the maneuver (s).
83       * duration = endDate - startDate
84       * @return the duration of the maneuver (s)
85       */
86      public double getDuration() {
87          return endDate.durationFrom(startDate);
88      }
89  
90      /** {@inheritDoc} */
91      @Override
92      public void init(final SpacecraftState initialState, final AbsoluteDate target) {
93          // set the initial value of firing
94          final AbsoluteDate sDate = initialState.getDate();
95          this.forward             = sDate.compareTo(target) < 0;
96          final boolean isBetween  = sDate.isBetween(startDate, endDate);
97          final boolean isOnStart  = startDate.compareTo(sDate) == 0;
98          final boolean isOnEnd    = endDate.compareTo(sDate) == 0;
99  
100         triggeredStart = null;
101         triggeredEnd   = null;
102         if (forward) {
103             if (isBetween || isOnStart) {
104                 triggeredStart = startDate;
105             }
106         } else {
107             if (isBetween || isOnEnd) {
108                 triggeredEnd = endDate;
109             }
110         }
111     }
112 
113     /** {@inheritDoc} */
114     @Override
115     public Stream<EventDetector> getEventsDetectors() {
116         // In forward propagation direction, firing must be enabled
117         // at start time and disabled at end time; in backward
118         // propagation direction, firing must be enabled
119         // at end time and disabled at start time
120         final DateDetector startDetector = new DateDetector(startDate).
121             withHandler((SpacecraftState state, DateDetector d, boolean increasing) -> {
122                 triggeredStart = state.getDate();
123                 return Action.RESET_DERIVATIVES;
124             }
125             );
126         final DateDetector endDetector = new DateDetector(endDate).
127             withHandler((SpacecraftState state, DateDetector d, boolean increasing) -> {
128                 triggeredEnd = state.getDate();
129                 return Action.RESET_DERIVATIVES;
130             });
131         return Stream.of(startDetector, endDetector);
132     }
133 
134     /** {@inheritDoc} */
135     @Override
136     public <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventsDetectors(final Field<T> field) {
137         // In forward propagation direction, firing must be enabled
138         // at start time and disabled at end time; in backward
139         // propagation direction, firing must be enabled
140         // at end time and disabled at start time
141         final FieldDateDetector<T> startDetector = new FieldDateDetector<>(new FieldAbsoluteDate<>(field, startDate)).
142             withHandler((FieldSpacecraftState<T> state, FieldDateDetector<T> d, boolean increasing) -> {
143                 triggeredStart = state.getDate().toAbsoluteDate();
144                 return Action.RESET_DERIVATIVES;
145             });
146         final FieldDateDetector<T> endDetector = new FieldDateDetector<>(new FieldAbsoluteDate<>(field, endDate)).
147             withHandler((FieldSpacecraftState<T> state, FieldDateDetector<T> d, boolean increasing) -> {
148                 triggeredEnd = state.getDate().toAbsoluteDate();
149                 return Action.RESET_DERIVATIVES;
150             });
151         return Stream.of(startDetector, endDetector);
152     }
153 
154     /** {@inheritDoc} */
155     @Override
156     public boolean isFiring(final AbsoluteDate date, final double[] parameters) {
157         // Firing state does not depend on a parameter driver here
158         return isFiring(date);
159     }
160 
161     @Override
162     public <T extends CalculusFieldElement<T>> boolean isFiring(final FieldAbsoluteDate<T> date,
163                                                             final T[] parameters) {
164         // Firing state does not depend on a parameter driver here
165         return isFiring(date.toAbsoluteDate());
166     }
167 
168     /** Check if maneuvering is on.
169      * @param date current date
170      * @return true if maneuver is on at this date
171      */
172     public boolean isFiring(final AbsoluteDate date) {
173         if (forward) {
174             if (triggeredStart == null) {
175                 // explicitly ignores state date, as propagator did not allow us to introduce discontinuity
176                 return false;
177             } else if (date.durationFrom(triggeredStart) < 0.0) {
178                 // we are unambiguously before maneuver start
179                 return false;
180             } else {
181                 if (triggeredEnd == null) {
182                     // explicitly ignores state date, as propagator did not allow us to introduce discontinuity
183                     return true;
184                 } else if (date.durationFrom(triggeredEnd) < 0.0) {
185                     // we are unambiguously before maneuver end
186                     return true;
187                 } else {
188                     // we are at or after maneuver end
189                     return false;
190                 }
191             }
192         } else {
193             if (triggeredEnd == null) {
194                 // explicitly ignores state date, as propagator did not allow us to introduce discontinuity
195                 return false;
196             } else if (date.durationFrom(triggeredEnd) > 0.0) {
197                 // we are unambiguously after maneuver end
198                 return false;
199             } else {
200                 if (triggeredStart == null) {
201                     // explicitly ignores state date, as propagator did not allow us to introduce discontinuity
202                     return true;
203                 } else if (date.durationFrom(triggeredStart) > 0.0) {
204                     // we are unambiguously after maneuver start
205                     return true;
206                 } else {
207                     // we are at or before maneuver start
208                     return false;
209                 }
210             }
211         }
212     }
213 
214 }