AttitudesSequence.java
/* Copyright 2002-2015 CS Systèmes d'Information
* Licensed to CS Systèmes d'Information (CS) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* CS licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.orekit.attitudes;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.orekit.errors.OrekitException;
import org.orekit.frames.Frame;
import org.orekit.propagation.Propagator;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.events.AbstractDetector;
import org.orekit.propagation.events.EventDetector;
import org.orekit.propagation.events.handlers.EventHandler;
import org.orekit.time.AbsoluteDate;
import org.orekit.utils.PVCoordinatesProvider;
/** This classes manages a sequence of different attitude providers that are activated
* in turn according to switching events.
* <p>Only one attitude provider in the sequence is in an active state. When one of
* the switch event associated with the active provider occurs, the active provider becomes
* the one specified with the event. A simple example is a provider for the sun lighted part
* of the orbit and another provider for the eclipse time. When the sun lighted provider is active,
* the eclipse entry event is checked and when it occurs the eclipse provider is activated.
* When the eclipse provider is active, the eclipse exit event is checked and when it occurs
* the sun lighted provider is activated again. This sequence is a simple loop.</p>
* <p>An active attitude provider may have several switch events and next provider settings, leading
* to different activation patterns depending on which events are triggered first. An example
* of this feature is handling switches to safe mode if some contingency condition is met, in
* addition to the nominal switches that correspond to proper operations. Another example
* is handling of maneuver mode.<p>
* @author Luc Maisonobe
* @since 5.1
*/
public class AttitudesSequence implements AttitudeProvider {
/** Serializable UID. */
private static final long serialVersionUID = 5140034224175180354L;
/** Active provider. */
private AttitudeProvider active;
/** Switching events map. */
private final Map<AttitudeProvider, Collection<Switch<?>>> switchingMap;
/** Constructor for an initially empty sequence.
*/
public AttitudesSequence() {
active = null;
switchingMap = new HashMap<AttitudeProvider, Collection<Switch<?>>>();
}
/** Reset the active provider.
* @param provider providerprovider to activate
*/
public void resetActiveProvider(final AttitudeProvider provider) {
// add the provider if not already known
if (!switchingMap.containsKey(provider)) {
switchingMap.put(provider, new ArrayList<Switch<?>>());
}
active = provider;
}
/** Register all wrapped switch events to the propagator.
* <p>
* This method must be called once before propagation, after the
* switching conditions have been set up by calls to {@link
* #addSwitchingCondition(AttitudeProvider, EventDetector, boolean, boolean, AttitudeProvider)}.
* </p>
* @param propagator propagator that will handle the events
*/
public void registerSwitchEvents(final Propagator propagator) {
for (final Collection<Switch<?>> collection : switchingMap.values()) {
for (final Switch<?> s : collection) {
propagator.addEventDetector(s);
}
}
}
/** Add a switching condition between two attitude providers.
* <p>
* An attitude provider may have several different switch events associated to
* it. Depending on which event is triggered, the appropriate provider is
* switched to.
* </p>
* <p>
* The switch events specified here must <em>not</em> be registered to the
* propagator directly. The proper way to register these events is to
* call {@link #registerSwitchEvents(Propagator)} once after all switching
* conditions have been set up. The reason for this is that the events will
* be wrapped before being registered.
* </p>
* @param before attitude provider before the switch event occurrence
* @param switchEvent event triggering the attitude providers switch (may be null
* for a provider without any ending condition, in this case the after provider
* is not referenced and may be null too)
* @param switchOnIncrease if true, switch is triggered on increasing event
* @param switchOnDecrease if true, switch is triggered on decreasing event
* @param after attitude provider to activate after the switch event occurrence
* (used only if switchEvent is non null)
* @param <T> class type for the generic version
*/
public <T extends EventDetector> void addSwitchingCondition(final AttitudeProvider before,
final T switchEvent,
final boolean switchOnIncrease,
final boolean switchOnDecrease,
final AttitudeProvider after) {
// add the before provider if not already known
if (!switchingMap.containsKey(before)) {
switchingMap.put(before, new ArrayList<Switch<?>>());
if (active == null) {
active = before;
}
}
if (switchEvent != null) {
// add the after provider if not already known
if (!switchingMap.containsKey(after)) {
switchingMap.put(after, new ArrayList<Switch<?>>());
}
// add the switching condition
switchingMap.get(before).add(new Switch<T>(switchEvent, switchOnIncrease, switchOnDecrease, after));
}
}
/** {@inheritDoc} */
public Attitude getAttitude(final PVCoordinatesProvider pvProv,
final AbsoluteDate date, final Frame frame)
throws OrekitException {
// delegate attitude computation to the active provider
return active.getAttitude(pvProv, date, frame);
}
/** Switch specification.
* @param <T> class type for the generic version
*/
private class Switch<T extends EventDetector> extends AbstractDetector<Switch<T>> {
/** Serializable UID. */
private static final long serialVersionUID = 20141228L;
/** Event. */
private final T event;
/** Event direction triggering the switch. */
private final boolean switchOnIncrease;
/** Event direction triggering the switch. */
private final boolean switchOnDecrease;
/** Next attitude provider. */
private final AttitudeProvider next;
/** Simple constructor.
* @param event event
* @param switchOnIncrease if true, switch is triggered on increasing event
* @param switchOnDecrease if true, switch is triggered on decreasing event
* otherwise switch is triggered on decreasing event
* @param next next attitude provider
*/
public Switch(final T event,
final boolean switchOnIncrease,
final boolean switchOnDecrease,
final AttitudeProvider next) {
this(event.getMaxCheckInterval(), event.getThreshold(), event.getMaxIterationCount(),
new LocalHandler<T>(), event, switchOnIncrease, switchOnDecrease, next);
}
/** Private constructor with full parameters.
* <p>
* This constructor is private as users are expected to use the builder
* API with the various {@code withXxx()} methods to set up the instance
* in a readable manner without using a huge amount of parameters.
* </p>
* @param maxCheck maximum checking interval (s)
* @param threshold convergence threshold (s)
* @param maxIter maximum number of iterations in the event time search
* @param handler event handler to call at event occurrences
* @param event event
* @param switchOnIncrease if true, switch is triggered on increasing event
* @param switchOnDecrease if true, switch is triggered on decreasing event
* otherwise switch is triggered on decreasing event
* @param next next attitude provider
* @since 6.1
*/
private Switch(final double maxCheck, final double threshold,
final int maxIter, final EventHandler<Switch<T>> handler, final T event,
final boolean switchOnIncrease, final boolean switchOnDecrease,
final AttitudeProvider next) {
super(maxCheck, threshold, maxIter, handler);
this.event = event;
this.switchOnIncrease = switchOnIncrease;
this.switchOnDecrease = switchOnDecrease;
this.next = next;
}
/** {@inheritDoc} */
@Override
protected Switch<T> create(final double newMaxCheck, final double newThreshold,
final int newMaxIter, final EventHandler<Switch<T>> newHandler) {
return new Switch<T>(newMaxCheck, newThreshold, newMaxIter, newHandler,
event, switchOnIncrease, switchOnDecrease, next);
}
/** Perform the switch.
*/
public void performSwitch() {
active = next;
}
/** {@inheritDoc} */
public void init(final SpacecraftState s0, final AbsoluteDate t) {
event.init(s0, t);
}
/** {@inheritDoc} */
public double g(final SpacecraftState s)
throws OrekitException {
return event.g(s);
}
}
/** Local handler.
* @param <T> class type for the generic version
*/
private static class LocalHandler<T extends EventDetector> implements EventHandler<Switch<T>> {
/** {@inheritDoc} */
public EventHandler.Action eventOccurred(final SpacecraftState s, final Switch<T> sw, final boolean increasing)
throws OrekitException {
if ((increasing && sw.switchOnIncrease) || (!increasing && sw.switchOnDecrease)) {
// switch to next attitude provider
sw.performSwitch();
}
return sw.event.eventOccurred(s, increasing);
}
/** {@inheritDoc} */
@Override
public SpacecraftState resetState(final Switch<T> sw, final SpacecraftState oldState)
throws OrekitException {
return sw.event.resetState(oldState);
}
}
}