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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  278.         this.facets = filter(facets);

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

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

  286.     }

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

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

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

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

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

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

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

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

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

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

  498.     }

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

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

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

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

  552.     /** {@inheritDoc} */
  553.     @Override
  554.     public List<ParameterDriver> getDragParametersDrivers() {
  555.         // Initialize list of drag parameter drivers
  556.         final List<ParameterDriver> drivers = new ArrayList<>();
  557.         drivers.add(dragParameterDriver);
  558.         // Verify if the driver for lift ratio parameter is defined
  559.         if (liftParameterDriver != null) {
  560.             drivers.add(liftParameterDriver);
  561.         }
  562.         return drivers;
  563.     }

  564.     /** {@inheritDoc} */
  565.     @Override
  566.     public List<ParameterDriver> getRadiationParametersDrivers() {
  567.         // Initialize list of drag parameter drivers
  568.         final List<ParameterDriver> drivers = new ArrayList<>();
  569.         drivers.add(absorptionParameterDriver);
  570.         drivers.add(reflectionParameterDriver);
  571.         return drivers;
  572.     }

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

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

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

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

  601.     }

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

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

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

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

  633.     }

  634.     /** {@inheritDoc} */
  635.     @Override
  636.     public Vector3D dragAcceleration(final AbsoluteDate date, final Frame frame, final Vector3D position,
  637.                                      final Rotation rotation, final double mass,
  638.                                      final double density, final Vector3D relativeVelocity,
  639.                                      final double[] parameters) {

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

  642.         // relative velocity in spacecraft frame
  643.         final double   vNorm2 = relativeVelocity.getNormSq();
  644.         final double   vNorm  = FastMath.sqrt(vNorm2);
  645.         final Vector3D vDir   = rotation.applyTo(relativeVelocity.scalarMultiply(1.0 / vNorm));
  646.         final double   coeff  = density * dragCoeff * vNorm2 / (2.0 * mass);
  647.         final double   oMr    = 1 - liftRatio;

  648.         // solar array contribution
  649.         final Vector3D frontNormal = getNormal(date, frame, position, rotation);
  650.         final double   s           = coeff * solarArrayArea * Vector3D.dotProduct(frontNormal, vDir);
  651.         Vector3D acceleration = new Vector3D(oMr * FastMath.abs(s), vDir,
  652.                                              liftRatio * s * 2,     frontNormal);

  653.         // body facets contribution
  654.         for (final Facet facet : facets) {
  655.             final double dot = Vector3D.dotProduct(facet.getNormal(), vDir);
  656.             if (dot < 0) {
  657.                 // the facet intercepts the incoming flux
  658.                 final double f = coeff * facet.getArea() * dot;
  659.                 acceleration = new Vector3D(1,                     acceleration,
  660.                                             oMr * FastMath.abs(f), vDir,
  661.                                             liftRatio * f * 2,     facet.getNormal());
  662.             }
  663.         }

  664.         // convert back to inertial frame
  665.         return rotation.applyInverseTo(acceleration);

  666.     }

  667.     /** {@inheritDoc} */
  668.     @Override
  669.     public Vector3D radiationPressureAcceleration(final AbsoluteDate date, final Frame frame, final Vector3D position,
  670.                                                   final Rotation rotation, final double mass, final Vector3D flux,
  671.                                                   final double[] parameters) {

  672.         if (flux.getNormSq() < Precision.SAFE_MIN) {
  673.             // null illumination (we are probably in umbra)
  674.             return Vector3D.ZERO;
  675.         }

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

  678.         // solar array contribution
  679.         Vector3D normal = getNormal(date, frame, position, rotation);
  680.         double dot = Vector3D.dotProduct(normal, fluxSat);
  681.         if (dot > 0) {
  682.             // the solar array is illuminated backward,
  683.             // fix signs to compute contribution correctly
  684.             dot   = -dot;
  685.             normal = normal.negate();
  686.         }
  687.         Vector3D force = facetRadiationAcceleration(normal, solarArrayArea, fluxSat, dot, parameters);

  688.         // body facets contribution
  689.         for (final Facet bodyFacet : facets) {
  690.             normal = bodyFacet.getNormal();
  691.             dot = Vector3D.dotProduct(normal, fluxSat);
  692.             if (dot < 0) {
  693.                 // the facet intercepts the incoming flux
  694.                 force = force.add(facetRadiationAcceleration(normal, bodyFacet.getArea(), fluxSat, dot, parameters));
  695.             }
  696.         }

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

  699.     }

  700.     /** {@inheritDoc} */
  701.     @Override
  702.     public <T extends CalculusFieldElement<T>> FieldVector3D<T>
  703.         dragAcceleration(final FieldAbsoluteDate<T> date, final Frame frame,
  704.                          final FieldVector3D<T> position, final FieldRotation<T> rotation,
  705.                          final T mass, final  T density, final FieldVector3D<T> relativeVelocity,
  706.                          final T[] parameters) {

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

  709.         // relative velocity in spacecraft frame
  710.         final T                vNorm2 = relativeVelocity.getNormSq();
  711.         final T                vNorm  = vNorm2.sqrt();
  712.         final FieldVector3D<T> vDir   = rotation.applyTo(relativeVelocity.scalarMultiply(vNorm.reciprocal()));
  713.         final T                coeff  = density.multiply(0.5).multiply(dragCoeff).multiply(vNorm2).divide(mass);
  714.         final T                oMr    = liftRatio.negate().add(1);

  715.         // solar array facet contribution
  716.         final FieldVector3D<T> frontNormal = getNormal(date, frame, position, rotation);
  717.         final T                s           = coeff.
  718.                                              multiply(solarArrayArea).
  719.                                              multiply(FieldVector3D.dotProduct(frontNormal, vDir));
  720.         FieldVector3D<T> acceleration = new FieldVector3D<>(s.abs().multiply(oMr), vDir,
  721.                                                             s.multiply(liftRatio).multiply(2), frontNormal);

  722.         // body facets contribution
  723.         final Field<T> field = coeff.getField();
  724.         for (final Facet facet : facets) {
  725.             final T dot = FieldVector3D.dotProduct(facet.getNormal(), vDir);
  726.             if (dot.getReal() < 0) {
  727.                 // the facet intercepts the incoming flux
  728.                 final T f = coeff.multiply(facet.getArea()).multiply(dot);
  729.                 acceleration = new FieldVector3D<>(field.getOne(),        acceleration,
  730.                                                    f.abs().multiply(oMr), vDir,
  731.                                                    f.multiply(liftRatio).multiply(2), new FieldVector3D<>(field, facet.getNormal()));
  732.             }
  733.         }

  734.         // convert back to inertial frame
  735.         return rotation.applyInverseTo(acceleration);

  736.     }

  737.     /** {@inheritDoc} */
  738.     @Override
  739.     public <T extends CalculusFieldElement<T>> FieldVector3D<T>
  740.         radiationPressureAcceleration(final FieldAbsoluteDate<T> date, final Frame frame,
  741.                                       final FieldVector3D<T> position,
  742.                                       final FieldRotation<T> rotation, final T mass,
  743.                                       final FieldVector3D<T> flux,
  744.                                       final T[] parameters) {

  745.         if (flux.getNormSq().getReal() < Precision.SAFE_MIN) {
  746.             // null illumination (we are probably in umbra)
  747.             return FieldVector3D.getZero(date.getField());
  748.         }

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

  751.         // solar array contribution
  752.         FieldVector3D<T> normal = getNormal(date, frame, position, rotation);
  753.         T dot = FieldVector3D.dotProduct(normal, fluxSat);
  754.         if (dot.getReal() > 0) {
  755.             // the solar array is illuminated backward,
  756.             // fix signs to compute contribution correctly
  757.             dot    = dot.negate();
  758.             normal = normal.negate();
  759.         }
  760.         FieldVector3D<T> force = facetRadiationAcceleration(normal, solarArrayArea, fluxSat, dot, parameters);

  761.         // body facets contribution
  762.         for (final Facet bodyFacet : facets) {
  763.             normal = new FieldVector3D<>(date.getField(), bodyFacet.getNormal());
  764.             dot = FieldVector3D.dotProduct(fluxSat, normal);
  765.             if (dot.getReal() < 0) {
  766.                 // the facet intercepts the incoming flux
  767.                 force = force.add(facetRadiationAcceleration(normal, bodyFacet.getArea(), fluxSat, dot, parameters));
  768.             }
  769.         }

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

  772.     }

  773.     /** Compute contribution of one facet to force.
  774.      * <p>This method implements equation 8-44 from David A. Vallado's
  775.      * Fundamentals of Astrodynamics and Applications, third edition,
  776.      * 2007, Microcosm Press.</p>
  777.      * @param normal facet normal
  778.      * @param area facet area
  779.      * @param fluxSat radiation pressure flux in spacecraft frame
  780.      * @param dot dot product of facet and fluxSat (must be negative)
  781.      * @param parameters values of the force model parameters
  782.      * @return contribution of the facet to force in spacecraft frame
  783.      */
  784.     private Vector3D facetRadiationAcceleration(final Vector3D normal, final double area, final Vector3D fluxSat,
  785.                                                 final double dot, final double[] parameters) {

  786.         final double absorptionCoeff         = parameters[0];
  787.         final double specularReflectionCoeff = parameters[1];
  788.         final double diffuseReflectionCoeff  = 1 - (absorptionCoeff + specularReflectionCoeff);

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

  790.         // Vallado's equation 8-44 uses different parameters which are related to our parameters as:
  791.         // cos (phi) = -dot / (psr * area)
  792.         // n         = facet / area
  793.         // s         = -fluxSat / psr
  794.         final double cN = 2 * area * dot * (diffuseReflectionCoeff / 3 - specularReflectionCoeff * dot / psr);
  795.         final double cS = (area * dot / psr) * (specularReflectionCoeff - 1);
  796.         return new Vector3D(cN, normal, cS, fluxSat);

  797.     }

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

  813.         final T absorptionCoeff         = parameters[0];
  814.         final T specularReflectionCoeff = parameters[1];
  815.         final T diffuseReflectionCoeff  = absorptionCoeff.add(specularReflectionCoeff).negate().add(1);

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

  817.         // Vallado's equation 8-44 uses different parameters which are related to our parameters as:
  818.         // cos (phi) = -dot / (psr * area)
  819.         // n         = facet / area
  820.         // s         = -fluxSat / psr
  821.         final T cN = dot.multiply(-2 * area).multiply(dot.multiply(specularReflectionCoeff).divide(psr).subtract(diffuseReflectionCoeff.divide(3)));
  822.         final T cS = dot.multiply(area).multiply(specularReflectionCoeff.subtract(1)).divide(psr);
  823.         return new FieldVector3D<>(cN, normal, cS, fluxSat);

  824.     }

  825.     /** Class representing a single facet of a convex spacecraft body.
  826.      * <p>Instance of this class are guaranteed to be immutable.</p>
  827.      * @author Luc Maisonobe
  828.      */
  829.     public static class Facet {

  830.         /** Unit Normal vector, pointing outward. */
  831.         private final Vector3D normal;

  832.         /** Area in m². */
  833.         private final double area;

  834.         /** Simple constructor.
  835.          * @param normal vector normal to the facet, pointing outward (will be normalized)
  836.          * @param area facet area in m²
  837.          */
  838.         public Facet(final Vector3D normal, final double area) {
  839.             this.normal = normal.normalize();
  840.             this.area   = area;
  841.         }

  842.         /** Get unit normal vector.
  843.          * @return unit normal vector
  844.          */
  845.         public Vector3D getNormal() {
  846.             return normal;
  847.         }

  848.         /** Get facet area.
  849.          * @return facet area
  850.          */
  851.         public double getArea() {
  852.             return area;
  853.         }

  854.     }

  855.     /** Build the surface vectors for body facets of a simple parallelepipedic box.
  856.      * @param xLength length of the body along its X axis (m)
  857.      * @param yLength length of the body along its Y axis (m)
  858.      * @param zLength length of the body along its Z axis (m)
  859.      * @return surface vectors array
  860.      */
  861.     private static Facet[] simpleBoxFacets(final double xLength, final double yLength, final double zLength) {
  862.         return new Facet[] {
  863.             new Facet(Vector3D.MINUS_I, yLength * zLength),
  864.             new Facet(Vector3D.PLUS_I,  yLength * zLength),
  865.             new Facet(Vector3D.MINUS_J, xLength * zLength),
  866.             new Facet(Vector3D.PLUS_J,  xLength * zLength),
  867.             new Facet(Vector3D.MINUS_K, xLength * yLength),
  868.             new Facet(Vector3D.PLUS_K,  xLength * yLength)
  869.         };
  870.     }

  871.     /** Filter out zero area facets.
  872.      * @param facets original facets (may include zero area facets)
  873.      * @return filtered array
  874.      */
  875.     private static List<Facet> filter(final Facet[] facets) {
  876.         final List<Facet> filtered = new ArrayList<Facet>(facets.length);
  877.         for (Facet facet : facets) {
  878.             if (facet.getArea() > 0) {
  879.                 filtered.add(facet);
  880.             }
  881.         }
  882.         return filtered;
  883.     }

  884. }