ApmData.java

  1. /* Copyright 2002-2024 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.files.ccsds.ndm.adm.apm;

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

  21. import org.hipparchus.complex.Quaternion;
  22. import org.orekit.attitudes.Attitude;
  23. import org.orekit.errors.OrekitException;
  24. import org.orekit.errors.OrekitMessages;
  25. import org.orekit.files.ccsds.ndm.adm.AttitudeType;
  26. import org.orekit.files.ccsds.section.CommentsContainer;
  27. import org.orekit.files.ccsds.section.Data;
  28. import org.orekit.frames.Frame;
  29. import org.orekit.time.AbsoluteDate;
  30. import org.orekit.utils.PVCoordinatesProvider;
  31. import org.orekit.utils.TimeStampedAngularCoordinates;

  32. /**
  33.  * Container for Attitude Parameter Message data.
  34.  * @author Bryan Cazabonne
  35.  * @since 10.2
  36.  */
  37. public class ApmData implements Data {

  38.     /** General comments block. */
  39.     private final CommentsContainer commentsBlock;

  40.     /** Epoch of the data. */
  41.     private final AbsoluteDate epoch;

  42.     /** Quaternion block. */
  43.     private final ApmQuaternion quaternionBlock;

  44.     /** Euler angles block. */
  45.     private final Euler eulerBlock;

  46.     /** Angular velocity block.
  47.      * @since 12.0
  48.      */
  49.     private final AngularVelocity angularVelocityBlock;

  50.     /** Spin-stabilized block. */
  51.     private final SpinStabilized spinStabilizedBlock;

  52.     /** Inertia block. */
  53.     private final Inertia inertia;

  54.     /** Maneuvers. */
  55.     private final List<Maneuver> maneuvers;

  56.     /** Simple constructor.
  57.      * @param commentsBlock general comments block
  58.      * @param epoch epoch of the data
  59.      * @param quaternionBlock quaternion logical block (may be null in ADM V2 or later)
  60.      * @param eulerBlock Euler angles logicial block (may be null)
  61.      * @param angularVelocityBlock angular velocity block (may be null)
  62.      * @param spinStabilizedBlock spin-stabilized logical block (may be null)
  63.      * @param inertia inertia logical block (may be null)
  64.      */
  65.     public ApmData(final CommentsContainer commentsBlock,
  66.                    final AbsoluteDate epoch,
  67.                    final ApmQuaternion quaternionBlock,
  68.                    final Euler eulerBlock,
  69.                    final AngularVelocity angularVelocityBlock,
  70.                    final SpinStabilized spinStabilizedBlock,
  71.                    final Inertia inertia) {
  72.         this.commentsBlock        = commentsBlock;
  73.         this.epoch                = epoch;
  74.         this.quaternionBlock      = quaternionBlock;
  75.         this.eulerBlock           = eulerBlock;
  76.         this.angularVelocityBlock = angularVelocityBlock;
  77.         this.spinStabilizedBlock  = spinStabilizedBlock;
  78.         this.inertia              = inertia;
  79.         this.maneuvers            = new ArrayList<>();
  80.     }

  81.     /** {@inheritDoc} */
  82.     @Override
  83.     public void validate(final double version) {

  84.         if (version < 2.0) {
  85.             // quaternion block is mandatory in ADM V1
  86.             if (quaternionBlock == null) {
  87.                 // generate a dummy entry just for triggering the exception
  88.                 new ApmQuaternion().validate(version);
  89.             }
  90.         } else {
  91.             // at least one logical block is mandatory in ADM V2
  92.             if (quaternionBlock == null && eulerBlock == null && angularVelocityBlock == null &&
  93.                 spinStabilizedBlock == null && inertia == null) {
  94.                 throw new OrekitException(OrekitMessages.CCSDS_INCOMPLETE_DATA);
  95.             }
  96.         }

  97.         if (quaternionBlock != null) {
  98.             quaternionBlock.validate(version);
  99.         }
  100.         if (eulerBlock != null) {
  101.             eulerBlock.validate(version);
  102.         }
  103.         if (angularVelocityBlock != null) {
  104.             angularVelocityBlock.validate(version);
  105.         }
  106.         if (spinStabilizedBlock != null) {
  107.             spinStabilizedBlock.validate(version);
  108.         }
  109.         if (inertia != null) {
  110.             inertia.validate(version);
  111.         }
  112.         for (final Maneuver maneuver : maneuvers) {
  113.             maneuver.validate(version);
  114.         }

  115.     }

  116.     /** Get the comments.
  117.      * @return comments
  118.      */
  119.     public List<String> getComments() {
  120.         return commentsBlock.getComments();
  121.     }

  122.     /**
  123.      * Get the epoch of the data.
  124.      * @return epoch the epoch
  125.      * @since 12.0
  126.      */
  127.     public AbsoluteDate getEpoch() {
  128.         return epoch;
  129.     }

  130.     /** Get the quaternion logical block.
  131.      * @return quaternion block
  132.      */
  133.     public ApmQuaternion getQuaternionBlock() {
  134.         return quaternionBlock;
  135.     }

  136.     /** Get the Euler angles logical block.
  137.      * @return Euler angles block (may be null)
  138.      */
  139.     public Euler getEulerBlock() {
  140.         return eulerBlock;
  141.     }

  142.     /** Get the angular velocity logical block.
  143.      * @return angular velocity block (may be null)
  144.      * @since 12.0
  145.      */
  146.     public AngularVelocity getAngularVelocityBlock() {
  147.         return angularVelocityBlock;
  148.     }

  149.     /** Get the spin-stabilized logical block.
  150.      * @return spin-stabilized block (may be null)
  151.      */
  152.     public SpinStabilized getSpinStabilizedBlock() {
  153.         return spinStabilizedBlock;
  154.     }

  155.     /** Get the inertia logical block.
  156.      * @return inertia block (may be null)
  157.      */
  158.     public Inertia getInertiaBlock() {
  159.         return inertia;
  160.     }

  161.     /**
  162.      * Get the number of maneuvers present in the APM.
  163.      * @return the number of maneuvers
  164.      */
  165.     public int getNbManeuvers() {
  166.         return maneuvers.size();
  167.     }

  168.     /**
  169.      * Get a list of all maneuvers.
  170.      * @return unmodifiable list of all maneuvers.
  171.      */
  172.     public List<Maneuver> getManeuvers() {
  173.         return Collections.unmodifiableList(maneuvers);
  174.     }

  175.     /**
  176.      * Get a maneuver.
  177.      * @param index maneuver index, counting from 0
  178.      * @return maneuver
  179.      */
  180.     public Maneuver getManeuver(final int index) {
  181.         return maneuvers.get(index);
  182.     }

  183.     /**
  184.      * Add a maneuver.
  185.      * @param maneuver maneuver to be set
  186.      */
  187.     public void addManeuver(final Maneuver maneuver) {
  188.         maneuvers.add(maneuver);
  189.     }

  190.     /**
  191.      * Get boolean testing whether the APM contains at least one maneuver.
  192.      * @return true if APM contains at least one maneuver
  193.      *         false otherwise
  194.      */
  195.     public boolean hasManeuvers() {
  196.         return !maneuvers.isEmpty();
  197.     }

  198.     /** Get the attitude.
  199.      * @param frame reference frame with respect to which attitude must be defined,
  200.      * (may be null if attitude is <em>not</em> orbit-relative and one wants
  201.      * attitude in the same frame as used in the attitude message)
  202.      * @param pvProvider provider for spacecraft position and velocity
  203.      * (may be null if attitude is <em>not</em> orbit-relative)
  204.      * @return attitude
  205.      * @since 12.0
  206.      */
  207.     public Attitude getAttitude(final Frame frame, final PVCoordinatesProvider pvProvider) {

  208.         if (quaternionBlock != null) {
  209.             // we have a quaternion
  210.             final Quaternion q = quaternionBlock.getQuaternion();

  211.             final TimeStampedAngularCoordinates tac;
  212.             if (quaternionBlock.hasRates()) {
  213.                 // quaternion logical block includes everything we need
  214.                 final Quaternion qDot = quaternionBlock.getQuaternionDot();
  215.                 tac = AttitudeType.QUATERNION_DERIVATIVE.build(true, quaternionBlock.getEndpoints().isExternal2SpacecraftBody(),
  216.                                                                null, true, epoch,
  217.                                                                q.getQ0(), q.getQ1(), q.getQ2(), q.getQ3(),
  218.                                                                qDot.getQ0(), qDot.getQ1(), qDot.getQ2(), qDot.getQ3());
  219.             } else if (angularVelocityBlock != null) {
  220.                 // get derivatives from the angular velocity logical block
  221.                 tac = AttitudeType.QUATERNION_ANGVEL.build(true,
  222.                                                            quaternionBlock.getEndpoints().isExternal2SpacecraftBody(),
  223.                                                            null, true, epoch,
  224.                                                            q.getQ0(), q.getQ1(), q.getQ2(), q.getQ3(),
  225.                                                            angularVelocityBlock.getAngVelX(),
  226.                                                            angularVelocityBlock.getAngVelY(),
  227.                                                            angularVelocityBlock.getAngVelZ());
  228.             } else if (eulerBlock != null && eulerBlock.hasRates()) {
  229.                 // get derivatives from the Euler logical block
  230.                 final double[] rates = eulerBlock.getRotationRates();
  231.                 if (eulerBlock.hasAngles()) {
  232.                     // the Euler block has everything we need
  233.                     final double[] angles = eulerBlock.getRotationAngles();
  234.                     tac = AttitudeType.EULER_ANGLE_DERIVATIVE.build(true,
  235.                                                                     eulerBlock.getEndpoints().isExternal2SpacecraftBody(),
  236.                                                                     eulerBlock.getEulerRotSeq(), eulerBlock.isSpacecraftBodyRate(), epoch,
  237.                                                                     angles[0], angles[1], angles[2],
  238.                                                                     rates[0], rates[1], rates[2]);
  239.                 } else {
  240.                     // the Euler block has only the rates (we are certainly using an ADM V1 message)
  241.                     // we need to rebuild the rotation from the quaternion
  242.                     tac = AttitudeType.QUATERNION_EULER_RATES.build(true,
  243.                                                                     eulerBlock.getEndpoints().isExternal2SpacecraftBody(),
  244.                                                                     eulerBlock.getEulerRotSeq(), eulerBlock.isSpacecraftBodyRate(), epoch,
  245.                                                                     q.getQ0(), q.getQ1(), q.getQ2(), q.getQ3(),
  246.                                                                     rates[0], rates[1], rates[2]);
  247.                 }

  248.             } else {
  249.                 // we rely only on the quaternion logical block, despite it doesn't include rates
  250.                 tac = AttitudeType.QUATERNION.build(true, quaternionBlock.getEndpoints().isExternal2SpacecraftBody(),
  251.                                                     null, true, epoch,
  252.                                                     q.getQ0(), q.getQ1(), q.getQ2(), q.getQ3());
  253.             }

  254.             // build the attitude
  255.             return quaternionBlock.getEndpoints().build(frame, pvProvider, tac);

  256.         } else if (eulerBlock != null) {
  257.             // we have Euler angles
  258.             final double[] angles = eulerBlock.getRotationAngles();

  259.             final TimeStampedAngularCoordinates tac;
  260.             if (eulerBlock.hasRates()) {
  261.                 // the Euler block has everything we need
  262.                 final double[] rates = eulerBlock.getRotationRates();
  263.                 tac = AttitudeType.EULER_ANGLE_DERIVATIVE.build(true,
  264.                                                                 eulerBlock.getEndpoints().isExternal2SpacecraftBody(),
  265.                                                                 eulerBlock.getEulerRotSeq(), eulerBlock.isSpacecraftBodyRate(), epoch,
  266.                                                                 angles[0], angles[1], angles[2],
  267.                                                                 rates[0], rates[1], rates[2]);
  268.             } else if (angularVelocityBlock != null) {
  269.                 // get derivatives from the angular velocity logical block
  270.                 tac = AttitudeType.EULER_ANGLE_ANGVEL.build(true,
  271.                                                             eulerBlock.getEndpoints().isExternal2SpacecraftBody(),
  272.                                                             eulerBlock.getEulerRotSeq(), eulerBlock.isSpacecraftBodyRate(), epoch,
  273.                                                             angles[0], angles[1], angles[2],
  274.                                                             angularVelocityBlock.getAngVelX(),
  275.                                                             angularVelocityBlock.getAngVelY(),
  276.                                                             angularVelocityBlock.getAngVelZ());
  277.             } else {
  278.                 // we rely only on the Euler logical block, despite it doesn't include rates
  279.                 tac = AttitudeType.EULER_ANGLE.build(true,
  280.                                                      eulerBlock.getEndpoints().isExternal2SpacecraftBody(),
  281.                                                      eulerBlock.getEulerRotSeq(), eulerBlock.isSpacecraftBodyRate(), epoch,
  282.                                                      angles[0], angles[1], angles[2]);
  283.             }

  284.             // build the attitude
  285.             return eulerBlock.getEndpoints().build(frame, pvProvider, tac);

  286.         } else if (spinStabilizedBlock != null) {
  287.             // we have a spin block

  288.             final TimeStampedAngularCoordinates tac;
  289.             if (spinStabilizedBlock.hasNutation()) {
  290.                 // we rely only on nutation
  291.                 tac = AttitudeType.SPIN_NUTATION.build(true, true, null, true, epoch,
  292.                                                        spinStabilizedBlock.getSpinAlpha(),
  293.                                                        spinStabilizedBlock.getSpinDelta(),
  294.                                                        spinStabilizedBlock.getSpinAngle(),
  295.                                                        spinStabilizedBlock.getSpinAngleVel(),
  296.                                                        spinStabilizedBlock.getNutation(),
  297.                                                        spinStabilizedBlock.getNutationPeriod(),
  298.                                                        spinStabilizedBlock.getNutationPhase());
  299.             } else if (spinStabilizedBlock.hasMomentum()) {
  300.                 // we rely only on momentum
  301.                 tac = AttitudeType.SPIN_NUTATION_MOMENTUM.build(true, true, null, true, epoch,
  302.                                                                 spinStabilizedBlock.getSpinAlpha(),
  303.                                                                 spinStabilizedBlock.getSpinDelta(),
  304.                                                                 spinStabilizedBlock.getSpinAngle(),
  305.                                                                 spinStabilizedBlock.getSpinAngleVel(),
  306.                                                                 spinStabilizedBlock.getMomentumAlpha(),
  307.                                                                 spinStabilizedBlock.getMomentumDelta(),
  308.                                                                 spinStabilizedBlock.getNutationVel());
  309.             } else {
  310.                 // we rely only on the spin logical block, despite it doesn't include rates
  311.                 tac = AttitudeType.SPIN.build(true, true, null, true, epoch,
  312.                                               spinStabilizedBlock.getSpinAlpha(),
  313.                                               spinStabilizedBlock.getSpinDelta(),
  314.                                               spinStabilizedBlock.getSpinAngle(),
  315.                                               spinStabilizedBlock.getSpinAngleVel());
  316.             }

  317.             // build the attitude
  318.             return spinStabilizedBlock.getEndpoints().build(frame, pvProvider, tac);

  319.         } else {
  320.             throw new OrekitException(OrekitMessages.CCSDS_INCOMPLETE_DATA);
  321.         }

  322.     }

  323. }