AbstractRadiationForceModel.java

  1. /* Copyright 2002-2022 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.radiation;

  18. import java.lang.reflect.Array;
  19. import java.util.HashMap;
  20. import java.util.Map;
  21. import java.util.stream.Stream;

  22. import org.hipparchus.Field;
  23. import org.hipparchus.CalculusFieldElement;
  24. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  25. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  26. import org.hipparchus.ode.events.Action;
  27. import org.hipparchus.util.FastMath;
  28. import org.hipparchus.util.MathArrays;
  29. import org.orekit.errors.OrekitException;
  30. import org.orekit.errors.OrekitMessages;
  31. import org.orekit.forces.AbstractForceModel;
  32. import org.orekit.propagation.FieldSpacecraftState;
  33. import org.orekit.propagation.SpacecraftState;
  34. import org.orekit.propagation.events.AbstractDetector;
  35. import org.orekit.propagation.events.EventDetector;
  36. import org.orekit.propagation.events.FieldAbstractDetector;
  37. import org.orekit.propagation.events.FieldEventDetector;
  38. import org.orekit.propagation.events.handlers.EventHandler;
  39. import org.orekit.propagation.events.handlers.FieldEventHandler;
  40. import org.orekit.utils.Constants;
  41. import org.orekit.utils.ExtendedPVCoordinatesProvider;

  42. /**
  43.  * Base class for radiation force models.
  44.  * @see SolarRadiationPressure
  45.  * @see ECOM2
  46.  * @since 10.2
  47.  */
  48. public abstract class AbstractRadiationForceModel extends AbstractForceModel {

  49.     /** Margin to force recompute lighting ratio derivatives when we are really inside penumbra. */
  50.     private static final double ANGULAR_MARGIN = 1.0e-10;

  51.     /** Central body model. */
  52.     private final double equatorialRadius;

  53.     /** Sun model. */
  54.     private final ExtendedPVCoordinatesProvider sun;

  55.     /** Other occulting bodies to consider. The Moon for instance. */
  56.     private final Map<ExtendedPVCoordinatesProvider, Double> otherOccultingBodies;

  57.     /**
  58.      * Default constructor.
  59.      * Only central body is considered.
  60.      * @param sun Sun model
  61.      * @param equatorialRadius central body spherical shape model (for umbra/penumbra computation)
  62.      */
  63.     protected AbstractRadiationForceModel(final ExtendedPVCoordinatesProvider sun, final double equatorialRadius) {
  64.         this.sun                  = sun;
  65.         this.equatorialRadius     = equatorialRadius;
  66.         this.otherOccultingBodies = new HashMap<>();
  67.     }

  68.     /** {@inheritDoc} */
  69.     @Override
  70.     public boolean dependsOnPositionOnly() {
  71.         return false;
  72.     }

  73.     /** {@inheritDoc} */
  74.     @Override
  75.     public Stream<EventDetector> getEventsDetectors() {
  76.         final EventDetector[] detectors = new EventDetector[2 + 2 * otherOccultingBodies.size()];
  77.         detectors[0] = new UmbraDetector();
  78.         detectors[1] = new PenumbraDetector();
  79.         int i = 2;
  80.         for (Map.Entry<ExtendedPVCoordinatesProvider, Double> entry : otherOccultingBodies.entrySet()) {
  81.             detectors[i]     = new GeneralUmbraDetector(entry.getKey(),    entry.getValue());
  82.             detectors[i + 1] = new GeneralPenumbraDetector(entry.getKey(), entry.getValue());
  83.             i = i + 2;
  84.         }
  85.         return Stream.of(detectors);
  86.     }

  87.     /** {@inheritDoc} */
  88.     @Override
  89.     public <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventsDetectors(final Field<T> field) {
  90.         final T zero = field.getZero();
  91.         @SuppressWarnings("unchecked")
  92.         final FieldEventDetector<T>[] detectors = (FieldEventDetector<T>[]) Array.newInstance(FieldEventDetector.class,
  93.                                                                                               2 + 2 * otherOccultingBodies.size());
  94.         detectors[0] = new FieldUmbraDetector<>(field);
  95.         detectors[1] = new FieldPenumbraDetector<>(field);
  96.         int i = 2;
  97.         for (Map.Entry<ExtendedPVCoordinatesProvider, Double> entry : otherOccultingBodies.entrySet()) {
  98.             detectors[i]     = new FieldGeneralUmbraDetector<>(field, entry.getKey(),    zero.newInstance(entry.getValue()));
  99.             detectors[i + 1] = new FieldGeneralPenumbraDetector<>(field, entry.getKey(), zero.newInstance(entry.getValue()));
  100.             i = i + 2;
  101.         }
  102.         return Stream.of(detectors);
  103.     }

  104.     /**
  105.      * Get the useful angles for eclipse computation.
  106.      * @param sunPosition Sun position in the selected frame
  107.      * @param position the satellite's position in the selected frame
  108.      * @return the 3 angles {(satCentral, satSun), Central body apparent radius, Sun apparent radius}
  109.      */
  110.     protected double[] getEclipseAngles(final Vector3D sunPosition, final Vector3D position) {
  111.         final double[] angle = new double[3];

  112.         final Vector3D satSunVector = sunPosition.subtract(position);

  113.         // Sat-Sun / Sat-CentralBody angle
  114.         angle[0] = Vector3D.angle(satSunVector, position.negate());

  115.         // Central body apparent radius
  116.         final double r = position.getNorm();
  117.         if (r <= equatorialRadius) {
  118.             throw new OrekitException(OrekitMessages.TRAJECTORY_INSIDE_BRILLOUIN_SPHERE, r);
  119.         }
  120.         angle[1] = FastMath.asin(equatorialRadius / r);

  121.         // Sun apparent radius
  122.         angle[2] = FastMath.asin(Constants.SUN_RADIUS / satSunVector.getNorm());

  123.         return angle;
  124.     }


  125.     /**
  126.      * Get the useful angles for eclipse computation.
  127.      * @param position the satellite's position in the selected frame
  128.      * @param occultingPosition Oculting body position in the selected frame
  129.      * @param occultingRadius Occulting body mean radius
  130.      * @param occultedPosition Occulted body position in the selected frame
  131.      * @param occultedRadius Occulted body mean radius
  132.      * @return the 3 angles {(satOcculting, satOcculted), Occulting body apparent radius, Occulted body apparent radius}
  133.      */
  134.     protected double[] getGeneralEclipseAngles(final Vector3D position, final Vector3D occultingPosition, final double occultingRadius,
  135.                                                final Vector3D occultedPosition, final double occultedRadius) {
  136.         final double[] angle = new double[3];

  137.         final Vector3D satOccultedVector = occultedPosition.subtract(position);
  138.         final Vector3D satOccultingVector = occultingPosition.subtract(position);

  139.         // Sat-Occulted / Sat-Occulting angle
  140.         angle[0] = Vector3D.angle(satOccultedVector, satOccultingVector);

  141.         // Occulting body apparent radius
  142.         angle[1] = FastMath.asin(occultingRadius / satOccultingVector.getNorm());

  143.         // Occulted body apparent radius
  144.         angle[2] = FastMath.asin(occultedRadius / satOccultedVector.getNorm());

  145.         return angle;
  146.     }

  147.     /**
  148.      * Get the useful angles for eclipse computation.
  149.      * @param sunPosition Sun position in the selected frame
  150.      * @param position the satellite's position in the selected frame.
  151.      * @param <T> extends CalculusFieldElement
  152.      * @return the 3 angles {(satCentral, satSun), Central body apparent radius, Sun apparent radius}
  153.      */
  154.     protected <T extends CalculusFieldElement<T>> T[] getEclipseAngles(final FieldVector3D<T> sunPosition, final FieldVector3D<T> position) {
  155.         final T[] angle = MathArrays.buildArray(position.getX().getField(), 3);

  156.         final FieldVector3D<T> mP           = position.negate();
  157.         final FieldVector3D<T> satSunVector = mP.add(sunPosition);

  158.         // Sat-Sun / Sat-CentralBody angle
  159.         angle[0] = FieldVector3D.angle(satSunVector, mP);

  160.         // Central body apparent radius
  161.         final T r = position.getNorm();
  162.         if (r.getReal() <= equatorialRadius) {
  163.             throw new OrekitException(OrekitMessages.TRAJECTORY_INSIDE_BRILLOUIN_SPHERE, r);
  164.         }
  165.         angle[1] = r.reciprocal().multiply(equatorialRadius).asin();

  166.         // Sun apparent radius
  167.         angle[2] = satSunVector.getNorm().reciprocal().multiply(Constants.SUN_RADIUS).asin();

  168.         return angle;
  169.     }

  170.     /**
  171.      * Get the useful angles for eclipse computation.
  172.      * @param occultingPosition Oculting body position in the selected frame
  173.      * @param occultingRadius Occulting body mean radius
  174.      * @param occultedPosition Occulted body position in the selected frame
  175.      * @param occultedRadius Occulted body mean radius
  176.      * @param position the satellite's position in the selected frame
  177.      * @param <T> extends RealFieldElement
  178.      * @return the 3 angles {(satOcculting, satOcculted), Occulting body apparent radius, Occulted body apparent radius}
  179.      */
  180.     protected <T extends CalculusFieldElement<T>> T[] getGeneralEclipseAngles(final FieldVector3D<T> position,
  181.                                                                               final FieldVector3D<T> occultingPosition, final T occultingRadius,
  182.                                                                               final FieldVector3D<T> occultedPosition, final T occultedRadius) {
  183.         final T[] angle = MathArrays.buildArray(position.getX().getField(), 3);

  184.         final FieldVector3D<T> satOccultedVector = occultedPosition.subtract(position);
  185.         final FieldVector3D<T> satOccultingVector = occultingPosition.subtract(position);

  186.         // Sat-Occulted / Sat-Occulting angle
  187.         angle[0] = FieldVector3D.angle(satOccultedVector, satOccultingVector);

  188.         // Occulting body apparent radius
  189.         angle[1] = occultingRadius.divide(satOccultingVector.getNorm()).asin();

  190.         // Occulted body apparent radius
  191.         angle[2] = occultedRadius.divide(satOccultedVector.getNorm()).asin();

  192.         return angle;
  193.     }

  194.     /**
  195.      * Add a new occulting body.
  196.      * Central body is already considered, it shall not be added this way.
  197.      * @param provider body PV provider
  198.      * @param radius body mean radius
  199.      */
  200.     public void addOccultingBody(final ExtendedPVCoordinatesProvider provider, final double radius) {
  201.         otherOccultingBodies.put(provider, radius);
  202.     }

  203.     /**
  204.      * Getter for other occulting bodies to consider.
  205.      * @return the map of other occulting bodies and corresponding mean radiuses
  206.      */
  207.     public Map<ExtendedPVCoordinatesProvider, Double> getOtherOccultingBodies() {
  208.         return otherOccultingBodies;
  209.     }

  210.     /**
  211.      * Getter for equatorial radius.
  212.      * @return central body equatorial radius
  213.      */
  214.     public double getEquatorialRadius() {
  215.         return equatorialRadius;
  216.     }


  217.     /** This class defines the umbra entry/exit detector. */
  218.     private class UmbraDetector extends AbstractDetector<UmbraDetector> {

  219.         /** Build a new instance. */
  220.         UmbraDetector() {
  221.             super(60.0, 1.0e-3, DEFAULT_MAX_ITER, new EventHandler<UmbraDetector>() {

  222.                 /** {@inheritDoc} */
  223.                 public Action eventOccurred(final SpacecraftState s, final UmbraDetector detector,
  224.                                             final boolean increasing) {
  225.                     return Action.RESET_DERIVATIVES;
  226.                 }

  227.             });
  228.         }

  229.         /** Private constructor with full parameters.
  230.          * <p>
  231.          * This constructor is private as users are expected to use the builder
  232.          * API with the various {@code withXxx()} methods to set up the instance
  233.          * in a readable manner without using a huge amount of parameters.
  234.          * </p>
  235.          * @param maxCheck maximum checking interval (s)
  236.          * @param threshold convergence threshold (s)
  237.          * @param maxIter maximum number of iterations in the event time search
  238.          * @param handler event handler to call at event occurrences
  239.          * @since 6.1
  240.          */
  241.         private UmbraDetector(final double maxCheck, final double threshold, final int maxIter,
  242.                               final EventHandler<? super UmbraDetector> handler) {
  243.             super(maxCheck, threshold, maxIter, handler);
  244.         }

  245.         /** {@inheritDoc} */
  246.         @Override
  247.         protected UmbraDetector create(final double newMaxCheck, final double newThreshold, final int newMaxIter,
  248.                                        final EventHandler<? super UmbraDetector> newHandler) {
  249.             return new UmbraDetector(newMaxCheck, newThreshold, newMaxIter, newHandler);
  250.         }

  251.         /** The G-function is the difference between the Sun-Sat-Central-Body angle and
  252.          * the central body apparent radius.
  253.          * @param s the current state information : date, kinematics, attitude
  254.          * @return value of the g function
  255.          */
  256.         public double g(final SpacecraftState s) {
  257.             final double[] angle = getEclipseAngles(sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(),
  258.                                                     s.getPVCoordinates().getPosition());
  259.             return angle[0] - angle[1] + angle[2] - ANGULAR_MARGIN;
  260.         }

  261.     }

  262.     /** This class defines the penumbra entry/exit detector. */
  263.     private class PenumbraDetector extends AbstractDetector<PenumbraDetector> {

  264.         /** Build a new instance. */
  265.         PenumbraDetector() {
  266.             super(60.0, 1.0e-3, DEFAULT_MAX_ITER, new EventHandler<PenumbraDetector>() {

  267.                 /** {@inheritDoc} */
  268.                 public Action eventOccurred(final SpacecraftState s, final PenumbraDetector detector,
  269.                                             final boolean increasing) {
  270.                     return Action.RESET_DERIVATIVES;
  271.                 }

  272.             });
  273.         }

  274.         /** Private constructor with full parameters.
  275.          * <p>
  276.          * This constructor is private as users are expected to use the builder
  277.          * API with the various {@code withXxx()} methods to set up the instance
  278.          * in a readable manner without using a huge amount of parameters.
  279.          * </p>
  280.          * @param maxCheck maximum checking interval (s)
  281.          * @param threshold convergence threshold (s)
  282.          * @param maxIter maximum number of iterations in the event time search
  283.          * @param handler event handler to call at event occurrences
  284.          * @since 6.1
  285.          */
  286.         private PenumbraDetector(final double maxCheck, final double threshold, final int maxIter,
  287.                                  final EventHandler<? super PenumbraDetector> handler) {
  288.             super(maxCheck, threshold, maxIter, handler);
  289.         }

  290.         /** {@inheritDoc} */
  291.         @Override
  292.         protected PenumbraDetector create(final double newMaxCheck, final double newThreshold, final int newMaxIter,
  293.                                           final EventHandler<? super PenumbraDetector> newHandler) {
  294.             return new PenumbraDetector(newMaxCheck, newThreshold, newMaxIter, newHandler);
  295.         }

  296.         /** The G-function is the difference between the Sun-Sat-Central-Body angle and
  297.          * the sum of the central body and Sun's apparent radius.
  298.          * @param s the current state information : date, kinematics, attitude
  299.          * @return value of the g function
  300.          */
  301.         public double g(final SpacecraftState s) {
  302.             final double[] angle = getEclipseAngles(sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(),
  303.                                                     s.getPVCoordinates().getPosition());
  304.             return angle[0] - angle[1] - angle[2] + ANGULAR_MARGIN;
  305.         }

  306.     }

  307.     /** This class defines the umbra entry/exit detector. */
  308.     private class FieldUmbraDetector<T extends CalculusFieldElement<T>>
  309.         extends FieldAbstractDetector<FieldUmbraDetector<T>, T> {

  310.         /** Build a new instance.
  311.          * @param field field to which elements belong
  312.          */
  313.         FieldUmbraDetector(final Field<T> field) {
  314.             super(field.getZero().add(60.0), field.getZero().add(1.0e-3),
  315.                   DEFAULT_MAX_ITER, new FieldEventHandler<FieldUmbraDetector<T>, T>() {

  316.                       /** {@inheritDoc} */
  317.                       public Action eventOccurred(final FieldSpacecraftState<T> s,
  318.                                                   final FieldUmbraDetector<T> detector,
  319.                                                   final boolean increasing) {
  320.                           return Action.RESET_DERIVATIVES;
  321.                       }

  322.                   });
  323.         }

  324.         /** Private constructor with full parameters.
  325.          * <p>
  326.          * This constructor is private as users are expected to use the builder
  327.          * API with the various {@code withXxx()} methods to set up the instance
  328.          * in a readable manner without using a huge amount of parameters.
  329.          * </p>
  330.          * @param maxCheck maximum checking interval (s)
  331.          * @param threshold convergence threshold (s)
  332.          * @param maxIter maximum number of iterations in the event time search
  333.          * @param handler event handler to call at event occurrences
  334.          */
  335.         private FieldUmbraDetector(final T maxCheck, final T threshold, final int maxIter,
  336.                                    final FieldEventHandler<? super FieldUmbraDetector<T>, T> handler) {
  337.             super(maxCheck, threshold, maxIter, handler);
  338.         }

  339.         /** {@inheritDoc} */
  340.         @Override
  341.         protected FieldUmbraDetector<T> create(final T newMaxCheck, final T newThreshold, final int newMaxIter,
  342.                                                final FieldEventHandler<? super FieldUmbraDetector<T>, T> newHandler) {
  343.             return new FieldUmbraDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler);
  344.         }

  345.         /** The G-function is the difference between the Sun-Sat-Central-Body angle and
  346.          * the central body apparent radius.
  347.          * @param s the current state information : date, kinematics, attitude
  348.          * @return value of the g function
  349.          */
  350.         public T g(final FieldSpacecraftState<T> s) {
  351.             final T[] angle = getEclipseAngles(sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(),
  352.                                                s.getPVCoordinates().getPosition());
  353.             return angle[0].subtract(angle[1]).add(angle[2]).subtract(ANGULAR_MARGIN);
  354.         }

  355.     }

  356.     /** This class defines the penumbra entry/exit detector. */
  357.     private class FieldPenumbraDetector<T extends CalculusFieldElement<T>>
  358.           extends FieldAbstractDetector<FieldPenumbraDetector<T>, T> {

  359.         /** Build a new instance.
  360.          * @param field field to which elements belong
  361.          */
  362.         FieldPenumbraDetector(final Field<T> field) {
  363.             super(field.getZero().add(60.0), field.getZero().add(1.0e-3),
  364.                   DEFAULT_MAX_ITER, new FieldEventHandler<FieldPenumbraDetector<T>, T>() {

  365.                       /** {@inheritDoc} */
  366.                       public Action eventOccurred(final FieldSpacecraftState<T> s,
  367.                                                   final FieldPenumbraDetector<T> detector,
  368.                                                   final boolean increasing) {
  369.                           return Action.RESET_DERIVATIVES;
  370.                       }

  371.                   });
  372.         }

  373.         /** Private constructor with full parameters.
  374.          * <p>
  375.          * This constructor is private as users are expected to use the builder
  376.          * API with the various {@code withXxx()} methods to set up the instance
  377.          * in a readable manner without using a huge amount of parameters.
  378.          * </p>
  379.          * @param maxCheck maximum checking interval (s)
  380.          * @param threshold convergence threshold (s)
  381.          * @param maxIter maximum number of iterations in the event time search
  382.          * @param handler event handler to call at event occurrences
  383.          */
  384.         private FieldPenumbraDetector(final T maxCheck, final T threshold, final int maxIter,
  385.                                       final FieldEventHandler<? super FieldPenumbraDetector<T>, T> handler) {
  386.             super(maxCheck, threshold, maxIter, handler);
  387.         }

  388.         /** {@inheritDoc} */
  389.         @Override
  390.         protected FieldPenumbraDetector<T> create(final T newMaxCheck, final T newThreshold, final int newMaxIter,
  391.                                                   final FieldEventHandler<? super FieldPenumbraDetector<T>, T> newHandler) {
  392.             return new FieldPenumbraDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler);
  393.         }

  394.         /** The G-function is the difference between the Sun-Sat-Central-Body angle and
  395.          * the sum of the central body and Sun's apparent radius.
  396.          * @param s the current state information : date, kinematics, attitude
  397.          * @return value of the g function
  398.          */
  399.         public T g(final FieldSpacecraftState<T> s) {
  400.             final T[] angle = getEclipseAngles(sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(),
  401.                                                s.getPVCoordinates().getPosition());
  402.             return angle[0].subtract(angle[1]).subtract(angle[2]).add(ANGULAR_MARGIN);
  403.         }

  404.     }

  405.     /** This class defines the umbra entry/exit detector. */
  406.     private class GeneralUmbraDetector extends AbstractDetector<GeneralUmbraDetector> {

  407.         /** Occulting body PV provider. */
  408.         private ExtendedPVCoordinatesProvider provider;

  409.         /** Occulting body mean radius. */
  410.         private double radius;

  411.         /** Build a new instance.
  412.          * @param provider occulting body PV provider
  413.          * @param radius occulting body mean radius
  414.          */
  415.         GeneralUmbraDetector(final ExtendedPVCoordinatesProvider provider, final double radius) {
  416.             super(60.0, 1.0e-3, DEFAULT_MAX_ITER, new EventHandler<GeneralUmbraDetector>() {

  417.                 /** {@inheritDoc} */
  418.                 public Action eventOccurred(final SpacecraftState s, final GeneralUmbraDetector detector,
  419.                                             final boolean increasing) {
  420.                     return Action.RESET_DERIVATIVES;
  421.                 }

  422.             });
  423.             this.provider = provider;
  424.             this.radius   = radius;
  425.         }

  426.         /** Private constructor with full parameters.
  427.          * <p>
  428.          * This constructor is private as users are expected to use the builder
  429.          * API with the various {@code withXxx()} methods to set up the instance
  430.          * in a readable manner without using a huge amount of parameters.
  431.          * </p>
  432.          * @param maxCheck maximum checking interval (s)
  433.          * @param threshold convergence threshold (s)
  434.          * @param maxIter maximum number of iterations in the event time search
  435.          * @param handler event handler to call at event occurrences
  436.          * @since 6.1
  437.          */
  438.         private GeneralUmbraDetector(final double maxCheck, final double threshold, final int maxIter,
  439.                                      final EventHandler<? super GeneralUmbraDetector> handler) {
  440.             super(maxCheck, threshold, maxIter, handler);
  441.         }

  442.         /** {@inheritDoc} */
  443.         @Override
  444.         protected GeneralUmbraDetector create(final double newMaxCheck, final double newThreshold, final int newMaxIter,
  445.                                               final EventHandler<? super GeneralUmbraDetector> newHandler) {
  446.             return new GeneralUmbraDetector(newMaxCheck, newThreshold, newMaxIter, newHandler);
  447.         }

  448.         /** The G-function is the difference between the Sun-Sat-Central-Body angle and
  449.          * the central body apparent radius.
  450.          * @param s the current state information : date, kinematics, attitude
  451.          * @return value of the g function
  452.          */
  453.         public double g(final SpacecraftState s) {
  454.             final double[] angle = getGeneralEclipseAngles(s.getPVCoordinates().getPosition(),
  455.                                                            provider.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(),
  456.                                                            radius, sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(),
  457.                                                            Constants.SUN_RADIUS);
  458.             return angle[0] - angle[1] + angle[2] - ANGULAR_MARGIN;
  459.         }

  460.     }

  461.     /** This class defines the umbra entry/exit detector. */
  462.     private class GeneralPenumbraDetector extends AbstractDetector<GeneralPenumbraDetector> {

  463.         /** Occulting body PV provider. */
  464.         private ExtendedPVCoordinatesProvider provider;

  465.         /** Occulting body mean radius. */
  466.         private double radius;

  467.         /** Build a new instance.
  468.          * @param provider occulting body PV provider
  469.          * @param radius occulting body mean radius
  470.          */
  471.         GeneralPenumbraDetector(final ExtendedPVCoordinatesProvider provider, final double radius) {
  472.             super(60.0, 1.0e-3, DEFAULT_MAX_ITER, new EventHandler<GeneralPenumbraDetector>() {

  473.                 /** {@inheritDoc} */
  474.                 public Action eventOccurred(final SpacecraftState s, final GeneralPenumbraDetector detector,
  475.                                             final boolean increasing) {
  476.                     return Action.RESET_DERIVATIVES;
  477.                 }

  478.             });
  479.             this.provider = provider;
  480.             this.radius   = radius;
  481.         }

  482.         /** Private constructor with full parameters.
  483.          * <p>
  484.          * This constructor is private as users are expected to use the builder
  485.          * API with the various {@code withXxx()} methods to set up the instance
  486.          * in a readable manner without using a huge amount of parameters.
  487.          * </p>
  488.          * @param maxCheck maximum checking interval (s)
  489.          * @param threshold convergence threshold (s)
  490.          * @param maxIter maximum number of iterations in the event time search
  491.          * @param handler event handler to call at event occurrences
  492.          * @since 6.1
  493.          */
  494.         private GeneralPenumbraDetector(final double maxCheck, final double threshold, final int maxIter,
  495.                                         final EventHandler<? super GeneralPenumbraDetector> handler) {
  496.             super(maxCheck, threshold, maxIter, handler);
  497.         }

  498.         /** {@inheritDoc} */
  499.         @Override
  500.         protected GeneralPenumbraDetector create(final double newMaxCheck, final double newThreshold, final int newMaxIter,
  501.                                                  final EventHandler<? super GeneralPenumbraDetector> newHandler) {
  502.             return new GeneralPenumbraDetector(newMaxCheck, newThreshold, newMaxIter, newHandler);
  503.         }

  504.         /** The G-function is the difference between the Sun-Sat-Central-Body angle and
  505.          * the central body apparent radius.
  506.          * @param s the current state information : date, kinematics, attitude
  507.          * @return value of the g function
  508.          */
  509.         public double g(final SpacecraftState s) {
  510.             final double[] angle = getGeneralEclipseAngles(s.getPVCoordinates().getPosition(),
  511.                                                            provider.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(),
  512.                                                            radius, sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(),
  513.                                                            Constants.SUN_RADIUS);
  514.             return angle[0] - angle[1] - angle[2] + ANGULAR_MARGIN;
  515.         }

  516.     }

  517.     /** This class defines the umbra entry/exit detector. */
  518.     private class FieldGeneralUmbraDetector<T extends CalculusFieldElement<T>>
  519.         extends FieldAbstractDetector<FieldGeneralUmbraDetector<T>, T> {

  520.         /** Occulting body PV provider. */
  521.         private ExtendedPVCoordinatesProvider provider;

  522.         /** Occulting body mean radius. */
  523.         private T radius;

  524.         /** Build a new instance.
  525.          * @param field field to which elements belong
  526.          * @param provider occulting body PV provider
  527.          * @param radius occulting body mean radius
  528.          */
  529.         FieldGeneralUmbraDetector(final Field<T> field, final ExtendedPVCoordinatesProvider provider, final T radius) {
  530.             super(field.getZero().add(60.0), field.getZero().add(1.0e-3),
  531.                   DEFAULT_MAX_ITER, new FieldEventHandler<FieldGeneralUmbraDetector<T>, T>() {

  532.                       /** {@inheritDoc} */
  533.                       public Action eventOccurred(final FieldSpacecraftState<T> s,
  534.                                                   final FieldGeneralUmbraDetector<T> detector,
  535.                                                   final boolean increasing) {
  536.                           return Action.RESET_DERIVATIVES;
  537.                       }

  538.                   });
  539.             this.provider = provider;
  540.             this.radius   = radius;
  541.         }

  542.         /** Private constructor with full parameters.
  543.          * <p>
  544.          * This constructor is private as users are expected to use the builder
  545.          * API with the various {@code withXxx()} methods to set up the instance
  546.          * in a readable manner without using a huge amount of parameters.
  547.          * </p>
  548.          * @param maxCheck maximum checking interval (s)
  549.          * @param threshold convergence threshold (s)
  550.          * @param maxIter maximum number of iterations in the event time search
  551.          * @param handler event handler to call at event occurrences
  552.          */
  553.         private FieldGeneralUmbraDetector(final T maxCheck, final T threshold,
  554.                                    final int maxIter,
  555.                                    final FieldEventHandler<? super FieldGeneralUmbraDetector<T>, T> handler) {
  556.             super(maxCheck, threshold, maxIter, handler);
  557.         }

  558.         /** {@inheritDoc} */
  559.         @Override
  560.         protected FieldGeneralUmbraDetector<T> create(final T newMaxCheck, final T newThreshold, final int newMaxIter,
  561.                                                       final FieldEventHandler<? super FieldGeneralUmbraDetector<T>, T> newHandler) {
  562.             return new FieldGeneralUmbraDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler);
  563.         }

  564.         /** The G-function is the difference between the Sun-Sat-Central-Body angle and
  565.          * the central body apparent radius.
  566.          * @param s the current state information : date, kinematics, attitude
  567.          * @return value of the g function
  568.          */
  569.         public T g(final FieldSpacecraftState<T> s) {
  570.             final T[] angle = getGeneralEclipseAngles(s.getPVCoordinates().getPosition(),
  571.                                                       provider.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(),
  572.                                                       radius, sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(),
  573.                                                       s.getA().getField().getZero().add(Constants.SUN_RADIUS));
  574.             return angle[0].subtract(angle[1]).add(angle[2]).subtract(ANGULAR_MARGIN);
  575.         }

  576.     }

  577.     /** This class defines the umbra entry/exit detector. */
  578.     private class FieldGeneralPenumbraDetector<T extends CalculusFieldElement<T>>
  579.         extends FieldAbstractDetector<FieldGeneralPenumbraDetector<T>, T> {

  580.         /** Occulting body PV provider. */
  581.         private ExtendedPVCoordinatesProvider provider;

  582.         /** Occulting body mean radius. */
  583.         private T radius;

  584.         /** Build a new instance.
  585.          * @param field field to which elements belong
  586.          * @param provider occulting body PV provider
  587.          * @param radius occulting body mean radius
  588.          */
  589.         FieldGeneralPenumbraDetector(final Field<T> field, final ExtendedPVCoordinatesProvider provider, final T radius) {
  590.             super(field.getZero().add(60.0), field.getZero().add(1.0e-3),
  591.                   DEFAULT_MAX_ITER, new FieldEventHandler<FieldGeneralPenumbraDetector<T>, T>() {

  592.                       /** {@inheritDoc} */
  593.                       public Action eventOccurred(final FieldSpacecraftState<T> s,
  594.                                                   final FieldGeneralPenumbraDetector<T> detector,
  595.                                                   final boolean increasing) {
  596.                           return Action.RESET_DERIVATIVES;
  597.                       }

  598.                   });
  599.             this.provider = provider;
  600.             this.radius   = radius;
  601.         }

  602.         /** Private constructor with full parameters.
  603.          * <p>
  604.          * This constructor is private as users are expected to use the builder
  605.          * API with the various {@code withXxx()} methods to set up the instance
  606.          * in a readable manner without using a huge amount of parameters.
  607.          * </p>
  608.          * @param maxCheck maximum checking interval (s)
  609.          * @param threshold convergence threshold (s)
  610.          * @param maxIter maximum number of iterations in the event time search
  611.          * @param handler event handler to call at event occurrences
  612.          */
  613.         private FieldGeneralPenumbraDetector(final T maxCheck, final T threshold, final int maxIter,
  614.                                              final FieldEventHandler<? super FieldGeneralPenumbraDetector<T>, T> handler) {
  615.             super(maxCheck, threshold, maxIter, handler);
  616.         }

  617.         /** {@inheritDoc} */
  618.         @Override
  619.         protected FieldGeneralPenumbraDetector<T> create(final T newMaxCheck, final T newThreshold, final int newMaxIter,
  620.                                                          final FieldEventHandler<? super FieldGeneralPenumbraDetector<T>, T> newHandler) {
  621.             return new FieldGeneralPenumbraDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler);
  622.         }

  623.         /** The G-function is the difference between the Sun-Sat-Central-Body angle and
  624.          * the central body apparent radius.
  625.          * @param s the current state information : date, kinematics, attitude
  626.          * @return value of the g function
  627.          */
  628.         public T g(final FieldSpacecraftState<T> s) {
  629.             final T[] angle = getGeneralEclipseAngles(s.getPVCoordinates().getPosition(),
  630.                                                       provider.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(),
  631.                                                       radius, sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(),
  632.                                                       s.getA().getField().getZero().add(Constants.SUN_RADIUS));
  633.             return angle[0].subtract(angle[1]).subtract(angle[2]).add(ANGULAR_MARGIN);
  634.         }

  635.     }
  636. }