AbstractRadiationForceModel.java

  1. /* Copyright 2002-2020 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.util.stream.Stream;

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

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

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

  48.     /** Central body model. */
  49.     private final double equatorialRadius;

  50.     /** Sun model. */
  51.     private final ExtendedPVCoordinatesProvider sun;

  52.     /**
  53.      * Constructor.
  54.      * @param sun Sun model
  55.      * @param equatorialRadius spherical shape model (for umbra/penumbra computation)
  56.      */
  57.     protected AbstractRadiationForceModel(final ExtendedPVCoordinatesProvider sun, final double equatorialRadius) {
  58.         this.sun              = sun;
  59.         this.equatorialRadius = equatorialRadius;
  60.     }

  61.     /** {@inheritDoc} */
  62.     @Override
  63.     public boolean dependsOnPositionOnly() {
  64.         return false;
  65.     }

  66.     /** {@inheritDoc} */
  67.     @Override
  68.     public Stream<EventDetector> getEventsDetectors() {
  69.         return Stream.of(new UmbraDetector(), new PenumbraDetector());
  70.     }

  71.     /** {@inheritDoc} */
  72.     @Override
  73.     public <T extends RealFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventsDetectors(final Field<T> field) {
  74.         return Stream.of(new FieldUmbraDetector<>(field), new FieldPenumbraDetector<>(field));
  75.     }

  76.     /**
  77.      * Get the useful angles for eclipse computation.
  78.      * @param sunPosition Sun position in the selected frame
  79.      * @param position the satellite's position in the selected frame
  80.      * @return the 3 angles {(satCentral, satSun), Central body apparent radius, Sun apparent radius}
  81.      */
  82.     protected double[] getEclipseAngles(final Vector3D sunPosition, final Vector3D position) {
  83.         final double[] angle = new double[3];

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

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

  87.         // Central body apparent radius
  88.         final double r = position.getNorm();
  89.         if (r <= equatorialRadius) {
  90.             throw new OrekitException(OrekitMessages.TRAJECTORY_INSIDE_BRILLOUIN_SPHERE, r);
  91.         }
  92.         angle[1] = FastMath.asin(equatorialRadius / r);

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

  95.         return angle;
  96.     }

  97.     /**
  98.      * Get the useful angles for eclipse computation.
  99.      * @param sunPosition Sun position in the selected frame
  100.      * @param position the satellite's position in the selected frame.
  101.      * @param <T> extends RealFieldElement
  102.      * @return the 3 angles {(satCentral, satSun), Central body apparent radius, Sun apparent radius}
  103.      */
  104.     protected <T extends RealFieldElement<T>> T[] getEclipseAngles(final FieldVector3D<T> sunPosition, final FieldVector3D<T> position) {
  105.         final T[] angle = MathArrays.buildArray(position.getX().getField(), 3);

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

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

  110.         // Central body apparent radius
  111.         final T r = position.getNorm();
  112.         if (r.getReal() <= equatorialRadius) {
  113.             throw new OrekitException(OrekitMessages.TRAJECTORY_INSIDE_BRILLOUIN_SPHERE, r);
  114.         }
  115.         angle[1] = r.reciprocal().multiply(equatorialRadius).asin();

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

  118.         return angle;
  119.     }


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

  122.         /** Build a new instance. */
  123.         UmbraDetector() {
  124.             super(60.0, 1.0e-3, DEFAULT_MAX_ITER, new EventHandler<UmbraDetector>() {

  125.                 /** {@inheritDoc} */
  126.                 public Action eventOccurred(final SpacecraftState s, final UmbraDetector detector,
  127.                                             final boolean increasing) {
  128.                     return Action.RESET_DERIVATIVES;
  129.                 }

  130.             });
  131.         }

  132.         /** Private constructor with full parameters.
  133.          * <p>
  134.          * This constructor is private as users are expected to use the builder
  135.          * API with the various {@code withXxx()} methods to set up the instance
  136.          * in a readable manner without using a huge amount of parameters.
  137.          * </p>
  138.          * @param maxCheck maximum checking interval (s)
  139.          * @param threshold convergence threshold (s)
  140.          * @param maxIter maximum number of iterations in the event time search
  141.          * @param handler event handler to call at event occurrences
  142.          * @since 6.1
  143.          */
  144.         private UmbraDetector(final double maxCheck, final double threshold,
  145.                               final int maxIter, final EventHandler<? super UmbraDetector> handler) {
  146.             super(maxCheck, threshold, maxIter, handler);
  147.         }

  148.         /** {@inheritDoc} */
  149.         @Override
  150.         protected UmbraDetector create(final double newMaxCheck, final double newThreshold,
  151.                                        final int newMaxIter, final EventHandler<? super UmbraDetector> newHandler) {
  152.             return new UmbraDetector(newMaxCheck, newThreshold, newMaxIter, newHandler);
  153.         }

  154.         /** The G-function is the difference between the Sun-Sat-Central-Body angle and
  155.          * the central body apparent radius.
  156.          * @param s the current state information : date, kinematics, attitude
  157.          * @return value of the g function
  158.          */
  159.         public double g(final SpacecraftState s) {
  160.             final double[] angle = getEclipseAngles(sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(),
  161.                                                     s.getPVCoordinates().getPosition());
  162.             return angle[0] - angle[1] + angle[2] - ANGULAR_MARGIN;
  163.         }

  164.     }

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

  167.         /** Build a new instance. */
  168.         PenumbraDetector() {
  169.             super(60.0, 1.0e-3, DEFAULT_MAX_ITER, new EventHandler<PenumbraDetector>() {

  170.                 /** {@inheritDoc} */
  171.                 public Action eventOccurred(final SpacecraftState s, final PenumbraDetector detector,
  172.                                             final boolean increasing) {
  173.                     return Action.RESET_DERIVATIVES;
  174.                 }

  175.             });
  176.         }

  177.         /** Private constructor with full parameters.
  178.          * <p>
  179.          * This constructor is private as users are expected to use the builder
  180.          * API with the various {@code withXxx()} methods to set up the instance
  181.          * in a readable manner without using a huge amount of parameters.
  182.          * </p>
  183.          * @param maxCheck maximum checking interval (s)
  184.          * @param threshold convergence threshold (s)
  185.          * @param maxIter maximum number of iterations in the event time search
  186.          * @param handler event handler to call at event occurrences
  187.          * @since 6.1
  188.          */
  189.         private PenumbraDetector(final double maxCheck, final double threshold,
  190.                                  final int maxIter, final EventHandler<? super PenumbraDetector> handler) {
  191.             super(maxCheck, threshold, maxIter, handler);
  192.         }

  193.         /** {@inheritDoc} */
  194.         @Override
  195.         protected PenumbraDetector create(final double newMaxCheck, final double newThreshold,
  196.                                           final int newMaxIter, final EventHandler<? super PenumbraDetector> newHandler) {
  197.             return new PenumbraDetector(newMaxCheck, newThreshold, newMaxIter, newHandler);
  198.         }

  199.         /** The G-function is the difference between the Sun-Sat-Central-Body angle and
  200.          * the sum of the central body and Sun's apparent radius.
  201.          * @param s the current state information : date, kinematics, attitude
  202.          * @return value of the g function
  203.          */
  204.         public double g(final SpacecraftState s) {
  205.             final double[] angle = getEclipseAngles(sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(),
  206.                                                     s.getPVCoordinates().getPosition());
  207.             return angle[0] - angle[1] - angle[2] + ANGULAR_MARGIN;
  208.         }

  209.     }

  210.     /** This class defines the umbra entry/exit detector. */
  211.     private class FieldUmbraDetector<T extends RealFieldElement<T>>
  212.         extends FieldAbstractDetector<FieldUmbraDetector<T>, T> {

  213.         /** Build a new instance.
  214.          * @param field field to which elements belong
  215.          */
  216.         FieldUmbraDetector(final Field<T> field) {
  217.             super(field.getZero().add(60.0), field.getZero().add(1.0e-3),
  218.                   DEFAULT_MAX_ITER, new FieldEventHandler<FieldUmbraDetector<T>, T>() {

  219.                       /** {@inheritDoc} */
  220.                       public Action eventOccurred(final FieldSpacecraftState<T> s,
  221.                                                   final FieldUmbraDetector<T> detector,
  222.                                                   final boolean increasing) {
  223.                           return Action.RESET_DERIVATIVES;
  224.                       }

  225.                   });
  226.         }

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

  243.         /** {@inheritDoc} */
  244.         @Override
  245.         protected FieldUmbraDetector<T> create(final T newMaxCheck, final T newThreshold,
  246.                                                final int newMaxIter,
  247.                                                final FieldEventHandler<? super FieldUmbraDetector<T>, T> newHandler) {
  248.             return new FieldUmbraDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler);
  249.         }

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

  260.     }

  261.     /** This class defines the penumbra entry/exit detector. */
  262.     private class FieldPenumbraDetector<T extends RealFieldElement<T>>
  263.           extends FieldAbstractDetector<FieldPenumbraDetector<T>, T> {

  264.         /** Build a new instance.
  265.          * @param field field to which elements belong
  266.          */
  267.         FieldPenumbraDetector(final Field<T> field) {
  268.             super(field.getZero().add(60.0), field.getZero().add(1.0e-3),
  269.                   DEFAULT_MAX_ITER, new FieldEventHandler<FieldPenumbraDetector<T>, T>() {

  270.                       /** {@inheritDoc} */
  271.                       public Action eventOccurred(final FieldSpacecraftState<T> s,
  272.                                                   final FieldPenumbraDetector<T> detector,
  273.                                                   final boolean increasing) {
  274.                           return Action.RESET_DERIVATIVES;
  275.                       }

  276.                   });
  277.         }

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

  294.         /** {@inheritDoc} */
  295.         @Override
  296.         protected FieldPenumbraDetector<T> create(final T newMaxCheck, final T newThreshold,
  297.                                                   final int newMaxIter,
  298.                                                   final FieldEventHandler<? super FieldPenumbraDetector<T>, T> newHandler) {
  299.             return new FieldPenumbraDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler);
  300.         }

  301.         /** The G-function is the difference between the Sun-Sat-Central-Body angle and
  302.          * the sum of the central body and Sun's apparent radius.
  303.          * @param s the current state information : date, kinematics, attitude
  304.          * @return value of the g function
  305.          */
  306.         public T g(final FieldSpacecraftState<T> s) {
  307.             final T[] angle = getEclipseAngles(sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(),
  308.                                                s.getPVCoordinates().getPosition());
  309.             return angle[0].subtract(angle[1]).subtract(angle[2]).add(ANGULAR_MARGIN);
  310.         }

  311.     }

  312. }