KinematicTransform.java

  1. /* Copyright 2022-2025 Romain Serra
  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.frames;

  18. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  19. import org.hipparchus.geometry.euclidean.threed.Rotation;
  20. import org.orekit.time.AbsoluteDate;
  21. import org.orekit.utils.PVCoordinates;
  22. import org.orekit.utils.TimeStampedPVCoordinates;

  23. import java.util.Arrays;

  24. /**
  25.  * A transform that only includes translation and rotation as well as their respective rates.
  26.  * It is kinematic in the sense that it cannot transform an acceleration vector.
  27.  *
  28.  * @author Romain Serra
  29.  * @see StaticTransform
  30.  * @see Transform
  31.  * @since 12.1
  32.  */
  33. public interface KinematicTransform extends StaticTransform {

  34.     /**
  35.      * Get the identity kinematic transform.
  36.      *
  37.      * @return identity transform.
  38.      */
  39.     static KinematicTransform getIdentity() {
  40.         return Transform.IDENTITY;
  41.     }

  42.     /** Compute a composite velocity.
  43.      * @param first first applied transform
  44.      * @param second second applied transform
  45.      * @return velocity part of the composite transform
  46.      */
  47.     static Vector3D compositeVelocity(final KinematicTransform first, final KinematicTransform second) {

  48.         final Vector3D v1 = first.getVelocity();
  49.         final Rotation r1 = first.getRotation();
  50.         final Vector3D o1 = first.getRotationRate();
  51.         final Vector3D p2 = second.getTranslation();
  52.         final Vector3D v2 = second.getVelocity();

  53.         final Vector3D crossP = Vector3D.crossProduct(o1, p2);

  54.         return v1.add(r1.applyInverseTo(v2.add(crossP)));
  55.     }

  56.     /** Compute a composite rotation rate.
  57.      * @param first first applied transform
  58.      * @param second second applied transform
  59.      * @return rotation rate part of the composite transform
  60.      */
  61.     static Vector3D compositeRotationRate(final KinematicTransform first, final KinematicTransform second) {

  62.         final Vector3D o1 = first.getRotationRate();
  63.         final Rotation r2 = second.getRotation();
  64.         final Vector3D o2 = second.getRotationRate();

  65.         return o2.add(r2.applyTo(o1));
  66.     }

  67.     /** Transform {@link PVCoordinates}, without the acceleration vector.
  68.      * @param pv the position-velocity couple to transform.
  69.      * @return transformed position-velocity
  70.      */
  71.     default PVCoordinates transformOnlyPV(final PVCoordinates pv) {
  72.         final Vector3D transformedP = transformPosition(pv.getPosition());
  73.         final Vector3D crossP       = Vector3D.crossProduct(getRotationRate(), transformedP);
  74.         final Vector3D transformedV = getRotation().applyTo(pv.getVelocity().add(getVelocity())).subtract(crossP);
  75.         return new PVCoordinates(transformedP, transformedV);
  76.     }

  77.     /** Transform {@link TimeStampedPVCoordinates}, without the acceleration vector.
  78.      * <p>
  79.      * In order to allow the user more flexibility, this method does <em>not</em> check for
  80.      * consistency between the transform {@link #getDate() date} and the time-stamped
  81.      * position-velocity {@link TimeStampedPVCoordinates#getDate() date}. The returned
  82.      * value will always have the same {@link TimeStampedPVCoordinates#getDate() date} as
  83.      * the input argument, regardless of the instance {@link #getDate() date}.
  84.      * </p>
  85.      * @param pv the position-velocity couple to transform.
  86.      * @return transformed position-velocity
  87.      */
  88.     default TimeStampedPVCoordinates transformOnlyPV(final TimeStampedPVCoordinates pv) {
  89.         final Vector3D transformedP = transformPosition(pv.getPosition());
  90.         final Vector3D crossP       = Vector3D.crossProduct(getRotationRate(), transformedP);
  91.         final Vector3D transformedV = getRotation().applyTo(pv.getVelocity().add(getVelocity())).subtract(crossP);
  92.         return new TimeStampedPVCoordinates(pv.getDate(), transformedP, transformedV);
  93.     }

  94.     /** Compute the Jacobian of the {@link #transformOnlyPV(PVCoordinates)} (PVCoordinates)}
  95.      * method of the transform.
  96.      * <p>
  97.      * Element {@code jacobian[i][j]} is the derivative of Cartesian coordinate i
  98.      * of the transformed {@link PVCoordinates} with respect to Cartesian coordinate j
  99.      * of the input {@link PVCoordinates} in method {@link #transformOnlyPV(PVCoordinates)}.
  100.      * </p>
  101.      * <p>
  102.      * This definition implies that if we define position-velocity coordinates
  103.      * <pre>
  104.      * PV₁ = transform.transformPVCoordinates(PV₀), then
  105.      * </pre>
  106.      * <p> their differentials dPV₁ and dPV₀ will obey the following relation
  107.      * where J is the matrix computed by this method:
  108.      * <pre>
  109.      * dPV₁ = J &times; dPV₀
  110.      * </pre>
  111.      *
  112.      * @return Jacobian matrix
  113.      */
  114.     default double[][] getPVJacobian() {
  115.         final double[][] jacobian = new double[6][6];

  116.         // elementary matrix for rotation
  117.         final double[][] mData = getRotation().getMatrix();

  118.         // dP1/dP0
  119.         System.arraycopy(mData[0], 0, jacobian[0], 0, 3);
  120.         System.arraycopy(mData[1], 0, jacobian[1], 0, 3);
  121.         System.arraycopy(mData[2], 0, jacobian[2], 0, 3);

  122.         // dP1/dV0
  123.         Arrays.fill(jacobian[0], 3, 6, 0.0);
  124.         Arrays.fill(jacobian[1], 3, 6, 0.0);
  125.         Arrays.fill(jacobian[2], 3, 6, 0.0);

  126.         // dV1/dP0
  127.         final Vector3D o = getRotationRate();
  128.         final double ox = o.getX();
  129.         final double oy = o.getY();
  130.         final double oz = o.getZ();
  131.         for (int i = 0; i < 3; ++i) {
  132.             jacobian[3][i] = -(oy * mData[2][i] - oz * mData[1][i]);
  133.             jacobian[4][i] = -(oz * mData[0][i] - ox * mData[2][i]);
  134.             jacobian[5][i] = -(ox * mData[1][i] - oy * mData[0][i]);
  135.         }

  136.         // dV1/dV0
  137.         System.arraycopy(mData[0], 0, jacobian[3], 3, 3);
  138.         System.arraycopy(mData[1], 0, jacobian[4], 3, 3);
  139.         System.arraycopy(mData[2], 0, jacobian[5], 3, 3);

  140.         return jacobian;
  141.     }

  142.     /** Get the first time derivative of the translation.
  143.      * @return first time derivative of the translation
  144.      * @see #getTranslation()
  145.      */
  146.     Vector3D getVelocity();

  147.     /** Get the first time derivative of the rotation.
  148.      * <p>The norm represents the angular rate.</p>
  149.      * @return First time derivative of the rotation
  150.      * @see #getRotation()
  151.      */
  152.     Vector3D getRotationRate();

  153.     /**
  154.      * Get the inverse transform of the instance.
  155.      *
  156.      * @return inverse transform of the instance
  157.      */
  158.     KinematicTransform getInverse();

  159.     /**
  160.      * Build a transform by combining two existing ones.
  161.      * <p>
  162.      * Note that the dates of the two existing transformed are <em>ignored</em>,
  163.      * and the combined transform date is set to the date supplied in this
  164.      * constructor without any attempt to shift the raw transforms. This is a
  165.      * design choice allowing user full control of the combination.
  166.      * </p>
  167.      *
  168.      * @param date   date of the transform
  169.      * @param first  first transform applied
  170.      * @param second second transform applied
  171.      * @return the newly created kinematic transform that has the same effect as
  172.      * applying {@code first}, then {@code second}.
  173.      * @see #of(AbsoluteDate, PVCoordinates, Rotation, Vector3D)
  174.      */
  175.     static KinematicTransform compose(final AbsoluteDate date,
  176.                                       final KinematicTransform first,
  177.                                       final KinematicTransform second) {
  178.         final Vector3D composedTranslation = StaticTransform.compositeTranslation(first, second);
  179.         final Vector3D composedTranslationRate = KinematicTransform.compositeVelocity(first, second);
  180.         return of(date, new PVCoordinates(composedTranslation, composedTranslationRate),
  181.                 StaticTransform.compositeRotation(first, second),
  182.                 KinematicTransform.compositeRotationRate(first, second));
  183.     }

  184.     /**
  185.      * Create a new kinematic transform from a rotation and zero, constant translation.
  186.      *
  187.      * @param date     of translation.
  188.      * @param rotation to apply after the translation. That is after translating
  189.      *                 applying this rotation produces positions expressed in
  190.      *                 the new frame.
  191.      * @param rotationRate rate of rotation
  192.      * @return the newly created kinematic transform.
  193.      * @see #of(AbsoluteDate, PVCoordinates, Rotation, Vector3D)
  194.      */
  195.     static KinematicTransform of(final AbsoluteDate date,
  196.                                  final Rotation rotation,
  197.                                  final Vector3D rotationRate) {
  198.         return of(date, PVCoordinates.ZERO, rotation, rotationRate);
  199.     }

  200.     /**
  201.      * Create a new kinematic transform from a translation and its rate.
  202.      *
  203.      * @param date        of translation.
  204.      * @param pvCoordinates translation (with rate) to apply, expressed in the old frame. That is, the
  205.      *                    opposite of the coordinates of the new origin in the
  206.      *                    old frame.
  207.      * @return the newly created kinematic transform.
  208.      * @see #of(AbsoluteDate, PVCoordinates, Rotation, Vector3D)
  209.      */
  210.     static KinematicTransform of(final AbsoluteDate date,
  211.                                  final PVCoordinates pvCoordinates) {
  212.         return of(date, pvCoordinates, Rotation.IDENTITY, Vector3D.ZERO);
  213.     }

  214.     /**
  215.      * Create a new kinematic transform from a translation and rotation.
  216.      *
  217.      * @param date        of translation.
  218.      * @param pvCoordinates translation (with rate) to apply, expressed in the old frame. That is, the
  219.      *                    opposite of the coordinates of the new origin in the
  220.      *                    old frame.
  221.      * @param rotation    to apply after the translation. That is after
  222.      *                    translating applying this rotation produces positions
  223.      *                    expressed in the new frame.
  224.      * @param rotationRate rate of rotation
  225.      * @return the newly created kinematic transform.
  226.      * @see #compose(AbsoluteDate, KinematicTransform, KinematicTransform)
  227.      * @see #of(AbsoluteDate, PVCoordinates, Rotation, Vector3D)
  228.      * @see #of(AbsoluteDate, PVCoordinates, Rotation, Vector3D)
  229.      */
  230.     static KinematicTransform of(final AbsoluteDate date, final PVCoordinates pvCoordinates,
  231.                                  final Rotation rotation, final Vector3D rotationRate) {
  232.         return new KinematicTransform() {

  233.             @Override
  234.             public KinematicTransform getInverse() {
  235.                 final Rotation r = getRotation();
  236.                 final Vector3D rp = r.applyTo(getTranslation());
  237.                 final Vector3D pInv = rp.negate();
  238.                 final Vector3D crossP      = Vector3D.crossProduct(getRotationRate(), rp);
  239.                 final Vector3D vInv        = crossP.subtract(getRotation().applyTo(getVelocity()));
  240.                 final Rotation rInv = r.revert();
  241.                 return KinematicTransform.of(getDate(), new PVCoordinates(pInv, vInv),
  242.                         rInv, rInv.applyTo(getRotationRate()).negate());
  243.             }

  244.             @Override
  245.             public AbsoluteDate getDate() {
  246.                 return date;
  247.             }

  248.             @Override
  249.             public Vector3D getTranslation() {
  250.                 return pvCoordinates.getPosition();
  251.             }

  252.             @Override
  253.             public Rotation getRotation() {
  254.                 return rotation;
  255.             }

  256.             @Override
  257.             public Vector3D getVelocity() {
  258.                 return pvCoordinates.getVelocity();
  259.             }

  260.             @Override
  261.             public Vector3D getRotationRate() {
  262.                 return rotationRate;
  263.             }
  264.         };
  265.     }

  266. }