FieldKinematicTransform.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.CalculusFieldElement;
  19. import org.hipparchus.Field;
  20. import org.hipparchus.geometry.euclidean.threed.FieldRotation;
  21. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  22. import org.hipparchus.util.MathArrays;
  23. import org.orekit.time.AbsoluteDate;
  24. import org.orekit.time.FieldAbsoluteDate;
  25. import org.orekit.utils.FieldPVCoordinates;
  26. import org.orekit.utils.PVCoordinates;
  27. import org.orekit.utils.TimeStampedFieldPVCoordinates;
  28. import org.orekit.utils.TimeStampedPVCoordinates;

  29. import java.util.Arrays;

  30. /**
  31.  * A transform that only includes translation and rotation as well as their respective rates.
  32.  * It is kinematic in the sense that it cannot transform an acceleration vector.
  33.  *
  34.  * @author Romain Serra
  35.  * @see FieldStaticTransform
  36.  * @see FieldTransform
  37.  * @see KinematicTransform
  38.  * @since 12.1
  39.  */
  40. public interface FieldKinematicTransform<T extends CalculusFieldElement<T>> extends FieldStaticTransform<T> {

  41.     /**
  42.      * Get the identity kinematic transform.
  43.      *
  44.      * @param <T> type of the elements
  45.      * @param field field used by default
  46.      * @return identity transform.
  47.      */
  48.     static <T extends CalculusFieldElement<T>> FieldKinematicTransform<T> getIdentity(final Field<T> field) {
  49.         return FieldTransform.getIdentity(field);
  50.     }

  51.     /** Compute a composite velocity.
  52.      * @param first first applied transform
  53.      * @param second second applied transform
  54.      * @param <T> the type of the field elements
  55.      * @return velocity part of the composite transform
  56.      */
  57.     static <T extends CalculusFieldElement<T>> FieldVector3D<T> compositeVelocity(final FieldKinematicTransform<T> first,
  58.                                                                                   final FieldKinematicTransform<T> second) {

  59.         final FieldVector3D<T> v1 = first.getVelocity();
  60.         final FieldRotation<T> r1 = first.getRotation();
  61.         final FieldVector3D<T> o1 = first.getRotationRate();
  62.         final FieldVector3D<T> p2 = second.getTranslation();
  63.         final FieldVector3D<T> v2 = second.getVelocity();

  64.         final FieldVector3D<T> crossP = FieldVector3D.crossProduct(o1, p2);

  65.         return v1.add(r1.applyInverseTo(v2.add(crossP)));
  66.     }

  67.     /** Compute a composite rotation rate.
  68.      * @param <T> type of the elements
  69.      * @param first first applied transform
  70.      * @param second second applied transform
  71.      * @return rotation rate part of the composite transform
  72.      */
  73.     static <T extends CalculusFieldElement<T>> FieldVector3D<T> compositeRotationRate(final FieldKinematicTransform<T> first,
  74.                                                                                       final FieldKinematicTransform<T> second) {

  75.         final FieldVector3D<T> o1 = first.getRotationRate();
  76.         final FieldRotation<T> r2 = second.getRotation();
  77.         final FieldVector3D<T> o2 = second.getRotationRate();

  78.         return o2.add(r2.applyTo(o1));
  79.     }

  80.     /** Transform {@link PVCoordinates}, without the acceleration vector.
  81.      * @param pv the position-velocity couple to transform.
  82.      * @return transformed position-velocity
  83.      */
  84.     default FieldPVCoordinates<T> transformOnlyPV(final FieldPVCoordinates<T> pv) {
  85.         final FieldVector3D<T> transformedP = transformPosition(pv.getPosition());
  86.         final FieldVector3D<T> crossP       = FieldVector3D.crossProduct(getRotationRate(), transformedP);
  87.         final FieldVector3D<T> transformedV = getRotation().applyTo(pv.getVelocity().add(getVelocity())).subtract(crossP);
  88.         return new FieldPVCoordinates<>(transformedP, transformedV);
  89.     }

  90.     /** Transform {@link TimeStampedPVCoordinates}, without the acceleration vector.
  91.      * <p>
  92.      * In order to allow the user more flexibility, this method does <em>not</em> check for
  93.      * consistency between the transform {@link #getDate() date} and the time-stamped
  94.      * position-velocity {@link TimeStampedPVCoordinates#getDate() date}. The returned
  95.      * value will always have the same {@link TimeStampedPVCoordinates#getDate() date} as
  96.      * the input argument, regardless of the instance {@link #getDate() date}.
  97.      * </p>
  98.      * @param pv the position-velocity couple to transform.
  99.      * @return transformed position-velocity
  100.      */
  101.     default TimeStampedFieldPVCoordinates<T> transformOnlyPV(final TimeStampedFieldPVCoordinates<T> pv) {
  102.         final FieldVector3D<T> transformedP = transformPosition(pv.getPosition());
  103.         final FieldVector3D<T> crossP       = FieldVector3D.crossProduct(getRotationRate(), transformedP);
  104.         final FieldVector3D<T> transformedV = getRotation().applyTo(pv.getVelocity().add(getVelocity())).subtract(crossP);
  105.         return new TimeStampedFieldPVCoordinates<>(pv.getDate(), transformedP, transformedV,
  106.                 FieldVector3D.getZero(pv.getDate().getField()));
  107.     }

  108.     /** Compute the Jacobian of the {@link #transformOnlyPV(FieldPVCoordinates)} (FieldPVCoordinates)}
  109.      * method of the transform.
  110.      * <p>
  111.      * Element {@code jacobian[i][j]} is the derivative of Cartesian coordinate i
  112.      * of the transformed {@link FieldPVCoordinates} with respect to Cartesian coordinate j
  113.      * of the input {@link FieldPVCoordinates} in method {@link #transformOnlyPV(FieldPVCoordinates)}.
  114.      * </p>
  115.      * <p>
  116.      * This definition implies that if we define position-velocity coordinates
  117.      * <pre>
  118.      * PV₁ = transform.transformPVCoordinates(PV₀), then
  119.      * </pre>
  120.      * <p> their differentials dPV₁ and dPV₀ will obey the following relation
  121.      * where J is the matrix computed by this method:
  122.      * <pre>
  123.      * dPV₁ = J &times; dPV₀
  124.      * </pre>
  125.      *
  126.      * @return Jacobian matrix
  127.      */
  128.     default T[][] getPVJacobian() {
  129.         final Field<T> field = getFieldDate().getField();
  130.         final T zero = field.getZero();
  131.         final T[][] jacobian = MathArrays.buildArray(field, 6, 6);

  132.         // elementary matrix for rotation
  133.         final T[][] mData = getRotation().getMatrix();

  134.         // dP1/dP0
  135.         System.arraycopy(mData[0], 0, jacobian[0], 0, 3);
  136.         System.arraycopy(mData[1], 0, jacobian[1], 0, 3);
  137.         System.arraycopy(mData[2], 0, jacobian[2], 0, 3);

  138.         // dP1/dV0
  139.         Arrays.fill(jacobian[0], 3, 6, zero);
  140.         Arrays.fill(jacobian[1], 3, 6, zero);
  141.         Arrays.fill(jacobian[2], 3, 6, zero);

  142.         // dV1/dP0
  143.         final FieldVector3D<T> o = getRotationRate();
  144.         final T ox = o.getX();
  145.         final T oy = o.getY();
  146.         final T oz = o.getZ();
  147.         for (int i = 0; i < 3; ++i) {
  148.             jacobian[3][i] = oz.multiply(mData[1][i]).subtract(oy.multiply(mData[2][i]));
  149.             jacobian[4][i] = ox.multiply(mData[2][i]).subtract(oz.multiply(mData[0][i]));
  150.             jacobian[5][i] = oy.multiply(mData[0][i]).subtract(ox.multiply(mData[1][i]));
  151.         }

  152.         // dV1/dV0
  153.         System.arraycopy(mData[0], 0, jacobian[3], 3, 3);
  154.         System.arraycopy(mData[1], 0, jacobian[4], 3, 3);
  155.         System.arraycopy(mData[2], 0, jacobian[5], 3, 3);

  156.         return jacobian;
  157.     }

  158.     /** Get the first time derivative of the translation.
  159.      * @return first time derivative of the translation
  160.      * @see #getTranslation()
  161.      */
  162.     FieldVector3D<T> getVelocity();

  163.     /** Get the first time derivative of the rotation.
  164.      * <p>The norm represents the angular rate.</p>
  165.      * @return First time derivative of the rotation
  166.      * @see #getRotation()
  167.      */
  168.     FieldVector3D<T> getRotationRate();

  169.     /**
  170.      * Get the inverse transform of the instance.
  171.      *
  172.      * @return inverse transform of the instance
  173.      */
  174.     FieldKinematicTransform<T> getInverse();

  175.     /**
  176.      * Build a transform by combining two existing ones.
  177.      * <p>
  178.      * Note that the dates of the two existing transformed are <em>ignored</em>,
  179.      * and the combined transform date is set to the date supplied in this
  180.      * constructor without any attempt to shift the raw transforms. This is a
  181.      * design choice allowing user full control of the combination.
  182.      * </p>
  183.      *
  184.      * @param <T> type of the elements
  185.      * @param date   date of the transform
  186.      * @param first  first transform applied
  187.      * @param second second transform applied
  188.      * @return the newly created kinematic transform that has the same effect as
  189.      * applying {@code first}, then {@code second}.
  190.      * @see #of(FieldAbsoluteDate, FieldPVCoordinates, FieldRotation, FieldVector3D)
  191.      */
  192.     static <T extends CalculusFieldElement<T>> FieldKinematicTransform<T> compose(final FieldAbsoluteDate<T> date,
  193.                                                                                   final FieldKinematicTransform<T> first,
  194.                                                                                   final FieldKinematicTransform<T> second) {
  195.         final FieldVector3D<T> composedTranslation = FieldStaticTransform.compositeTranslation(first, second);
  196.         final FieldVector3D<T> composedTranslationRate = FieldKinematicTransform.compositeVelocity(first, second);
  197.         return of(date, new FieldPVCoordinates<>(composedTranslation, composedTranslationRate),
  198.                 FieldStaticTransform.compositeRotation(first, second),
  199.                 FieldKinematicTransform.compositeRotationRate(first, second));
  200.     }

  201.     /**
  202.      * Create a new kinematic transform from a rotation and zero, constant translation.
  203.      *
  204.      * @param <T> type of the elements
  205.      * @param date     of translation.
  206.      * @param rotation to apply after the translation. That is after translating
  207.      *                 applying this rotation produces positions expressed in
  208.      *                 the new frame.
  209.      * @param rotationRate rate of rotation
  210.      * @return the newly created kinematic transform.
  211.      * @see #of(FieldAbsoluteDate, FieldPVCoordinates, FieldRotation, FieldVector3D)
  212.      */
  213.     static <T extends CalculusFieldElement<T>> FieldKinematicTransform<T> of(final FieldAbsoluteDate<T> date,
  214.                                                                              final FieldRotation<T> rotation,
  215.                                                                              final FieldVector3D<T> rotationRate) {
  216.         return of(date, FieldPVCoordinates.getZero(date.getField()), rotation, rotationRate);
  217.     }

  218.     /**
  219.      * Create a new kinematic transform from a translation and its rate.
  220.      *
  221.      * @param <T> type of the elements
  222.      * @param date        of translation.
  223.      * @param pvCoordinates translation (with rate) to apply, expressed in the old frame. That is, the
  224.      *                    opposite of the coordinates of the new origin in the
  225.      *                    old frame.
  226.      * @return the newly created kinematic transform.
  227.      * @see #of(FieldAbsoluteDate, FieldPVCoordinates, FieldRotation, FieldVector3D)
  228.      */
  229.     static <T extends CalculusFieldElement<T>> FieldKinematicTransform<T> of(final FieldAbsoluteDate<T> date,
  230.                                                                              final FieldPVCoordinates<T> pvCoordinates) {
  231.         final Field<T> field = date.getField();
  232.         return of(date, pvCoordinates, FieldRotation.getIdentity(field), FieldVector3D.getZero(field));
  233.     }

  234.     /**
  235.      * Create a new kinematic transform from a non-Field version.
  236.      *
  237.      * @param <T> type of the elements
  238.      * @param field field.
  239.      * @param kinematicTransform non-Field kinematic transform
  240.      * @return the newly created kinematic transform.
  241.      * @see #of(FieldAbsoluteDate, FieldPVCoordinates, FieldRotation, FieldVector3D)
  242.      */
  243.     static <T extends CalculusFieldElement<T>> FieldKinematicTransform<T> of(final Field<T> field,
  244.                                                                              final KinematicTransform kinematicTransform) {
  245.         final FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, kinematicTransform.getDate());
  246.         final FieldPVCoordinates<T> pvCoordinates = new FieldPVCoordinates<>(field,
  247.             new PVCoordinates(kinematicTransform.getTranslation(), kinematicTransform.getVelocity()));
  248.         final FieldRotation<T> rotation = new FieldRotation<>(field, kinematicTransform.getRotation());
  249.         final FieldVector3D<T> rotationRate = new FieldVector3D<>(field, kinematicTransform.getRotationRate());
  250.         return of(date, pvCoordinates, rotation, rotationRate);
  251.     }

  252.     /**
  253.      * Create a new kinematic transform from a translation and rotation.
  254.      *
  255.      * @param <T> type of the elements
  256.      * @param date        of translation.
  257.      * @param pvCoordinates translation (with rate) to apply, expressed in the old frame. That is, the
  258.      *                    opposite of the coordinates of the new origin in the
  259.      *                    old frame.
  260.      * @param rotation    to apply after the translation. That is after
  261.      *                    translating applying this rotation produces positions
  262.      *                    expressed in the new frame.
  263.      * @param rotationRate rate of rotation
  264.      * @return the newly created kinematic transform.
  265.      * @see #compose(FieldAbsoluteDate, FieldKinematicTransform, FieldKinematicTransform)
  266.      * @see #of(FieldAbsoluteDate, FieldPVCoordinates, FieldRotation, FieldVector3D)
  267.      * @see #of(FieldAbsoluteDate, FieldPVCoordinates, FieldRotation, FieldVector3D)
  268.      */
  269.     static <T extends CalculusFieldElement<T>> FieldKinematicTransform<T> of(final FieldAbsoluteDate<T> date,
  270.                                                                           final FieldPVCoordinates<T> pvCoordinates,
  271.                                                                           final FieldRotation<T> rotation,
  272.                                                                           final FieldVector3D<T> rotationRate) {
  273.         return new FieldKinematicTransform<T>() {

  274.             @Override
  275.             public FieldKinematicTransform<T> getInverse() {
  276.                 final FieldRotation<T> r = getRotation();
  277.                 final FieldVector3D<T> rp = r.applyTo(getTranslation());
  278.                 final FieldVector3D<T> pInv = rp.negate();
  279.                 final FieldVector3D<T> crossP      = FieldVector3D.crossProduct(getRotationRate(), rp);
  280.                 final FieldVector3D<T> vInv        = crossP.subtract(getRotation().applyTo(getVelocity()));
  281.                 final FieldRotation<T> rInv = r.revert();
  282.                 return FieldKinematicTransform.of(date, new FieldPVCoordinates<>(pInv, vInv),
  283.                         rInv, rInv.applyTo(getRotationRate()).negate());
  284.             }

  285.             @Override
  286.             public AbsoluteDate getDate() {
  287.                 return date.toAbsoluteDate();
  288.             }

  289.             @Override
  290.             public FieldAbsoluteDate<T> getFieldDate() {
  291.                 return date;
  292.             }

  293.             @Override
  294.             public FieldVector3D<T> getTranslation() {
  295.                 return pvCoordinates.getPosition();
  296.             }

  297.             @Override
  298.             public FieldRotation<T> getRotation() {
  299.                 return rotation;
  300.             }

  301.             @Override
  302.             public FieldVector3D<T> getVelocity() {
  303.                 return pvCoordinates.getVelocity();
  304.             }

  305.             @Override
  306.             public FieldVector3D<T> getRotationRate() {
  307.                 return rotationRate;
  308.             }
  309.         };
  310.     }

  311. }