ManeuverFieldType.java

  1. /* Copyright 2002-2025 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.odm.ocm;

  18. import org.hipparchus.util.Precision;
  19. import org.orekit.errors.OrekitException;
  20. import org.orekit.errors.OrekitMessages;
  21. import org.orekit.files.ccsds.definitions.OnOff;
  22. import org.orekit.files.ccsds.definitions.TimeConverter;
  23. import org.orekit.files.ccsds.utils.ContextBinding;
  24. import org.orekit.time.DateTimeComponents;
  25. import org.orekit.utils.AccurateFormatter;
  26. import org.orekit.utils.Formatter;
  27. import org.orekit.utils.units.Unit;

  28. /** Maneuver field type used in CCSDS {@link Ocm Orbit Comprehensive Messages}.
  29.  * @author Luc Maisonobe
  30.  * @since 11.0
  31.  */
  32. public enum ManeuverFieldType {

  33.     // CHECKSTYLE: stop MultipleStringLiterals check

  34.     /** Absolute epoch time. */
  35.     TIME_ABSOLUTE("n/a",
  36.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setDate(context.getTimeSystem().getConverter(context).parse(field)),
  37.         (u, converter, maneuver, formatter) -> {
  38.             final DateTimeComponents dt = converter.components(maneuver.getDate());
  39.             return formatter.toString(dt.getDate().getYear(), dt.getDate().getMonth(), dt.getDate().getDay(),
  40.                     dt.getTime().getHour(), dt.getTime().getMinute(), dt.getTime().getSecond());
  41.         }),

  42.     /** Relative epoch time. */
  43.     TIME_RELATIVE("s",
  44.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setDate(context.getReferenceDate().shiftedBy(toSI(field, u))),
  45.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(converter.offset(maneuver.getDate())))),

  46.     /** Maneuver duration. */
  47.     MAN_DURA("s",
  48.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setDuration(toSI(field, u)),
  49.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getDuration()))),

  50.     /** Mass change. */
  51.     DELTA_MASS("kg",
  52.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setDeltaMass(toSI(field, u)),
  53.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getDeltaMass()))),

  54.     /** Acceleration along X axis. */
  55.     ACC_X("km/s²",
  56.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setAcceleration(0, toSI(field, u)),
  57.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getAcceleration().getX()))),

  58.     /** Acceleration along Y axis. */
  59.     ACC_Y("km/s²",
  60.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setAcceleration(1, toSI(field, u)),
  61.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getAcceleration().getY()))),

  62.     /** Acceleration along Z axis. */
  63.     ACC_Z("km/s²",
  64.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setAcceleration(2, toSI(field, u)),
  65.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getAcceleration().getZ()))),

  66.     /** Interpolation mode between current and next acceleration line. */
  67.     ACC_INTERP("n/a",
  68.         (field, u, context, maneuver, lineNumber, fileName) -> {
  69.             try {
  70.                 maneuver.setAccelerationInterpolation(OnOff.valueOf(field));
  71.             } catch (IllegalArgumentException iae) {
  72.                 throw new OrekitException(OrekitMessages.CCSDS_UNEXPECTED_KEYWORD,
  73.                                           lineNumber, fileName, field);
  74.             }
  75.         },
  76.         (u, converter, maneuver, formatter) -> maneuver.getAccelerationInterpolation().name()),

  77.     /** One σ percent error on acceleration magnitude. */
  78.     ACC_MAG_SIGMA("%",
  79.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setAccelerationMagnitudeSigma(toSI(field, u)),
  80.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getAccelerationMagnitudeSigma()))),

  81.     /** One σ off-nominal acceleration direction. */
  82.     ACC_DIR_SIGMA("°",
  83.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setAccelerationDirectionSigma(toSI(field, u)),
  84.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getAccelerationDirectionSigma()))),

  85.     /** Velocity increment along X axis. */
  86.     DV_X("km/s",
  87.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setDv(0, toSI(field, u)),
  88.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getDv().getX()))),

  89.     /** Velocity increment along Y axis. */
  90.     DV_Y("km/s",
  91.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setDv(1, toSI(field, u)),
  92.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getDv().getY()))),

  93.     /** Velocity increment along Z axis. */
  94.     DV_Z("km/s",
  95.         (field, u, context, maneuver, lineNumber, fileName) ->  maneuver.setDv(2, toSI(field, u)),
  96.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getDv().getZ()))),

  97.     /** One σ percent error on ΔV magnitude. */
  98.     DV_MAG_SIGMA("%",
  99.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setDvMagSigma(toSI(field, u)),
  100.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getDvMagSigma()))),

  101.     /** One σ angular off-nominal ΔV direction. */
  102.     DV_DIR_SIGMA("°",
  103.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setDvDirSigma(toSI(field, u)),
  104.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getDvDirSigma()))),

  105.     /** Thrust component along X axis. */
  106.     THR_X("N",
  107.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setThrust(0, toSI(field, u)),
  108.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getThrust().getX()))),

  109.     /** Thrust component along Y axis. */
  110.     THR_Y("N",
  111.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setThrust(1, toSI(field, u)),
  112.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getThrust().getY()))),

  113.     /** Thrust component along Z axis. */
  114.     THR_Z("N",
  115.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setThrust(2, toSI(field, u)),
  116.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getThrust().getZ()))),

  117.     /** Thrust efficiency η typically between 0.0 and 1.0. */
  118.     THR_EFFIC("n/a",
  119.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setThrustEfficiency(toSI(field, u)),
  120.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getThrustEfficiency()))),

  121.     /** Interpolation mode between current and next acceleration line. */
  122.     THR_INTERP("n/a",
  123.         (field, u, context, maneuver, lineNumber, fileName) -> {
  124.             try {
  125.                 maneuver.setThrustInterpolation(OnOff.valueOf(field));
  126.             } catch (IllegalArgumentException iae) {
  127.                 throw new OrekitException(OrekitMessages.CCSDS_UNEXPECTED_KEYWORD,
  128.                                           lineNumber, fileName, field);
  129.             }
  130.         },
  131.         (u, converter, maneuver, formatter) -> maneuver.getThrustInterpolation().name()),

  132.     /** Thrust specific impulse. */
  133.     THR_ISP("s",
  134.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setThrustIsp(toSI(field, u)),
  135.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getThrustIsp()))),

  136.     /** One σ percent error on thrust magnitude. */
  137.     THR_MAG_SIGMA("%",
  138.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setThrustMagnitudeSigma(toSI(field, u)),
  139.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getThrustMagnitudeSigma()))),

  140.     /** One σ angular off-nominal thrust direction. */
  141.     THR_DIR_SIGMA("°",
  142.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setThrustDirectionSigma(toSI(field, u)),
  143.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getThrustDirectionSigma()))),

  144.     /** Identifier of resulting "child" object deployed from this host. */
  145.     DEPLOY_ID("n/a",
  146.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setDeployId(field),
  147.         (u, converter, maneuver, formatter) -> maneuver.getDeployId()),

  148.     /** Velocity increment of deployed "child" object along X axis. */
  149.     DEPLOY_DV_X("km/s",
  150.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setDeployDv(0, toSI(field, u)),
  151.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getDeployDv().getX()))),

  152.     /** Velocity increment of deployed "child" object along Y axis. */
  153.     DEPLOY_DV_Y("km/s",
  154.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setDeployDv(1, toSI(field, u)),
  155.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getDeployDv().getY()))),

  156.     /** Velocity increment of deployed "child" object along Z axis. */
  157.     DEPLOY_DV_Z("km/s",
  158.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setDeployDv(2, toSI(field, u)),
  159.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getDeployDv().getZ()))),

  160.     /** Decrement in host mass as a result of deployment (shall be ≤ 0). */
  161.     DEPLOY_MASS("kg",
  162.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setDeployMass(toSI(field, u)),
  163.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getDeployMass()))),

  164.     /** One σ percent error on deployment ΔV magnitude. */
  165.     DEPLOY_DV_SIGMA("%",
  166.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setDeployDvSigma(toSI(field, u)),
  167.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getDeployDvSigma()))),

  168.     /** One σ angular off-nominal deployment vector direction. */
  169.     DEPLOY_DIR_SIGMA("°",
  170.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setDeployDirSigma(toSI(field, u)),
  171.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getDeployDirSigma()))),

  172.     /** Ratio of child-to-host ΔV vectors. */
  173.     DEPLOY_DV_RATIO("n/a",
  174.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setDeployDvRatio(toSI(field, u)),
  175.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getDeployDvRatio()))),

  176.     /** Typical (50th percentile) product of drag coefficient times cross-sectional area of deployed "child" object. */
  177.     DEPLOY_DV_CDA("m²",
  178.         (field, u, context, maneuver, lineNumber, fileName) -> maneuver.setDeployDvCda(toSI(field, u)),
  179.         (u, converter, maneuver, formatter) -> formatter.toString(u.fromSI(maneuver.getDeployDvCda())));


  180.     // CHECKSTYLE: resume MultipleStringLiterals check

  181.     /** Elements units. */
  182.     private final Unit unit;

  183.     /** Processing method. */
  184.     private final transient FieldProcessor processor;

  185.     /** Writing method. */
  186.     private final transient FieldWriter writer;

  187.     /** Simple constructor.
  188.      * @param unitSpecifications field unit specifications
  189.      * @param processor field processing method
  190.      * @param writer field writing method
  191.      */
  192.     ManeuverFieldType(final String unitSpecifications, final FieldProcessor processor, final FieldWriter writer) {
  193.         this.unit      = Unit.parse(unitSpecifications);
  194.         this.processor = processor;
  195.         this.writer    = writer;
  196.     }

  197.     /** Get the field unit.
  198.      * @return field unit
  199.      */
  200.     public Unit getUnit() {
  201.         return unit;
  202.     }

  203.     /** Check if parsed unit is compatible with field type.
  204.      * @param parsedUnit unit to check
  205.      */
  206.     public void checkUnit(final Unit parsedUnit) {
  207.         if (unit == Unit.NONE ^ parsedUnit == Unit.NONE ||
  208.             !(unit.sameDimension(parsedUnit) && Precision.equals(unit.getScale(), parsedUnit.getScale(), 1))) {
  209.             throw new OrekitException(OrekitMessages.INCOMPATIBLE_UNITS,
  210.                                       unit.getName(), parsedUnit.getName());
  211.         }
  212.     }

  213.     /** Check if a field is a time field.
  214.      * @return true if field is a time field
  215.      */
  216.     public boolean isTime() {
  217.         return this == TIME_ABSOLUTE || this == TIME_RELATIVE;
  218.     }

  219.     /** Get the value in SI units corresponding to a field.
  220.      * @param field text field
  221.      * @param unit unit to use
  222.      * @return double value in SI units
  223.      */
  224.     private static double toSI(final String field, final Unit unit) {
  225.         return unit.toSI(Double.parseDouble(field));
  226.     }

  227.     /** Process one field.
  228.      * @param field field to process
  229.      * @param context context binding
  230.      * @param maneuver maneuver to fill
  231.      * @param lineNumber line number at which the field occurs
  232.      * @param fileName name of the file in which the field occurs
  233.      */
  234.     public void process(final String field, final ContextBinding context, final OrbitManeuver maneuver,
  235.                         final int lineNumber, final String fileName) {
  236.         processor.process(field, unit, context, maneuver, lineNumber, fileName);
  237.     }

  238.     /** Output one maneuver field.
  239.      * @param converter converter for dates
  240.      * @param maneuver maneuver containing the field to output
  241.      * @param formatter used format doubles and dates to strings
  242.      * @return output field
  243.      */
  244.     public String outputField(final TimeConverter converter, final OrbitManeuver maneuver, final Formatter formatter)  {
  245.         return writer.output(unit, converter, maneuver, formatter);
  246.     }

  247.     /** Output one maneuver field.
  248.      * @param converter converter for dates
  249.      * @param maneuver maneuver containing the field to output
  250.      * @return output field
  251.      * @deprecated since 13.0, because formatter should be specified. Use {@link ManeuverFieldType#outputField(TimeConverter, OrbitManeuver, Formatter)} instead.
  252.      */
  253.     @Deprecated
  254.     public String outputField(final TimeConverter converter, final OrbitManeuver maneuver) {
  255.         return writer.output(unit, converter, maneuver, new AccurateFormatter());
  256.     }

  257.     /** Interface for processing one field. */
  258.     interface FieldProcessor {
  259.         /** Process one field.
  260.          * @param field field to process
  261.          * @param unit unit to use
  262.          * @param context context binding
  263.          * @param maneuver maneuver to fill
  264.          * @param lineNumber line number at which the field occurs
  265.          * @param fileName name of the file in which the field occurs
  266.          */
  267.         void process(String field, Unit unit, ContextBinding context, OrbitManeuver maneuver,
  268.                      int lineNumber, String fileName);
  269.     }

  270.     /** Interface for writing one field based on formatting standards. */
  271.     interface FieldWriter {
  272.         /** Process one field.
  273.          * @param unit unit to use
  274.          * @param converter converter for dates
  275.          * @param maneuver maneuver containing the field to output
  276.          * @param formatter used to format dates and doubles to string
  277.          * @return output field
  278.          */
  279.         String output(Unit unit, TimeConverter converter, OrbitManeuver maneuver, Formatter formatter);
  280.     }

  281. }