BoxAndSolarArraySpacecraft.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;

  18. import java.util.ArrayList;
  19. import java.util.List;

  20. import org.hipparchus.Field;
  21. import org.hipparchus.RealFieldElement;
  22. import org.hipparchus.analysis.differentiation.DerivativeStructure;
  23. import org.hipparchus.geometry.euclidean.threed.FieldRotation;
  24. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  25. import org.hipparchus.geometry.euclidean.threed.Rotation;
  26. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  27. import org.hipparchus.util.FastMath;
  28. import org.hipparchus.util.Precision;
  29. import org.hipparchus.util.SinCos;
  30. import org.orekit.errors.OrekitException;
  31. import org.orekit.errors.OrekitInternalError;
  32. import org.orekit.forces.drag.DragSensitive;
  33. import org.orekit.forces.radiation.RadiationSensitive;
  34. import org.orekit.frames.Frame;
  35. import org.orekit.time.AbsoluteDate;
  36. import org.orekit.time.FieldAbsoluteDate;
  37. import org.orekit.utils.PVCoordinatesProvider;
  38. import org.orekit.utils.ParameterDriver;

  39. /** Class representing the features of a classical satellite
  40.  * with a convex body shape and rotating flat solar arrays.
  41.  * <p>
  42.  * The body can be either a simple parallelepipedic box aligned with
  43.  * spacecraft axes or a set of facets defined by their area and normal vector.
  44.  * This should handle accurately most spacecraft shapes.
  45.  * </p>
  46.  * <p>
  47.  * The solar array rotation with respect to satellite body can be either
  48.  * the best lighting orientation (i.e. Sun exactly in solar array meridian
  49.  * plane defined by solar array rotation axis and solar array normal vector)
  50.  * or a rotation evolving linearly according to a start position and an
  51.  * angular rate (which can be set to 0 for non-rotating panels, which may
  52.  * occur in special modes or during contingencies).
  53.  * </p>
  54.  * <p>
  55.  * The lift component of the drag force can be optionally considered. It should
  56.  * probably only be used for reentry computation, with much denser atmosphere
  57.  * than in regular orbit propagation. The lift component is computed using a
  58.  * ratio of molecules that experience specular reflection instead of diffuse
  59.  * reflection (absorption followed by outgassing at negligible velocity).
  60.  * Without lift (i.e. when the lift ratio is set to 0), drag force is along
  61.  * atmosphere relative velocity. With lift (i.e. when the lift ratio is set to any
  62.  * value between 0 and 1), the drag force depends on both relative velocity direction
  63.  * and facets normal orientation. For a single panel, if the relative velocity is
  64.  * head-on (i.e. aligned with the panel normal), the force will be in the same
  65.  * direction with and without lift, but the magnitude with lift ratio set to 1.0 will
  66.  * be twice the magnitude with lift ratio set to 0.0 (because atmosphere molecules
  67.  * bounces backward at same velocity in case of specular reflection).
  68.  * </p>
  69.  * <p>
  70.  * This model does not take cast shadow between body and solar array into account.
  71.  * </p>
  72.  *
  73.  * @author Luc Maisonobe
  74.  * @author Pascal Parraud
  75.  */
  76. public class BoxAndSolarArraySpacecraft implements RadiationSensitive, DragSensitive {

  77.     /** Parameters scaling factor.
  78.      * <p>
  79.      * We use a power of 2 to avoid numeric noise introduction
  80.      * in the multiplications/divisions sequences.
  81.      * </p>
  82.      */
  83.     private final double SCALE = FastMath.scalb(1.0, -3);

  84.     /** Driver for drag coefficient parameter. */
  85.     private final ParameterDriver dragParameterDriver;

  86.     /** Driver for lift ratio parameter (may be null is lift is ignored). */
  87.     private final ParameterDriver liftParameterDriver;

  88.     /** Driver for radiation pressure absorption coefficient parameter. */
  89.     private final ParameterDriver absorptionParameterDriver;

  90.     /** Driver for radiation pressure reflection coefficient parameter. */
  91.     private final ParameterDriver reflectionParameterDriver;

  92.     /** Surface vectors for body facets. */
  93.     private final List<Facet> facets;

  94.     /** Solar array area (m²). */
  95.     private final double solarArrayArea;

  96.     /** Reference date for linear rotation angle (may be null). */
  97.     private final AbsoluteDate referenceDate;

  98.     /** Rotation rate for linear rotation angle. */
  99.     private final double rotationRate;

  100.     /** Solar array reference axis in spacecraft frame (may be null). */
  101.     private final Vector3D saX;

  102.     /** Solar array third axis in spacecraft frame (may be null). */
  103.     private final Vector3D saY;

  104.     /** Solar array rotation axis in spacecraft frame. */
  105.     private final Vector3D saZ;

  106.     /** Sun model. */
  107.     private final PVCoordinatesProvider sun;

  108.     /** Build a spacecraft model with best lighting of solar array.
  109.      * <p>
  110.      * This constructor builds an instance that completely ignores lift
  111.      * in atmospheric drag (the value of lift coefficient is set to zero,
  112.      * and there are no {@link ParameterDriver drivers} to change it).
  113.      * </p>
  114.      * <p>
  115.      * Solar arrays orientation will be such that at each time the Sun direction
  116.      * will always be in the solar array meridian plane defined by solar array
  117.      * rotation axis and solar array normal vector.
  118.      * </p>
  119.      * @param xLength length of the body along its X axis (m)
  120.      * @param yLength length of the body along its Y axis (m)
  121.      * @param zLength length of the body along its Z axis (m)
  122.      * @param sun sun model
  123.      * @param solarArrayArea area of the solar array (m²)
  124.      * @param solarArrayAxis solar array rotation axis in satellite frame
  125.      * @param dragCoeff drag coefficient (used only for drag)
  126.      * @param absorptionCoeff absorption coefficient between 0.0 an 1.0
  127.      * (used only for radiation pressure)
  128.      * @param reflectionCoeff specular reflection coefficient between 0.0 an 1.0
  129.      * (used only for radiation pressure)
  130.      */
  131.     public BoxAndSolarArraySpacecraft(final double xLength, final double yLength,
  132.                                       final double zLength,
  133.                                       final PVCoordinatesProvider sun, final double solarArrayArea,
  134.                                       final Vector3D solarArrayAxis,
  135.                                       final double dragCoeff,
  136.                                       final double absorptionCoeff,
  137.                                       final double reflectionCoeff) {
  138.         this(simpleBoxFacets(xLength, yLength, zLength), sun, solarArrayArea, solarArrayAxis,
  139.              dragCoeff, 0.0, false,
  140.              absorptionCoeff, reflectionCoeff);
  141.     }

  142.     /** Build a spacecraft model with best lighting of solar array.
  143.      * <p>
  144.      * Solar arrays orientation will be such that at each time the Sun direction
  145.      * will always be in the solar array meridian plane defined by solar array
  146.      * rotation axis and solar array normal vector.
  147.      * </p>
  148.      * @param xLength length of the body along its X axis (m)
  149.      * @param yLength length of the body along its Y axis (m)
  150.      * @param zLength length of the body along its Z axis (m)
  151.      * @param sun sun model
  152.      * @param solarArrayArea area of the solar array (m²)
  153.      * @param solarArrayAxis solar array rotation axis in satellite frame
  154.      * @param dragCoeff drag coefficient (used only for drag)
  155.      * @param liftRatio lift ratio (proportion between 0 and 1 of atmosphere modecules
  156.      * that will experience specular reflection when hitting spacecraft instead
  157.      * of experiencing diffuse reflection, hence producing lift)
  158.      * @param absorptionCoeff absorption coefficient between 0.0 an 1.0
  159.      * (used only for radiation pressure)
  160.      * @param reflectionCoeff specular reflection coefficient between 0.0 an 1.0
  161.      * (used only for radiation pressure)
  162.      * @since 9.0
  163.      */
  164.     public BoxAndSolarArraySpacecraft(final double xLength, final double yLength,
  165.                                       final double zLength,
  166.                                       final PVCoordinatesProvider sun, final double solarArrayArea,
  167.                                       final Vector3D solarArrayAxis,
  168.                                       final double dragCoeff, final double liftRatio,
  169.                                       final double absorptionCoeff,
  170.                                       final double reflectionCoeff) {
  171.         this(simpleBoxFacets(xLength, yLength, zLength), sun, solarArrayArea, solarArrayAxis,
  172.              dragCoeff, liftRatio,
  173.              absorptionCoeff, reflectionCoeff);
  174.     }

  175.     /** Build a spacecraft model with best lighting of solar array.
  176.      * <p>
  177.      * The spacecraft body is described by an array of surface vectors. Each facet of
  178.      * the body is described by a vector normal to the facet (pointing outward of the spacecraft)
  179.      * and whose norm is the surface area in m².
  180.      * </p>
  181.      * <p>
  182.      * Solar arrays orientation will be such that at each time the Sun direction
  183.      * will always be in the solar array meridian plane defined by solar array
  184.      * rotation axis and solar array normal vector.
  185.      * </p>
  186.      * @param facets body facets (only the facets with strictly positive area will be stored)
  187.      * @param sun sun model
  188.      * @param solarArrayArea area of the solar array (m²)
  189.      * @param solarArrayAxis solar array rotation axis in satellite frame
  190.      * @param dragCoeff drag coefficient (used only for drag)
  191.      * @param absorptionCoeff absorption coefficient between 0.0 an 1.0
  192.      * (used only for radiation pressure)
  193.      * @param reflectionCoeff specular reflection coefficient between 0.0 an 1.0
  194.      * (used only for radiation pressure)
  195.      */
  196.     public BoxAndSolarArraySpacecraft(final Facet[] facets,
  197.                                       final PVCoordinatesProvider sun, final double solarArrayArea,
  198.                                       final Vector3D solarArrayAxis,
  199.                                       final double dragCoeff,
  200.                                       final double absorptionCoeff,
  201.                                       final double reflectionCoeff) {
  202.         this(facets, sun, solarArrayArea, solarArrayAxis,
  203.              dragCoeff, 0.0, false,
  204.              absorptionCoeff, reflectionCoeff);
  205.     }

  206.     /** Build a spacecraft model with best lighting of solar array.
  207.      * <p>
  208.      * The spacecraft body is described by an array of surface vectors. Each facet of
  209.      * the body is described by a vector normal to the facet (pointing outward of the spacecraft)
  210.      * and whose norm is the surface area in m².
  211.      * </p>
  212.      * <p>
  213.      * Solar arrays orientation will be such that at each time the Sun direction
  214.      * will always be in the solar array meridian plane defined by solar array
  215.      * rotation axis and solar array normal vector.
  216.      * </p>
  217.      * @param facets body facets (only the facets with strictly positive area will be stored)
  218.      * @param sun sun model
  219.      * @param solarArrayArea area of the solar array (m²)
  220.      * @param solarArrayAxis solar array rotation axis in satellite frame
  221.      * @param dragCoeff drag coefficient (used only for drag)
  222.      * @param liftRatio lift ratio (proportion between 0 and 1 of atmosphere modecules
  223.      * that will experience specular reflection when hitting spacecraft instead
  224.      * of experiencing diffuse reflection, hence producing lift)
  225.      * @param absorptionCoeff absorption coefficient between 0.0 an 1.0
  226.      * (used only for radiation pressure)
  227.      * @param reflectionCoeff specular reflection coefficient between 0.0 an 1.0
  228.      * (used only for radiation pressure)
  229.      * @since 9.0
  230.      */
  231.     public BoxAndSolarArraySpacecraft(final Facet[] facets,
  232.                                       final PVCoordinatesProvider sun, final double solarArrayArea,
  233.                                       final Vector3D solarArrayAxis,
  234.                                       final double dragCoeff, final double liftRatio,
  235.                                       final double absorptionCoeff,
  236.                                       final double reflectionCoeff) {
  237.         this(facets, sun, solarArrayArea, solarArrayAxis,
  238.              dragCoeff, liftRatio, true,
  239.              absorptionCoeff, reflectionCoeff);
  240.     }

  241.     /** Build a spacecraft model with best lighting of solar array.
  242.      * <p>
  243.      * The spacecraft body is described by an array of surface vectors. Each facet of
  244.      * the body is described by a vector normal to the facet (pointing outward of the spacecraft)
  245.      * and whose norm is the surface area in m².
  246.      * </p>
  247.      * <p>
  248.      * Solar arrays orientation will be such that at each time the Sun direction
  249.      * will always be in the solar array meridian plane defined by solar array
  250.      * rotation axis and solar array normal vector.
  251.      * </p>
  252.      * @param facets body facets (only the facets with strictly positive area will be stored)
  253.      * @param sun sun model
  254.      * @param solarArrayArea area of the solar array (m²)
  255.      * @param solarArrayAxis solar array rotation axis in satellite frame
  256.      * @param dragCoeff drag coefficient (used only for drag)
  257.      * @param liftRatio lift ratio (proportion between 0 and 1 of atmosphere modecules
  258.      * that will experience specular reflection when hitting spacecraft instead
  259.      * of experiencing diffuse reflection, hence producing lift)
  260.      * @param useLift if true, lift should be used, otherwise it is completely ignored
  261.      * @param absorptionCoeff absorption coefficient between 0.0 an 1.0
  262.      * (used only for radiation pressure)
  263.      * @param reflectionCoeff specular reflection coefficient between 0.0 an 1.0
  264.      * (used only for radiation pressure)
  265.      * @since 9.0
  266.      */
  267.     private BoxAndSolarArraySpacecraft(final Facet[] facets,
  268.                                        final PVCoordinatesProvider sun, final double solarArrayArea,
  269.                                        final Vector3D solarArrayAxis,
  270.                                        final double dragCoeff, final double liftRatio, final boolean useLift,
  271.                                        final double absorptionCoeff,
  272.                                        final double reflectionCoeff) {

  273.         // drag
  274.         dragParameterDriver = buildDragParameterDriver(dragCoeff);
  275.         liftParameterDriver = useLift ? buildLiftParameterDriver(liftRatio) : null;

  276.         // radiation pressure
  277.         absorptionParameterDriver = buildAbsorptionParameterDriver(absorptionCoeff);
  278.         reflectionParameterDriver = buildReflectionParameterDriver(reflectionCoeff);

  279.         this.facets = filter(facets);

  280.         this.sun            = sun;
  281.         this.solarArrayArea = solarArrayArea;
  282.         this.referenceDate  = null;
  283.         this.rotationRate   = 0;

  284.         this.saZ = solarArrayAxis.normalize();
  285.         this.saY = null;
  286.         this.saX = null;

  287.     }

  288.     /** Build a spacecraft model with linear rotation of solar array.
  289.      * <p>
  290.      * Solar arrays orientation will be a regular rotation from the
  291.      * reference orientation at reference date and using a constant
  292.      * rotation rate.
  293.      * </p>
  294.      * @param xLength length of the body along its X axis (m)
  295.      * @param yLength length of the body along its Y axis (m)
  296.      * @param zLength length of the body along its Z axis (m)
  297.      * @param sun sun model
  298.      * @param solarArrayArea area of the solar array (m²)
  299.      * @param solarArrayAxis solar array rotation axis in satellite frame
  300.      * @param referenceDate reference date for the solar array rotation
  301.      * @param referenceNormal direction of the solar array normal at reference date
  302.      * in spacecraft frame
  303.      * @param rotationRate rotation rate of the solar array, may be 0 (rad/s)
  304.      * @param dragCoeff drag coefficient (used only for drag)
  305.      * @param absorptionCoeff absorption coefficient between 0.0 an 1.0
  306.      * (used only for radiation pressure)
  307.      * @param reflectionCoeff specular reflection coefficient between 0.0 an 1.0
  308.      * (used only for radiation pressure)
  309.      */
  310.     public BoxAndSolarArraySpacecraft(final double xLength, final double yLength,
  311.                                       final double zLength,
  312.                                       final PVCoordinatesProvider sun, final double solarArrayArea,
  313.                                       final Vector3D solarArrayAxis,
  314.                                       final AbsoluteDate referenceDate,
  315.                                       final Vector3D referenceNormal,
  316.                                       final double rotationRate,
  317.                                       final double dragCoeff,
  318.                                       final double absorptionCoeff,
  319.                                       final double reflectionCoeff) {
  320.         this(simpleBoxFacets(xLength, yLength, zLength), sun, solarArrayArea, solarArrayAxis,
  321.              referenceDate, referenceNormal, rotationRate,
  322.              dragCoeff, 0.0, false,
  323.              absorptionCoeff, reflectionCoeff);
  324.     }

  325.     /** Build a spacecraft model with linear rotation of solar array.
  326.      * <p>
  327.      * Solar arrays orientation will be a regular rotation from the
  328.      * reference orientation at reference date and using a constant
  329.      * rotation rate.
  330.      * </p>
  331.      * @param xLength length of the body along its X axis (m)
  332.      * @param yLength length of the body along its Y axis (m)
  333.      * @param zLength length of the body along its Z axis (m)
  334.      * @param sun sun model
  335.      * @param solarArrayArea area of the solar array (m²)
  336.      * @param solarArrayAxis solar array rotation axis in satellite frame
  337.      * @param referenceDate reference date for the solar array rotation
  338.      * @param referenceNormal direction of the solar array normal at reference date
  339.      * in spacecraft frame
  340.      * @param rotationRate rotation rate of the solar array, may be 0 (rad/s)
  341.      * @param dragCoeff drag coefficient (used only for drag)
  342.      * @param liftRatio lift ratio (proportion between 0 and 1 of atmosphere modecules
  343.      * that will experience specular reflection when hitting spacecraft instead
  344.      * of experiencing diffuse reflection, hence producing lift)
  345.      * @param absorptionCoeff absorption coefficient between 0.0 an 1.0
  346.      * (used only for radiation pressure)
  347.      * @param reflectionCoeff specular reflection coefficient between 0.0 an 1.0
  348.      * (used only for radiation pressure)
  349.      * @since 9.0
  350.      */
  351.     public BoxAndSolarArraySpacecraft(final double xLength, final double yLength,
  352.                                       final double zLength,
  353.                                       final PVCoordinatesProvider sun, final double solarArrayArea,
  354.                                       final Vector3D solarArrayAxis,
  355.                                       final AbsoluteDate referenceDate,
  356.                                       final Vector3D referenceNormal,
  357.                                       final double rotationRate,
  358.                                       final double dragCoeff, final double liftRatio,
  359.                                       final double absorptionCoeff,
  360.                                       final double reflectionCoeff) {
  361.         this(simpleBoxFacets(xLength, yLength, zLength), sun, solarArrayArea, solarArrayAxis,
  362.              referenceDate, referenceNormal, rotationRate,
  363.              dragCoeff, liftRatio, true,
  364.              absorptionCoeff, reflectionCoeff);
  365.     }

  366.     /** Build a spacecraft model with linear rotation of solar array.
  367.      * <p>
  368.      * The spacecraft body is described by an array of surface vectors. Each facet of
  369.      * the body is described by a vector normal to the facet (pointing outward of the spacecraft)
  370.      * and whose norm is the surface area in m².
  371.      * </p>
  372.      * <p>
  373.      * Solar arrays orientation will be a regular rotation from the
  374.      * reference orientation at reference date and using a constant
  375.      * rotation rate.
  376.      * </p>
  377.      * @param facets body facets (only the facets with strictly positive area will be stored)
  378.      * @param sun sun model
  379.      * @param solarArrayArea area of the solar array (m²)
  380.      * @param solarArrayAxis solar array rotation axis in satellite frame
  381.      * @param referenceDate reference date for the solar array rotation
  382.      * @param referenceNormal direction of the solar array normal at reference date
  383.      * in spacecraft frame
  384.      * @param rotationRate rotation rate of the solar array, may be 0 (rad/s)
  385.      * @param dragCoeff drag coefficient (used only for drag)
  386.      * @param absorptionCoeff absorption coefficient between 0.0 an 1.0
  387.      * (used only for radiation pressure)
  388.      * @param reflectionCoeff specular reflection coefficient between 0.0 an 1.0
  389.      * (used only for radiation pressure)
  390.      */
  391.     public BoxAndSolarArraySpacecraft(final Facet[] facets,
  392.                                       final PVCoordinatesProvider sun, final double solarArrayArea,
  393.                                       final Vector3D solarArrayAxis,
  394.                                       final AbsoluteDate referenceDate,
  395.                                       final Vector3D referenceNormal,
  396.                                       final double rotationRate,
  397.                                       final double dragCoeff,
  398.                                       final double absorptionCoeff,
  399.                                       final double reflectionCoeff) {
  400.         this(facets, sun, solarArrayArea, solarArrayAxis, referenceDate, referenceNormal, rotationRate,
  401.              dragCoeff, 0.0, false,
  402.              absorptionCoeff, reflectionCoeff);
  403.     }

  404.     /** Build a spacecraft model with linear rotation of solar array.
  405.      * <p>
  406.      * The spacecraft body is described by an array of surface vectors. Each facet of
  407.      * the body is described by a vector normal to the facet (pointing outward of the spacecraft)
  408.      * and whose norm is the surface area in m².
  409.      * </p>
  410.      * <p>
  411.      * Solar arrays orientation will be a regular rotation from the
  412.      * reference orientation at reference date and using a constant
  413.      * rotation rate.
  414.      * </p>
  415.      * @param facets body facets (only the facets with strictly positive area will be stored)
  416.      * @param sun sun model
  417.      * @param solarArrayArea area of the solar array (m²)
  418.      * @param solarArrayAxis solar array rotation axis in satellite frame
  419.      * @param referenceDate reference date for the solar array rotation
  420.      * @param referenceNormal direction of the solar array normal at reference date
  421.      * in spacecraft frame
  422.      * @param rotationRate rotation rate of the solar array, may be 0 (rad/s)
  423.      * @param dragCoeff drag coefficient (used only for drag)
  424.      * @param liftRatio lift ratio (proportion between 0 and 1 of atmosphere modecules
  425.      * that will experience specular reflection when hitting spacecraft instead
  426.      * of experiencing diffuse reflection, hence producing lift)
  427.      * @param absorptionCoeff absorption coefficient between 0.0 an 1.0
  428.      * (used only for radiation pressure)
  429.      * @param reflectionCoeff specular reflection coefficient between 0.0 an 1.0
  430.      * (used only for radiation pressure)
  431.      * @since 9.0
  432.      */
  433.     public BoxAndSolarArraySpacecraft(final Facet[] facets,
  434.                                       final PVCoordinatesProvider sun, final double solarArrayArea,
  435.                                       final Vector3D solarArrayAxis,
  436.                                       final AbsoluteDate referenceDate,
  437.                                       final Vector3D referenceNormal,
  438.                                       final double rotationRate,
  439.                                       final double dragCoeff, final double liftRatio,
  440.                                       final double absorptionCoeff,
  441.                                       final double reflectionCoeff) {
  442.         this(facets, sun, solarArrayArea, solarArrayAxis, referenceDate, referenceNormal, rotationRate,
  443.              dragCoeff, liftRatio, true,
  444.              absorptionCoeff, reflectionCoeff);
  445.     }

  446.     /** Build a spacecraft model with linear rotation of solar array.
  447.      * <p>
  448.      * The spacecraft body is described by an array of surface vectors. Each facet of
  449.      * the body is described by a vector normal to the facet (pointing outward of the spacecraft)
  450.      * and whose norm is the surface area in m².
  451.      * </p>
  452.      * <p>
  453.      * Solar arrays orientation will be a regular rotation from the
  454.      * reference orientation at reference date and using a constant
  455.      * rotation rate.
  456.      * </p>
  457.      * @param facets body facets (only the facets with strictly positive area will be stored)
  458.      * @param sun sun model
  459.      * @param solarArrayArea area of the solar array (m²)
  460.      * @param solarArrayAxis solar array rotation axis in satellite frame
  461.      * @param referenceDate reference date for the solar array rotation
  462.      * @param referenceNormal direction of the solar array normal at reference date
  463.      * in spacecraft frame
  464.      * @param rotationRate rotation rate of the solar array, may be 0 (rad/s)
  465.      * @param dragCoeff drag coefficient (used only for drag)
  466.      * @param liftRatio lift ratio (proportion between 0 and 1 of atmosphere modecules
  467.      * that will experience specular reflection when hitting spacecraft instead
  468.      * of experiencing diffuse reflection, hence producing lift)
  469.      * @param useLift if true, lift should be used, otherwise it is completely ignored
  470.      * @param absorptionCoeff absorption coefficient between 0.0 an 1.0
  471.      * (used only for radiation pressure)
  472.      * @param reflectionCoeff specular reflection coefficient between 0.0 an 1.0
  473.      * (used only for radiation pressure)
  474.      * @since 9.0
  475.      */
  476.     private BoxAndSolarArraySpacecraft(final Facet[] facets,
  477.                                        final PVCoordinatesProvider sun, final double solarArrayArea,
  478.                                        final Vector3D solarArrayAxis,
  479.                                        final AbsoluteDate referenceDate,
  480.                                        final Vector3D referenceNormal,
  481.                                        final double rotationRate,
  482.                                        final double dragCoeff, final double liftRatio, final boolean useLift,
  483.                                        final double absorptionCoeff,
  484.                                        final double reflectionCoeff) {

  485.         // drag
  486.         dragParameterDriver = buildDragParameterDriver(dragCoeff);
  487.         liftParameterDriver = useLift ? buildLiftParameterDriver(liftRatio) : null;

  488.         // radiation pressure
  489.         absorptionParameterDriver = buildAbsorptionParameterDriver(absorptionCoeff);
  490.         reflectionParameterDriver = buildReflectionParameterDriver(reflectionCoeff);

  491.         this.facets = filter(facets.clone());

  492.         this.sun            = sun;
  493.         this.solarArrayArea = solarArrayArea;
  494.         this.referenceDate  = referenceDate;
  495.         this.rotationRate   = rotationRate;

  496.         this.saZ = solarArrayAxis.normalize();
  497.         this.saY = Vector3D.crossProduct(saZ, referenceNormal).normalize();
  498.         this.saX = Vector3D.crossProduct(saY, saZ);

  499.     }

  500.     /** Build the parameter driver for drag coefficient.
  501.      * @param coeff drag coefficient
  502.      * @return parameter driver for drag coefficient
  503.      * @since 9.0
  504.      */
  505.     private ParameterDriver buildDragParameterDriver(final double coeff) {
  506.         try {
  507.             return new ParameterDriver(DragSensitive.DRAG_COEFFICIENT,
  508.                                        coeff, SCALE, 0.0, Double.POSITIVE_INFINITY);
  509.         } catch (OrekitException oe) {
  510.             // this should never happen
  511.             throw new OrekitInternalError(oe);
  512.         }
  513.     }

  514.     /** Build the parameter driver for lift coefficient.
  515.      * @param coeff lift coefficient
  516.      * @return parameter driver for lift coefficient
  517.      * @since 9.0
  518.      */
  519.     private ParameterDriver buildLiftParameterDriver(final double coeff) {
  520.         try {
  521.             return new ParameterDriver(DragSensitive.LIFT_RATIO, coeff, SCALE, 0.0, 1.0);
  522.         } catch (OrekitException oe) {
  523.             // this should never happen
  524.             throw new OrekitInternalError(oe);
  525.         }
  526.     }

  527.     /** Build the parameter driver for absorption coefficient.
  528.      * @param coeff absorption coefficient
  529.      * @return parameter driver for absorption coefficient
  530.      * @since 9.0
  531.      */
  532.     private ParameterDriver buildAbsorptionParameterDriver(final double coeff) {
  533.         try {
  534.             return new ParameterDriver(RadiationSensitive.ABSORPTION_COEFFICIENT, coeff, SCALE, 0.0, 1.0);
  535.         } catch (OrekitException oe) {
  536.             // this should never happen
  537.             throw new OrekitInternalError(oe);
  538.         }
  539.     }

  540.     /** Build the parameter driver for reflection coefficient.
  541.      * @param coeff absorption coefficient
  542.      * @return parameter driver for reflection coefficient
  543.      * @since 9.0
  544.      */
  545.     private ParameterDriver buildReflectionParameterDriver(final double coeff) {
  546.         try {
  547.             return new ParameterDriver(RadiationSensitive.REFLECTION_COEFFICIENT, coeff, SCALE, 0.0, 1.0);
  548.         } catch (OrekitException oe) {
  549.             // this should never happen
  550.             throw new OrekitInternalError(oe);
  551.         }
  552.     }

  553.     /** {@inheritDoc} */
  554.     @Override
  555.     public ParameterDriver[] getDragParametersDrivers() {
  556.         return liftParameterDriver == null ?
  557.                new ParameterDriver[] {
  558.                    dragParameterDriver
  559.                } : new ParameterDriver[] {
  560.                    dragParameterDriver, liftParameterDriver
  561.                };
  562.     }

  563.     /** {@inheritDoc} */
  564.     @Override
  565.     public ParameterDriver[] getRadiationParametersDrivers() {
  566.         return new ParameterDriver[] {
  567.             absorptionParameterDriver, reflectionParameterDriver
  568.         };
  569.     }

  570.     /** Get solar array normal in spacecraft frame.
  571.      * @param date current date
  572.      * @param frame inertial reference frame for state (both orbit and attitude)
  573.      * @param position position of spacecraft in reference frame
  574.      * @param rotation orientation (attitude) of the spacecraft with respect to reference frame
  575.      * @return solar array normal in spacecraft frame
  576.      */
  577.     public synchronized Vector3D getNormal(final AbsoluteDate date, final Frame frame,
  578.                                            final Vector3D position, final Rotation rotation) {

  579.         if (referenceDate != null) {
  580.             // use a simple rotation at fixed rate
  581.             final double alpha = rotationRate * date.durationFrom(referenceDate);
  582.             final SinCos scAlpha = FastMath.sinCos(alpha);
  583.             return new Vector3D(scAlpha.cos(), saX, scAlpha.sin(), saY);
  584.         }

  585.         // compute orientation for best lighting
  586.         final Vector3D sunInert = sun.getPVCoordinates(date, frame).getPosition().subtract(position).normalize();
  587.         final Vector3D sunSpacecraft = rotation.applyTo(sunInert);
  588.         final double d = Vector3D.dotProduct(sunSpacecraft, saZ);
  589.         final double f = 1 - d * d;
  590.         if (f < Precision.EPSILON) {
  591.             // extremely rare case: the sun is along solar array rotation axis
  592.             // (there will not be much output power ...)
  593.             // we set up an arbitrary normal
  594.             return saZ.orthogonal();
  595.         }

  596.         final double s = 1.0 / FastMath.sqrt(f);
  597.         return new Vector3D(s, sunSpacecraft, -s * d, saZ);

  598.     }

  599.     /** Get solar array normal in spacecraft frame.
  600.      * @param date current date
  601.      * @param frame inertial reference frame for state (both orbit and attitude)
  602.      * @param position position of spacecraft in reference frame
  603.      * @param rotation orientation (attitude) of the spacecraft with respect to reference frame
  604.      * @return solar array normal in spacecraft frame
  605.      * @param <T> type of the field elements
  606.      */
  607.     public synchronized <T extends RealFieldElement<T>> FieldVector3D<T> getNormal(final FieldAbsoluteDate<T> date,
  608.                                                                                    final Frame frame,
  609.                                                                                    final FieldVector3D<T> position,
  610.                                                                                    final FieldRotation<T> rotation) {

  611.         if (referenceDate != null) {
  612.             // use a simple rotation at fixed rate
  613.             final T alpha = date.durationFrom(referenceDate).multiply(rotationRate);
  614.             return new FieldVector3D<>(alpha.cos(), saX, alpha.sin(), saY);
  615.         }

  616.         // compute orientation for best lighting
  617.         final FieldVector3D<T> sunInert = position.subtract(sun.getPVCoordinates(date.toAbsoluteDate(), frame).getPosition()).negate().normalize();
  618.         final FieldVector3D<T> sunSpacecraft = rotation.applyTo(sunInert);
  619.         final T d = FieldVector3D.dotProduct(sunSpacecraft, saZ);
  620.         final T f = d.multiply(d).subtract(1).negate();
  621.         if (f.getReal() < Precision.EPSILON) {
  622.             // extremely rare case: the sun is along solar array rotation axis
  623.             // (there will not be much output power ...)
  624.             // we set up an arbitrary normal
  625.             return new FieldVector3D<>(f.getField(), saZ.orthogonal());
  626.         }

  627.         final T s = f.sqrt().reciprocal();
  628.         return new FieldVector3D<>(s, sunSpacecraft,
  629.                                    s.multiply(d).negate(), new FieldVector3D<>(date.getField(), saZ));

  630.     }

  631.     /** Get solar array normal in spacecraft frame.
  632.      * @param date current date
  633.      * @param frame inertial reference frame for state (both orbit and attitude)
  634.      * @param position position of spacecraft in reference frame
  635.      * @param rotation orientation (attitude) of the spacecraft with respect to reference frame
  636.      * @return solar array normal in spacecraft frame
  637.      * @deprecated Method not used anymore, should have been deleted in 9.0 but was left over. To be deleted in the next major version.
  638.      */
  639.     @Deprecated
  640.     public synchronized FieldVector3D<DerivativeStructure> getNormal(final AbsoluteDate date, final Frame frame,
  641.                                                                      final FieldVector3D<DerivativeStructure> position,
  642.                                                                      final FieldRotation<DerivativeStructure> rotation) {

  643.         final DerivativeStructure zero = position.getX().getField().getZero();

  644.         if (referenceDate != null) {
  645.             // use a simple rotation at fixed rate
  646.             final DerivativeStructure alpha = zero.add(rotationRate * date.durationFrom(referenceDate));
  647.             return new FieldVector3D<>(alpha.cos(), saX, alpha.sin(), saY);
  648.         }

  649.         // compute orientation for best lighting
  650.         final FieldVector3D<DerivativeStructure> sunInert =
  651.                 position.subtract(sun.getPVCoordinates(date, frame).getPosition()).negate().normalize();
  652.         final FieldVector3D<DerivativeStructure> sunSpacecraft = rotation.applyTo(sunInert);
  653.         final DerivativeStructure d = FieldVector3D.dotProduct(sunSpacecraft, saZ);
  654.         final DerivativeStructure f = d.multiply(d).subtract(1).negate();
  655.         if (f.getValue() < Precision.EPSILON) {
  656.             // extremely rare case: the sun is along solar array rotation axis
  657.             // (there will not be much output power ...)
  658.             // we set up an arbitrary normal
  659.             return new FieldVector3D<>(position.getX().getField(), saZ.orthogonal());
  660.         }

  661.         final DerivativeStructure s = f.sqrt().reciprocal();
  662.         return new FieldVector3D<>(s, sunSpacecraft,
  663.                                    s.multiply(d).negate(), new FieldVector3D<>(zero.getField(), saZ));

  664.     }


  665.     /** {@inheritDoc} */
  666.     @Override
  667.     public Vector3D dragAcceleration(final AbsoluteDate date, final Frame frame, final Vector3D position,
  668.                                      final Rotation rotation, final double mass,
  669.                                      final double density, final Vector3D relativeVelocity,
  670.                                      final double[] parameters) {

  671.         final double dragCoeff = parameters[0];
  672.         final double liftRatio = liftParameterDriver == null ? 0.0 : parameters[1];

  673.         // relative velocity in spacecraft frame
  674.         final double   vNorm2 = relativeVelocity.getNormSq();
  675.         final double   vNorm  = FastMath.sqrt(vNorm2);
  676.         final Vector3D vDir   = rotation.applyTo(relativeVelocity.scalarMultiply(1.0 / vNorm));
  677.         final double   coeff  = density * dragCoeff * vNorm2 / (2.0 * mass);
  678.         final double   oMr    = 1 - liftRatio;

  679.         // solar array contribution
  680.         final Vector3D frontNormal = getNormal(date, frame, position, rotation);
  681.         final double   s           = coeff * solarArrayArea * Vector3D.dotProduct(frontNormal, vDir);
  682.         Vector3D acceleration = new Vector3D(oMr * FastMath.abs(s), vDir,
  683.                                              liftRatio * s * 2,     frontNormal);

  684.         // body facets contribution
  685.         for (final Facet facet : facets) {
  686.             final double dot = Vector3D.dotProduct(facet.getNormal(), vDir);
  687.             if (dot < 0) {
  688.                 // the facet intercepts the incoming flux
  689.                 final double f = coeff * facet.getArea() * dot;
  690.                 acceleration = new Vector3D(1,                     acceleration,
  691.                                             oMr * FastMath.abs(f), vDir,
  692.                                             liftRatio * f * 2,     facet.getNormal());
  693.             }
  694.         }

  695.         // convert back to inertial frame
  696.         return rotation.applyInverseTo(acceleration);

  697.     }

  698.     /** {@inheritDoc} */
  699.     @Override
  700.     public Vector3D radiationPressureAcceleration(final AbsoluteDate date, final Frame frame, final Vector3D position,
  701.                                                   final Rotation rotation, final double mass, final Vector3D flux,
  702.                                                   final double[] parameters) {

  703.         if (flux.getNormSq() < Precision.SAFE_MIN) {
  704.             // null illumination (we are probably in umbra)
  705.             return Vector3D.ZERO;
  706.         }

  707.         // radiation flux in spacecraft frame
  708.         final Vector3D fluxSat = rotation.applyTo(flux);

  709.         // solar array contribution
  710.         Vector3D normal = getNormal(date, frame, position, rotation);
  711.         double dot = Vector3D.dotProduct(normal, fluxSat);
  712.         if (dot > 0) {
  713.             // the solar array is illuminated backward,
  714.             // fix signs to compute contribution correctly
  715.             dot   = -dot;
  716.             normal = normal.negate();
  717.         }
  718.         Vector3D force = facetRadiationAcceleration(normal, solarArrayArea, fluxSat, dot, parameters);

  719.         // body facets contribution
  720.         for (final Facet bodyFacet : facets) {
  721.             normal = bodyFacet.getNormal();
  722.             dot = Vector3D.dotProduct(normal, fluxSat);
  723.             if (dot < 0) {
  724.                 // the facet intercepts the incoming flux
  725.                 force = force.add(facetRadiationAcceleration(normal, bodyFacet.getArea(), fluxSat, dot, parameters));
  726.             }
  727.         }

  728.         // convert to inertial frame
  729.         return rotation.applyInverseTo(new Vector3D(1.0 / mass, force));

  730.     }

  731.     /** {@inheritDoc} */
  732.     @Override
  733.     public <T extends RealFieldElement<T>> FieldVector3D<T>
  734.         dragAcceleration(final FieldAbsoluteDate<T> date, final Frame frame,
  735.                          final FieldVector3D<T> position, final FieldRotation<T> rotation,
  736.                          final T mass, final  T density, final FieldVector3D<T> relativeVelocity,
  737.                          final T[] parameters) {

  738.         final T dragCoeff = parameters[0];
  739.         final T liftRatio = liftParameterDriver == null ? dragCoeff.getField().getZero() : parameters[1];

  740.         // relative velocity in spacecraft frame
  741.         final T                vNorm2 = relativeVelocity.getNormSq();
  742.         final T                vNorm  = vNorm2.sqrt();
  743.         final FieldVector3D<T> vDir   = rotation.applyTo(relativeVelocity.scalarMultiply(vNorm.reciprocal()));
  744.         final T                coeff  = density.multiply(0.5).multiply(dragCoeff).multiply(vNorm2).divide(mass);
  745.         final T                oMr    = liftRatio.negate().add(1);

  746.         // solar array facet contribution
  747.         final FieldVector3D<T> frontNormal = getNormal(date, frame, position, rotation);
  748.         final T                s           = coeff.
  749.                                              multiply(solarArrayArea).
  750.                                              multiply(FieldVector3D.dotProduct(frontNormal, vDir));
  751.         FieldVector3D<T> acceleration = new FieldVector3D<>(s.abs().multiply(oMr), vDir,
  752.                                                             s.multiply(liftRatio).multiply(2), frontNormal);

  753.         // body facets contribution
  754.         final Field<T> field = coeff.getField();
  755.         for (final Facet facet : facets) {
  756.             final T dot = FieldVector3D.dotProduct(facet.getNormal(), vDir);
  757.             if (dot.getReal() < 0) {
  758.                 // the facet intercepts the incoming flux
  759.                 final T f = coeff.multiply(facet.getArea()).multiply(dot);
  760.                 acceleration = new FieldVector3D<>(field.getOne(),        acceleration,
  761.                                                    f.abs().multiply(oMr), vDir,
  762.                                                    f.multiply(liftRatio).multiply(2), new FieldVector3D<>(field, facet.getNormal()));
  763.             }
  764.         }

  765.         // convert back to inertial frame
  766.         return rotation.applyInverseTo(acceleration);

  767.     }

  768.     /** {@inheritDoc} */
  769.     @Override
  770.     public <T extends RealFieldElement<T>> FieldVector3D<T>
  771.         radiationPressureAcceleration(final FieldAbsoluteDate<T> date, final Frame frame,
  772.                                       final FieldVector3D<T> position,
  773.                                       final FieldRotation<T> rotation, final T mass,
  774.                                       final FieldVector3D<T> flux,
  775.                                       final T[] parameters) {

  776.         if (flux.getNormSq().getReal() < Precision.SAFE_MIN) {
  777.             // null illumination (we are probably in umbra)
  778.             return FieldVector3D.getZero(date.getField());
  779.         }

  780.         // radiation flux in spacecraft frame
  781.         final FieldVector3D<T> fluxSat = rotation.applyTo(flux);

  782.         // solar array contribution
  783.         FieldVector3D<T> normal = getNormal(date, frame, position, rotation);
  784.         T dot = FieldVector3D.dotProduct(normal, fluxSat);
  785.         if (dot.getReal() > 0) {
  786.             // the solar array is illuminated backward,
  787.             // fix signs to compute contribution correctly
  788.             dot    = dot.negate();
  789.             normal = normal.negate();
  790.         }
  791.         FieldVector3D<T> force = facetRadiationAcceleration(normal, solarArrayArea, fluxSat, dot, parameters);

  792.         // body facets contribution
  793.         for (final Facet bodyFacet : facets) {
  794.             normal = new FieldVector3D<>(date.getField(), bodyFacet.getNormal());
  795.             dot = FieldVector3D.dotProduct(fluxSat, normal);
  796.             if (dot.getReal() < 0) {
  797.                 // the facet intercepts the incoming flux
  798.                 force = force.add(facetRadiationAcceleration(normal, bodyFacet.getArea(), fluxSat, dot, parameters));
  799.             }
  800.         }

  801.         // convert to inertial frame
  802.         return rotation.applyInverseTo(new FieldVector3D<>(mass.reciprocal(), force));

  803.     }

  804.     /** Compute contribution of one facet to force.
  805.      * <p>This method implements equation 8-44 from David A. Vallado's
  806.      * Fundamentals of Astrodynamics and Applications, third edition,
  807.      * 2007, Microcosm Press.</p>
  808.      * @param normal facet normal
  809.      * @param area facet area
  810.      * @param fluxSat radiation pressure flux in spacecraft frame
  811.      * @param dot dot product of facet and fluxSat (must be negative)
  812.      * @param parameters values of the force model parameters
  813.      * @return contribution of the facet to force in spacecraft frame
  814.      */
  815.     private Vector3D facetRadiationAcceleration(final Vector3D normal, final double area, final Vector3D fluxSat,
  816.                                                 final double dot, final double[] parameters) {

  817.         final double absorptionCoeff         = parameters[0];
  818.         final double specularReflectionCoeff = parameters[1];
  819.         final double diffuseReflectionCoeff  = 1 - (absorptionCoeff + specularReflectionCoeff);

  820.         final double psr  = fluxSat.getNorm();

  821.         // Vallado's equation 8-44 uses different parameters which are related to our parameters as:
  822.         // cos (phi) = -dot / (psr * area)
  823.         // n         = facet / area
  824.         // s         = -fluxSat / psr
  825.         final double cN = 2 * area * dot * (diffuseReflectionCoeff / 3 - specularReflectionCoeff * dot / psr);
  826.         final double cS = (area * dot / psr) * (specularReflectionCoeff - 1);
  827.         return new Vector3D(cN, normal, cS, fluxSat);

  828.     }

  829.     /** Compute contribution of one facet to force.
  830.      * <p>This method implements equation 8-44 from David A. Vallado's
  831.      * Fundamentals of Astrodynamics and Applications, third edition,
  832.      * 2007, Microcosm Press.</p>
  833.      * @param normal facet normal
  834.      * @param area facet area
  835.      * @param fluxSat radiation pressure flux in spacecraft frame
  836.      * @param dot dot product of facet and fluxSat (must be negative)
  837.      * @param parameters values of the force model parameters
  838.      * @param <T> type of the field elements
  839.      * @return contribution of the facet to force in spacecraft frame
  840.      */
  841.     private <T extends RealFieldElement<T>> FieldVector3D<T>
  842.         facetRadiationAcceleration(final FieldVector3D<T> normal, final double area, final FieldVector3D<T> fluxSat,
  843.                                    final T dot, final T[] parameters) {

  844.         final T absorptionCoeff         = parameters[0];
  845.         final T specularReflectionCoeff = parameters[1];
  846.         final T diffuseReflectionCoeff  = absorptionCoeff.add(specularReflectionCoeff).negate().add(1);

  847.         final T psr  = fluxSat.getNorm();

  848.         // Vallado's equation 8-44 uses different parameters which are related to our parameters as:
  849.         // cos (phi) = -dot / (psr * area)
  850.         // n         = facet / area
  851.         // s         = -fluxSat / psr
  852.         final T cN = dot.multiply(-2 * area).multiply(dot.multiply(specularReflectionCoeff).divide(psr).subtract(diffuseReflectionCoeff.divide(3)));
  853.         final T cS = dot.multiply(area).multiply(specularReflectionCoeff.subtract(1)).divide(psr);
  854.         return new FieldVector3D<>(cN, normal, cS, fluxSat);

  855.     }

  856.     /** Class representing a single facet of a convex spacecraft body.
  857.      * <p>Instance of this class are guaranteed to be immutable.</p>
  858.      * @author Luc Maisonobe
  859.      */
  860.     public static class Facet {

  861.         /** Unit Normal vector, pointing outward. */
  862.         private final Vector3D normal;

  863.         /** Area in m². */
  864.         private final double area;

  865.         /** Simple constructor.
  866.          * @param normal vector normal to the facet, pointing outward (will be normalized)
  867.          * @param area facet area in m²
  868.          */
  869.         public Facet(final Vector3D normal, final double area) {
  870.             this.normal = normal.normalize();
  871.             this.area   = area;
  872.         }

  873.         /** Get unit normal vector.
  874.          * @return unit normal vector
  875.          */
  876.         public Vector3D getNormal() {
  877.             return normal;
  878.         }

  879.         /** Get facet area.
  880.          * @return facet area
  881.          */
  882.         public double getArea() {
  883.             return area;
  884.         }

  885.     }

  886.     /** Build the surface vectors for body facets of a simple parallelepipedic box.
  887.      * @param xLength length of the body along its X axis (m)
  888.      * @param yLength length of the body along its Y axis (m)
  889.      * @param zLength length of the body along its Z axis (m)
  890.      * @return surface vectors array
  891.      */
  892.     private static Facet[] simpleBoxFacets(final double xLength, final double yLength, final double zLength) {
  893.         return new Facet[] {
  894.             new Facet(Vector3D.MINUS_I, yLength * zLength),
  895.             new Facet(Vector3D.PLUS_I,  yLength * zLength),
  896.             new Facet(Vector3D.MINUS_J, xLength * zLength),
  897.             new Facet(Vector3D.PLUS_J,  xLength * zLength),
  898.             new Facet(Vector3D.MINUS_K, xLength * yLength),
  899.             new Facet(Vector3D.PLUS_K,  xLength * yLength)
  900.         };
  901.     }

  902.     /** Filter out zero area facets.
  903.      * @param facets original facets (may include zero area facets)
  904.      * @return filtered array
  905.      */
  906.     private static List<Facet> filter(final Facet[] facets) {
  907.         final List<Facet> filtered = new ArrayList<Facet>(facets.length);
  908.         for (Facet facet : facets) {
  909.             if (facet.getArea() > 0) {
  910.                 filtered.add(facet);
  911.             }
  912.         }
  913.         return filtered;
  914.     }

  915. }