PrecessionFinder.java

  1. /* Copyright 2023 Luc Maisonobe
  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.files.ccsds.ndm.adm;

  18. import org.hipparchus.analysis.differentiation.UnivariateDerivative2;
  19. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  20. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  21. import org.hipparchus.util.FastMath;
  22. import org.hipparchus.util.SinCos;
  23. import org.orekit.errors.OrekitException;
  24. import org.orekit.errors.OrekitMessages;

  25. /** Utility to extract precession from the motion of a spin axis.
  26.  * <p>
  27.  * The precession is used in {@link AttitudeType#SPIN_NUTATION} and
  28.  * {@link AttitudeType#SPIN_NUTATION_MOMENTUM} attitude types. CCSDS
  29.  * calls it nutation, but it is really precession.
  30.  * </p>
  31.  * @author Luc Maisonobe
  32.  * @since 12.0
  33.  */
  34. class PrecessionFinder {

  35.     /** Precession axis (axis of the cone described by spin). */
  36.     private final Vector3D axis;

  37.     /** Precession angle (fixed cone angle between precession axis and spin axis). */
  38.     private final double precessionAngle;

  39.     /** Angular velocity around the precession axis (rad/s). */
  40.     private final double angularVelocity;

  41.     /** Build from spin axis motion.
  42.      * <p>
  43.      * Note that the derivatives up to second order are really needed
  44.      * in order to retrieve the precession motion.
  45.      * </p>
  46.      * @param spin spin axis, including value, first and second derivative
  47.      */
  48.     PrecessionFinder(final FieldVector3D<UnivariateDerivative2> spin) {

  49.         // using a suitable coordinates frame, the spin axis can be considered to describe
  50.         // a cone of half aperture angle 0 ≤ η ≤ π around k axis, at angular rate ω ≥ 0
  51.         // with an initial position in the (+i,±k) half-plane. In this frame, the normalized
  52.         // direction of spin s = spin/||spin|| and its first and second time derivatives
  53.         // have coordinates:
  54.         //           s(t):        (sin η cos ω(t-t₀), sin η sin ω(t-t₀), cos η)
  55.         //           ds/dt(t):    (-ω sin η sin ω(t-t₀), ω sin η cos ω(t-t₀), 0)
  56.         //           d²s/dt²(t):  (-ω² sin η cos ω(t-t₀), -ω² sin η sin ω(t-t₀), 0)
  57.         // at initial time t = t₀ this leads to:
  58.         //           s₀ = s(t₀):       (sin η, 0, cos η)
  59.         //           s₁ = ds/dt(t₀):   (0, ω sin η, 0)
  60.         //           s₂ = d²s/dt²(t₀): (-ω² sin η, 0, 0)
  61.         // however, only the spin vector itself is provided, we don't initially know the unit
  62.         // vectors (i, j, k) of this "suitable coordinates frame". We can however easily
  63.         // build another frame (u, v, w) from the normalized spin vector s as follows:
  64.         //           u = s₀, v = s₁/||s₁||, w = u ⨯ v
  65.         // the coordinates of vectors u, v and w in the "suitable coordinates frame" are:
  66.         //           u: ( sin η,   0,  cos η)
  67.         //           v: (     0,   1,      0)
  68.         //           w: (-cos η,   0,  sin η)
  69.         // we can then deduce the following relations, which can be computed regardless
  70.         // of frames used to express the various vectors:
  71.         //           s₁ · v = ω  sin η  = ||s₁||
  72.         //           s₂ · w = ω² sin η cos η
  73.         // these relations can be solved for η and ω (we know that 0 ≤ η ≤ π and ω ≥ 0):
  74.         //           η = atan2(||s₁||², s₂ · w)
  75.         //           ω = ||s₁|| / sin  η
  76.         // then the k axis, which is the precession axis, can be deduced as:
  77.         //           k = cos η u + sin η w

  78.         final UnivariateDerivative2 nS = spin.getNorm();
  79.         if (nS.getValue() == 0) {
  80.             // special case, no motion at all, set up arbitrary values
  81.             axis            = Vector3D.PLUS_K;
  82.             precessionAngle = 0.0;
  83.             angularVelocity = 0.0;
  84.         } else {

  85.             // build the derivatives vectors
  86.             final FieldVector3D<UnivariateDerivative2> s = spin.scalarMultiply(nS.reciprocal());
  87.             final Vector3D s0 = new Vector3D(s.getX().getValue(),
  88.                                              s.getY().getValue(),
  89.                                              s.getZ().getValue());
  90.             final Vector3D s1 = new Vector3D(s.getX().getFirstDerivative(),
  91.                                              s.getY().getFirstDerivative(),
  92.                                              s.getZ().getFirstDerivative());
  93.             final Vector3D s2 = new Vector3D(s.getX().getSecondDerivative(),
  94.                                              s.getY().getSecondDerivative(),
  95.                                              s.getZ().getSecondDerivative());

  96.             final double nV2 = s1.getNormSq();
  97.             if (nV2 == 0.0) {
  98.                 // special case: we have a fixed spin vector
  99.                 axis            = s0;
  100.                 precessionAngle = 0.0;
  101.                 angularVelocity = 0.0;
  102.             } else {

  103.                 // check second derivatives were provided ; we do it on spin rather than s2
  104.                 // and use test against exact 0.0 because the normalization process changes
  105.                 // the derivatives and what we really want to check are missing derivatives
  106.                 if (new Vector3D(spin.getX().getSecondDerivative(),
  107.                                  spin.getY().getSecondDerivative(),
  108.                                  spin.getZ().getSecondDerivative()).getNormSq() == 0) {
  109.                     throw new OrekitException(OrekitMessages.CANNOT_ESTIMATE_PRECESSION_WITHOUT_PROPER_DERIVATIVES);
  110.                 }

  111.                 // build the unit vectors
  112.                 final double   nV = FastMath.sqrt(nV2);
  113.                 final Vector3D v  = s1.scalarMultiply(1.0 / nV);
  114.                 final Vector3D w  = Vector3D.crossProduct(s0, v);

  115.                 // compute precession model
  116.                 precessionAngle  = FastMath.atan2(nV2, Vector3D.dotProduct(s2, w));
  117.                 final SinCos sc  = FastMath.sinCos(precessionAngle);
  118.                 angularVelocity  = nV / sc.sin();
  119.                 axis             = new Vector3D(sc.cos(), s0, sc.sin(), w);

  120.             }
  121.         }

  122.     }

  123.     /** Get the precession axis.
  124.      * @return precession axis
  125.      */
  126.     public Vector3D getAxis() {
  127.         return axis;
  128.     }

  129.     /** Get the precession angle.
  130.      * @return fixed cone angle between precession axis an spin axis (rad)
  131.      */
  132.     public double getPrecessionAngle() {
  133.         return precessionAngle;
  134.     }

  135.     /** Get the angular velocity around precession axis.
  136.      * @return angular velocity around precession axis (rad/s)
  137.      */
  138.     public double getAngularVelocity() {
  139.         return angularVelocity;
  140.     }

  141. }