diff --git a/pom.xml b/pom.xml
index 9d4170192c..9e44199cf4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,9 +5,9 @@
org.orekit
orekit
jar
- 12.1.2
+ 12.2
OREKIT
- http://www.orekit.org/
+ https://www.orekit.org/
2002
@@ -34,7 +34,7 @@
3.4.1
3.4.0
1.2
- 1.2024.5
+ 1.2024.7
3.3.1
3.12.1
3.6.0
@@ -55,8 +55,8 @@
<script type="text/x-mathjax-config">MathJax.Hub.Config({ TeX: { extensions: ["autoload.js"]}});</script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS_CHTML"></script>
3.1
- 5.10.0
- 2.2
+ 5.11.2
+ 3.0
1.8
1.8
${git.revision}; ${maven.build.timestamp}
@@ -222,6 +222,9 @@
Louis Aucouturier
+
+ Rafael Ayala
+
Lucian Bărbulescu
@@ -273,6 +276,9 @@
Alberto Fossà
+
+ Laura Garcia
+
Dorian Gegout
@@ -476,7 +482,7 @@
org.junit.jupiter
junit-jupiter-params
- 5.10.0
+ ${orekit.junit.version}
test
diff --git a/spotbugs-exclude-filter.xml b/spotbugs-exclude-filter.xml
index 4baff30e5d..b75a194938 100644
--- a/spotbugs-exclude-filter.xml
+++ b/spotbugs-exclude-filter.xml
@@ -197,6 +197,10 @@
+
+
+
+
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 6766bfddad..3d94754521 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -20,6 +20,201 @@
Orekit Changes
+
+
+ Add init methods in LightFluxModel.
+
+
+ Updated conference paper URL in JB2008 class documentation.
+
+
+ Fixed wrong interpolation of clock models when parsing
+ SP3 files with default clock entries.
+
+
+ Add generic conversion routines for position angles.
+
+
+ Add addImpulseManeuver to relevant propagator builder.
+
+
+ Add abstract class for solar flux model with spherical body.
+
+
+ Fix crash of EKF whithout orbital parameters and at least one propagation parameter to estimate.
+
+
+ Override methods in (Field) static transform identity.
+
+
+ Override more methods in (Field) transform identity.
+
+
+ Deprecate getBody in AbstractBodyAttraction.
+
+
+ Fixed position accuracy in spliced SP3 files.
+
+
+ Add light flux model for spherical occulting and occulted bodies.
+
+
+ Add scales for defect in indirect shooting.
+
+
+ Make easier implementation of user-defined adjoint.
+
+
+ Add (Field)ResetDerivativesOnEvent.
+
+
+ Add adaptive step integration for indirect shooting.
+
+
+ Add Cartesian covariance conversions.
+
+
+ Use (Field)EventDetectionSettings in cylindrical shadow.
+
+
+ Deprecated a few methods in (Field)AbstractDetector to increase use of (Field)EventDetectionSettings.
+
+
+ Add toGeodeticPoint in FieldGeodeticPoint.
+
+
+ Pass propagation frame as argument when evaluating adjoint contribution.
+
+
+ Add evaluation of Hamiltonian in adjoint dynamics.
+
+
+ Add direct access to lighting ratio from state in AbstractLightFluxModel.
+
+
+ Additional protection for zenith line-of-sight in NeQuick-G model.
+
+
+ Add constructor with dV error in adaptive step integrator builder.
+
+
+ Add adjoint dynamics for single absolute attraction.
+
+
+ Add non-orbit build in (Field)IntegratorBuilder.
+
+
+ Add (Field)EventDetectionSettings class.
+
+
+ Added AlignedAndConstrained attitude mode.
+
+
+ Add adjoint dynamics for inertial force.
+
+
+ Add adjoint dynamics for third body force.
+
+
+ Added getOneLetterCode and getTwoLettersCode to TimeSystem.
+
+
+ Protected AmbiguityCache against concurrent modifications.
+
+
+ Add indirect single shooting method for fixed time fixed boundaries with Cartesian coordinates.
+
+
+ Add bounded energy cost for Cartesian adjoint dynamics.
+
+
+ Create Jacobian of (Field)KinematicTransform and use it in (Field)StateCovariance.
+
+
+ Add adjoint dynamics from J2 term.
+
+
+ Add unbounded energy cost for Cartesian adjoint dynamics.
+
+
+ Add finish method in (Field)EventHandler.
+
+
+ Add analytical solar ephemerides.
+
+
+ Add indirect control package with Cartesian adjoint dynamics for Keplerian motion.
+
+
+ J2-squared model can now implement their own short period model.
+
+
+ Added getter for switches in AttitudesSequence.
+
+
+ PropagatorBuilder are now Cloneable.
+
+
+ Added a parser for gravity models in SHA format.
+
+
+ Add more overrides of getAttitudeRotation.
+
+
+ Use proper body-fixed frame in DSSTZonal and refactored DSSTGravityContext.
+
+
+ Fix negative eccentricity issue with Brouwer-Lyddane propagator.
+
+
+
+
+ Removed non thread-safe use of DecimalFormat.
+
+
+ Pass Status in UKF theoretical measurement.
+
+
+ Fixed mixed up frames in inter-satellites measurements.
+
+
+ Protected several maps against concurrent modifications.
+
+
+ Fixed NeQuick ionospheric model for perfect zenith observation.
+
+
+ Fixed introduced noises when changing covariance frame with identical frame.
+
+
+ Fixed wrong agency name length in Rinex observation writer.
+
+
+ Greatly reduced computation time of NeQuick ionospheric model.
+
+
+ Added getOneLetterCode and getTwoLettersCode to TimeSystem.
+
+
+ Protected AmbiguityCache against concurrent modifications.
+
+
+ Fixed checkstyle error in SolarRadiationPressure.
+
+
+ Fixed update of initial state's prop type after a propagation in DSST.
+
+
Updated release guide according to new sonatype token generation process.
+
+ Added method getMJD and getJD in AbsoluteDate.
+
+
+ Add finish method in (Field)EventDetector and call it in propagators.
+
- Fixed ignored coordinates system in SP3 parser.
+ Fixed ignored coordinates system in SP3 parser.
Add method to create constant (Field)AdaptableInterval.
@@ -311,241 +512,241 @@
Added {Field}ClockOffset.
- Added J2-only ForceModel for performance.
+ Added J2-only ForceModel for performance.
- Fixed hasNonKeplerianAcceleration in FieldOrbit and improved performance.
+ Fixed hasNonKeplerianAcceleration in FieldOrbit and improved performance.
- Deprecated InterSatellitesPhaseAmbiguityModifier, OneWayGNSSPhaseAmbiguityModifier
- and PhaseAmbiguityModifier.
+ Deprecated InterSatellitesPhaseAmbiguityModifier, OneWayGNSSPhaseAmbiguityModifier
+ and PhaseAmbiguityModifier.
- Added AmbiguityDriver and AmbiguityCache.
+ Added AmbiguityDriver and AmbiguityCache.
- Allow generation of Rinex observation files in receiver clock time scale.
+ Allow generation of Rinex observation files in receiver clock time scale.
- Added ClockTimeScale.
+ Added ClockTimeScale.
- {get|set}ClkOffset → {get|set}ClockOffsetApplied.
+ {get|set}ClkOffset → {get|set}ClockOffsetApplied.
- Added getBodyName in body-based attraction models, as well as common abstract class.
+ Added getBodyName in body-based attraction models, as well as common abstract class.
- Added constructors with orbit type in SmallManeuverAnalyticalModel.
+ Added constructors with orbit type in SmallManeuverAnalyticalModel.
- Added get(Un)normalizedC20.
+ Added get(Un)normalizedC20.
- Added separate access to original estimation and modifications in estimated measurements.
+ Added separate access to original estimation and modifications in estimated measurements.
- Added InterSatellitesOneWayRangeRate measurements.
+ Added InterSatellitesOneWayRangeRate measurements.
- Added constant and quadratic clock models for GNSS measurements.
+ Added constant and quadratic clock models for GNSS measurements.
- Removed redundant code to create FieldOrbit from Orbit.
+ Removed redundant code to create FieldOrbit from Orbit.
- Improved performance with PositionAngleType in (Field)NumericalPropagator.
+ Improved performance with PositionAngleType in (Field)NumericalPropagator.
- Removed unnecessary calls to (Field)Transform in (Field)ShortTermEncounter2DDefinition.
+ Removed unnecessary calls to (Field)Transform in (Field)ShortTermEncounter2DDefinition.
- Added cache for position angle in FieldOrbit when applicable.
+ Added cache for position angle in FieldOrbit when applicable.
- Manage clock offset as an additional state in propagators built from SP3 files.
+ Manage clock offset as an additional state in propagators built from SP3 files.
- Added TimeStampedDoubleAndDerivative and associated interpolator.
+ Added TimeStampedDoubleAndDerivative and associated interpolator.
- Allow direction-dependent phase centers in inter-satellites measurements.
+ Allow direction-dependent phase centers in inter-satellites measurements.
- Create two new EventDetector: LatitudeRangeCrossingDetector and LongitudeRangeCrossingDetector.
+ Create two new EventDetector: LatitudeRangeCrossingDetector and LongitudeRangeCrossingDetector.
- Added cache for position angle in Orbit when applicable.
+ Added cache for position angle in Orbit when applicable.
- Implement resetIntermediateState in (Field)TLEPropagator.
+ Implement resetIntermediateState in (Field)TLEPropagator.
- Add default implementation of getPosition in (Field)Propagator.
+ Add default implementation of getPosition in (Field)Propagator.
- Add (Field)KinematicTransform.
+ Add (Field)KinematicTransform.
- Added support for Walker constellations, including in-orbit spares with shifted position.
+ Added support for Walker constellations, including in-orbit spares with shifted position.
- Moved getFieldDate up from FieldTransform to FieldStaticTransform.
+ Moved getFieldDate up from FieldTransform to FieldStaticTransform.
- Added getStaticInverse to (Field)StaticTransform.
+ Added getStaticInverse to (Field)StaticTransform.
- Make access to raw albedo and infrared radiation pressure in KnockeRediffusedForceModel public.
+ Make access to raw albedo and infrared radiation pressure in KnockeRediffusedForceModel public.
- Reduce code duplication in (Field)Propagator inheritors.
+ Reduce code duplication in (Field)Propagator inheritors.
- Take azimuthal asymmetry into account in Vienna tropospheric models.
+ Take azimuthal asymmetry into account in Vienna tropospheric models.
- Added GlobalPressureTemperature3 model.
+ Added GlobalPressureTemperature3 model.
- Added GlobalPressureTemperature2w (i.e. wet) model.
+ Added GlobalPressureTemperature2w (i.e. wet) model.
- Allow to use any GPT grid file (GPT2, GPT2w, GPT3) in GlobalPressureTemperature2 model.
+ Allow to use any GPT grid file (GPT2, GPT2w, GPT3) in GlobalPressureTemperature2 model.
- Added providers for horizontal gradient.
+ Added providers for horizontal gradient.
- Added Askne-Nordius tropospheric model.
+ Added Askne-Nordius tropospheric model.
- Replaced Vienna{One|Three}Model by Vienna{One|Three}.
+ Replaced Vienna{One|Three}Model by Vienna{One|Three}.
- Added ConstantTroposphericModel.
+ Added ConstantTroposphericModel.
- Added ChaoMappingFunction for tropospheric mapping function.
+ Added ChaoMappingFunction for tropospheric mapping function.
- Added ModifiedHopfieldModel for tropospheric delay.
+ Added ModifiedHopfieldModel for tropospheric delay.
- Added CanonicalSaastamoinenModel for tropospheric delay.
+ Added CanonicalSaastamoinenModel for tropospheric delay.
- Replaced SaastamoinenModel by ModifiedSaastamoinenModel for tropospheric delay.
+ Replaced SaastamoinenModel by ModifiedSaastamoinenModel for tropospheric delay.
- Added NBS/NRC steam table model for water vapor pressure.
+ Added NBS/NRC steam table model for water vapor pressure.
- Added Wang1988 model for water vapor pressure.
+ Added Wang1988 model for water vapor pressure.
- Added CIPM2007 model for water vapor pressure.
+ Added CIPM2007 model for water vapor pressure.
- Added WaterVaporPressureProvider interface.
+ Added WaterVaporPressureProvider interface.
- Added HeightDependentPressureTemperatureHumidityConverter for converting weather parameters.
+ Added HeightDependentPressureTemperatureHumidityConverter for converting weather parameters.
- Replaced WeatherModel by {Field}PressureTemperatureHumidityProvider.
+ Replaced WeatherModel by {Field}PressureTemperatureHumidityProvider.
- Added {Field}PressureTemperature and {Field}PressureTemperatureHumidity containers.
+ Added {Field}PressureTemperature and {Field}PressureTemperatureHumidity containers.
- Replaced GlobalPressureTemperature2Model by GlobalPressureTemperature2.
+ Replaced GlobalPressureTemperature2Model by GlobalPressureTemperature2.
- Replaced GlobalPressureTemperatureModel by GlobalPressureTemperature.
+ Replaced GlobalPressureTemperatureModel by GlobalPressureTemperature.
- Replaced MappingFunction by TroposphereMappingFunction.
+ Replaced MappingFunction by TroposphereMappingFunction.
- Replaced EstimatedTroposphericModel by EstimatedModel.
+ Replaced EstimatedTroposphericModel by EstimatedModel.
- Replaced DiscreteTroposphericModel by TroposphericModel.
+ Replaced DiscreteTroposphericModel by TroposphericModel.
- Added support for Intelsat's 11 elements propagation.
+ Added support for Intelsat's 11 elements propagation.
- Added NsgfV00Filter to allow parsing some wrong SP3 files.
+ Added NsgfV00Filter to allow parsing some wrong SP3 files.
- Started using new square method for Field.
+ Started using new square method for Field.
- Fixed parsing of SP3 files with partly missing standard deviations.
+ Fixed parsing of SP3 files with partly missing standard deviations.
- Added field versions of unit conversions from and to SI units.
+ Added field versions of unit conversions from and to SI units.
- Removed uses of scalar multiply on Field one.
+ Removed uses of scalar multiply on Field one.
- Added utility classes for position angle conversions for (Field) CircularOrbit and EquinoctialOrbit.
+ Added utility classes for position angle conversions for (Field) CircularOrbit and EquinoctialOrbit.
- Fixed exceptions occurring in EOP prediction with ill chosen fitting parameters.
+ Fixed exceptions occurring in EOP prediction with ill chosen fitting parameters.
- Added translation of error messages in Catalan language.
+ Added translation of error messages in Catalan language.
- Added EventDetector implementations for detecting beta angle crossing events.
+ Added EventDetector implementations for detecting beta angle crossing events.
-
-
- Change visibility of InertiaAxis and Inertia constructors to public.
+ Change visibility of InertiaAxis and Inertia constructors to public.
- Allow Rinex V4 observation files to have either "ANTENNA: DELTA X/Y/Z"
- or "ANTENNA: DELTA H/E/N" header line.
+ Allow Rinex V4 observation files to have either "ANTENNA: DELTA X/Y/Z"
+ or "ANTENNA: DELTA H/E/N" header line.
- Field versions of Frame.getStaticTransformTo don't allow
- null dates (they never did, but the javadoc wrongly stated this was allowed).
+ Field versions of Frame.getStaticTransformTo don't allow
+ null dates (they never did, but the javadoc wrongly stated this was allowed).
- Removed blank lines in SP3 file generation.
+ Removed blank lines in SP3 file generation.
- Fixed forbidden SBAS System Time in SP3 files.
+ Fixed forbidden SBAS System Time in SP3 files.
- Fixed wrong key for Beidou System Time in SP3 files.
+ Fixed wrong key for Beidou System Time in SP3 files.
- Fixed wrong parsing of some time systems in SP3 files.
+ Fixed wrong parsing of some time systems in SP3 files.
- Fixed incorrect transmitter location in BistaticRange measurement.
+ Fixed incorrect transmitter location in BistaticRange measurement.
- Fix regression in Ephemeris with interpolationPoints=1.
+ Fix regression in Ephemeris with interpolationPoints=1.
- Fixed loading of UTC (now thread safe).
+ Fixed loading of UTC (now thread safe).
- Fix DSST Jacobian setup.
+ Fix DSST Jacobian setup.
-
-
@@ -585,9 +786,9 @@
Fixed bad caching of the ocean tides model.
-
-
- Added new method addSupportedParameters in AbstractPropagatorBuilder.
+ Added new method addSupportedParameters in AbstractPropagatorBuilder.
- Added Gauss angles-only initial orbit determination method.
+ Added Gauss angles-only initial orbit determination method.
- Enhanced parsing of CRD files.
+ Enhanced parsing of CRD files.
- Limit use of synchronization in LazyLoadedTimeScales.
+ Limit use of synchronization in LazyLoadedTimeScales.
- Removed unused static variables in DTM2000.
+ Removed unused static variables in DTM2000.
- Changed "absPva == null" to "isOrbitDefined()" in (Field)SpacecraftState.
+ Changed "absPva == null" to "isOrbitDefined()" in (Field)SpacecraftState.
- Add toStaticTransform to (Field)SpacecraftState.
+ Add toStaticTransform to (Field)SpacecraftState.
- Introduce individual methods for tracking coordinates in TopocentricFrame.
+ Introduce individual methods for tracking coordinates in TopocentricFrame.
- Allow loading IONEX files from DataSource for Global Ionosphere Model.
+ Allow loading IONEX files from DataSource for Global Ionosphere Model.
- Use Ionospĥere Pierce Point in Global Ionosphere Model.
+ Use Ionospĥere Pierce Point in Global Ionosphere Model.
- Added getCartesianPoint to TopocentricFrame.
+ Added getCartesianPoint to TopocentricFrame.
- Fixed wrong uses of AbsoluteDate for Field transformations in Atmosphere and FieldElevationDetector.
+ Fixed wrong uses of AbsoluteDate for Field transformations in Atmosphere and FieldElevationDetector.
- Add toStaticTransform to (Field)Transform.
+ Add toStaticTransform to (Field)Transform.
- Added derivatives to EOP when they are missing in the files.
+ Added derivatives to EOP when they are missing in the files.
- Allow customization of interpolation degree in EOP.
+ Allow customization of interpolation degree in EOP.
- Added support for XML and csv versions of bulletin A, bulletin B and EOP C04.
+ Added support for XML and csv versions of bulletin A, bulletin B and EOP C04.
- Set InterSatVisibilityDetector global constructor from private to protected.
+ Set InterSatVisibilityDetector global constructor from private to protected.
- Fix bug on buildBox constructor coefficients order.
+ Fix bug on buildBox constructor coefficients order.
- Adding {Field}LongitudeCrossingDetector.
+ Adding {Field}LongitudeCrossingDetector.
- Added support for CCSDS/SANA geodetic orbital elements.
+ Added support for CCSDS/SANA geodetic orbital elements.
- Added method to create new instance without rates from position-angled based (Field)Orbit, with new Interface.
+ Added method to create new instance without rates from position-angled based (Field)Orbit, with new Interface.
- Added {Field}TrackingCoordinates.
+ Added {Field}TrackingCoordinates.
- Added lowestAltitudeIntermediate method to OneAxisEllipsoid.
+ Added lowestAltitudeIntermediate method to OneAxisEllipsoid.
- Fixed Sun radius.
+ Fixed Sun radius.
- Added getter for resetAtEnd in (Field)AbstractIntegratedIntegrator.
+ Added getter for resetAtEnd in (Field)AbstractIntegratedIntegrator.
- Fixed NaN appering in one axis ellipsoid Cartesian to geodetic transform in rare cases.
+ Fixed NaN appering in one axis ellipsoid Cartesian to geodetic transform in rare cases.
- Added configurable skimming altitude to inter-satellite direct view detector.
+ Added configurable skimming altitude to inter-satellite direct view detector.
- Added wind-up effect for inter-satellites phase measurements.
+ Added wind-up effect for inter-satellites phase measurements.
- Added builders for OneWayGNSSPhase and OneWayGNSSRange.
+ Added builders for OneWayGNSSPhase and OneWayGNSSRange.
- Use step interpolators for measurements generation requiring time shifts.
+ Use step interpolators for measurements generation requiring time shifts.
- {Field}OrekitStepInterpolator now implement {Field}PVCoordinatesProvider.
+ {Field}OrekitStepInterpolator now implement {Field}PVCoordinatesProvider.
- Added Az/El based initial orbit determination.
+ Added Az/El based initial orbit determination.
- Fixed multiple issues in initial orbit determination.
+ Fixed multiple issues in initial orbit determination.
- Fixed missing up- and down-stream inheritance of FieldTimeShiftable.
+ Fixed missing up- and down-stream inheritance of FieldTimeShiftable.
- Renamed PositionAngle into PositionAngleType
+ Renamed PositionAngle into PositionAngleType
- Added access to integrator's name for (Field)AbstractIntegratedPropagator
+ Added access to integrator's name for (Field)AbstractIntegratedPropagator
Fixed failing tests after correction of Hipparchus issue 253.
@@ -786,7 +987,7 @@
Moved Rinex parsing/writing into files package.
- Fixed wrong calls in Field acceleration for some gravitational force models.
+ Fixed wrong calls in Field acceleration for some gravitational force models.
Added FDOA measurements.
@@ -876,7 +1077,7 @@
Fixed failing tests on Windows in probability of collision package.
- Added a Field implementation of impulsive maneuvers.
+ Added a Field implementation of impulsive maneuvers.
Added torque-free attitude mode for general (non-symmetrical) body.
@@ -904,7 +1105,7 @@
Replace expected LOFType arguments by recently implemented LOF interface in LocalOrbitalFrame, LofOffset,
- TabulatedLofOffset and ThrustDirectionAndAttitudeProvider
+ TabulatedLofOffset and ThrustDirectionAndAttitudeProvider
Changed Fieldifier to return fielded orbit in the same orbit type as input orbit
@@ -1000,16 +1201,16 @@
Fixed typos in parameters name and associated getters when impacted.
- Added support for old Rinex 2 navigation files.
+ Added support for old Rinex 2 navigation files.
- Added piecewise-polynomial thrust model.
+ Added piecewise-polynomial thrust model.
- Accept some slightly invalid Rinex navigation files.
+ Accept some slightly invalid Rinex navigation files.
- Added MultiSatFixedStepHandler.
+ Added MultiSatFixedStepHandler.
Added "toOrbitRelativeFrame" method in LOFType enum to allow for a better compatibility between Orekit and CCSDS
@@ -1022,36 +1223,36 @@
equivalent
- Added support for CCSDS ADM V2.
+ Added support for CCSDS ADM V2.
- Added PrecessionFinder to recover precession parameters from
- vector first and second derivatives (used by some CCSDS ADM modes).
+ Added PrecessionFinder to recover precession parameters from
+ vector first and second derivatives (used by some CCSDS ADM modes).
Refactored FieldODEIntegratorBuilder interface and its implementing classes
- Added support for Rinex 4.00 (currently only navigation).
+ Added support for Rinex 4.00 (currently only navigation).
- Added prediction of Earth Orientation Parameters.
+ Added prediction of Earth Orientation Parameters.
- Added creation of ITRF from custom Earth Orientation Parameters history.
+ Added creation of ITRF from custom Earth Orientation Parameters history.
- Added support for Rinex 3.05 (observation and navigation).
+ Added support for Rinex 3.05 (observation and navigation).
- Replaced RinexObservationLoader by RinexObservationParser.
+ Replaced RinexObservationLoader by RinexObservationParser.
- Allow to write relative dates in CCSDS files, when dates are close enough to a reference.
+ Allow to write relative dates in CCSDS files, when dates are close enough to a reference.
- Properly manage known inconsistency between CCSDS and TLE
- concerning units of MEAN_MOTION_DOT and MEAN_MOTION_DDOT.
+ Properly manage known inconsistency between CCSDS and TLE
+ concerning units of MEAN_MOTION_DOT and MEAN_MOTION_DDOT.
Fixed error message when propagating state covariance expressed in LOF. Changed behaviour of
@@ -1077,7 +1278,7 @@
Added support for STK ephemeris files.
- Added support for new EOP C04 format published by IERS starting 2023-02-14
+ Added support for new EOP C04 format published by IERS starting 2023-02-14
Added scaling of linear system and allowed different arc duration in multiple shooting.
@@ -1087,12 +1288,12 @@
linear systems and fixed sign of epoch partials in multiple shooting.
- Added DragSensitive.GLOBAL_DRAG_FACTOR as a new global multiplication
- factor that can be applied to all drag coefficients
+ Added DragSensitive.GLOBAL_DRAG_FACTOR as a new global multiplication
+ factor that can be applied to all drag coefficients
- Added RadiationSensitive.GLOBAL_RADIATION_FACTOR as a new global multiplication
- factor that can be applied to all radiation coefficients
+ Added RadiationSensitive.GLOBAL_RADIATION_FACTOR as a new global multiplication
+ factor that can be applied to all radiation coefficients
Added panel dependent coefficients in BoxAndSolarArraySpacecraft.
@@ -1236,10 +1437,10 @@
Removed reference to old Orekit mailing list in LocalOrbitalFrame.
- Fixed theoretical evaluation of AngularRaDec when the reference frame is not Earth-centered.
+ Fixed theoretical evaluation of AngularRaDec when the reference frame is not Earth-centered.
- Fixed wrong wrapper in deprecated KeplerianOrbit's and FieldKeplerianOrbit's methods for anomaly conversions.
+ Fixed wrong wrapper in deprecated KeplerianOrbit's and FieldKeplerianOrbit's methods for anomaly conversions.
Improved documentation of glonass propagators.
@@ -1417,7 +1618,7 @@
Prevents zero max check intervals in maneuvers triggers detectors.
-
+
Added detection of non-positive max check interval and threshold.
@@ -1693,19 +1894,19 @@
when building the state transition matrix in multi satellites
orbit determination. It also fixes bugs in TLE and CRD files.
Finally it includes an update of the release guide.">
-
+
Allowed custom setting of state to TLE conversion in propagator builder.
-
+
Fixed handling of comments in CRD files.
-
+
Fixed deserialization of TLE caused by the bStarParameterDriver.
-
+
Fixed indexes when build state transition matrix for multi sat Kalman.
-
+
Updated the release guide to remove actions that are no longer required.
@@ -1714,26 +1915,26 @@
It fixes an important issue related to the calculation of the relativistic
clock correction for GNSS measurements. It also fixes bugs in OEM and CPF
files writing. Finally it includes some improvements in the class documentation">
-
+
Fixed wrong computation of relativistic clock correction for GNSS measurements.
-
+
Fixed parsing of Rinex clock files.
-
+
Fixed null pointer exception when constructing CPF from coordinates.
-
+
Improved documentation of solar radiation pressure class to include
additional information about osculating bodies.
-
+
Used the latest version of Maven available in RedHat 8.
-
+
Fixed handling of time system in OemWriter.
-
+
Improved documentation of ImpulseManeuver class.
@@ -1752,14 +1953,14 @@
to add several step handlers for the same orbit propagation, a new
event detector for angular separation as seen from the spacecraft.
See the list below for a full description of the changes.">
-
+
Allowed setting of AttitudeProvider to the BoundedPropagator
generated via propagation.
-
+
Fixed format symbols for year, month, day in DateComponents#toString().
-
+
Added a new event detector for angular separation as seen from the spacecraft.
@@ -1970,8 +2171,8 @@
Fixed call to ForceModel.init() in AbstractGaussianContribution class.
-
- Added IGS clock file support.
+
+ Added IGS clock file support.
Methods computeMeanState() and computeOsculatingState()
@@ -1987,7 +2188,7 @@
Fixed reference frame in tabulated attitude provider.
- Renamed SINEXLoader into SinexLoader.
+ Renamed SINEXLoader into SinexLoader.
Use DataSource in RinexLoader and SinexLoader.
@@ -2041,13 +2242,13 @@
Fixed NullPointerException in DSSTTesseral Hansen object.
- Changed getPVInPZ90() method to private.
+ Changed getPVInPZ90() method to private.
- Fixed calculation of CR3BP constants.
+ Fixed calculation of CR3BP constants.
- Updated JUnit version to 4.13.1.
+ Updated JUnit version to 4.13.1.
Updated Hipparchus version to 1.8 and updated code with new functionalities.
-
- Added aggregator for bounded attitude providers.
+
+ Added aggregator for bounded attitude providers.
-
+
Added Knocke's Earth rediffused radiation pressure force model.
-
- Allowed initialization of attitude provider from attitude segment.
+
+ Allowed initialization of attitude provider from attitude segment.
Allowed writing an AEM file from a list of SpacecraftStates.
@@ -2097,20 +2298,20 @@
Allowed user-defined format for ephemeris data lines in
StreamingAemWriter, AEMWriter, StreamingOemWriter and OEMWriter.
-
- Updated building instructions.
+
+ Updated building instructions.
-
- Added getters for phase measurement ambiguity driver.
+
+ Added getters for phase measurement ambiguity driver.
-
- Allowed to configure initial covariance for measurements in Kalman Filter.
+
+ Allowed to configure initial covariance for measurements in Kalman Filter.
Added clock drift contribution to range rate measurements.
-
- Fixed Javadoc of ElevationMask.
+
+ Fixed Javadoc of ElevationMask.
Allowed definition of a default interpolation degree in both AEMParser and OEMParser.
@@ -2124,14 +2325,14 @@
Added documentation for checkstyle configuration.
-
- Removed useless loop over an empty list
+
+ Removed useless loop over an empty list
Fixed parsing of some ICGEM gravity fields files.
- Added support for measurements parameters in UnivariateProcessNoise
+ Added support for measurements parameters in UnivariateProcessNoise
Fixed wrong handling of RESET-STATE in analytical propagators.
@@ -2181,7 +2382,7 @@
Fixed missing measurement parameter in inter-satellites range measurement.
-
+
Fixed computation of DSST short period Jacobian.
@@ -2376,22 +2577,22 @@
Improve performance of ZipJarCrawler.
- Added default constructors for DSSTZonal and DSSTTesseral.
+ Added default constructors for DSSTZonal and DSSTTesseral.
- Added OrekitException for unknown number of frequencies in ANTEX files.
+ Added OrekitException for unknown number of frequencies in ANTEX files.
- Added OrekitException in the case where IONEX header is corrupted.
+ Added OrekitException in the case where IONEX header is corrupted.
- Added a specific test for issue 359 in BatchLSEstimatorTest.
- The test verifies that a Newtonian attraction is known
- by both the propagator builder and the propagator when
- it is not added explicitly.
+ Added a specific test for issue 359 in BatchLSEstimatorTest.
+ The test verifies that a Newtonian attraction is known
+ by both the propagator builder and the propagator when
+ it is not added explicitly.
- Added write of covariance matrices in OEMWriter.
+ Added write of covariance matrices in OEMWriter.
Fixed origin transform in CcsdsModifierFrame.
@@ -2585,7 +2786,7 @@
Fixes broken links on Orekit JavaDoc.
-
+
Fixes broken links on Maven site.
@@ -3164,30 +3365,30 @@
Fixes issue #354.
- Added a convenience method to retrieve covariance matrix in
- physical units in orbit determination.
- Fixes issue #353.
+ Added a convenience method to retrieve covariance matrix in
+ physical units in orbit determination.
+ Fixes issue #353.
- Fixed two errors in Marini-Murray model implementation.
- Fixes issue #352.
+ Fixed two errors in Marini-Murray model implementation.
+ Fixes issue #352.
- Prevent duplicated Newtonian attraction in FieldNumericalPropagator.
- Fixes issue #350.
+ Prevent duplicated Newtonian attraction in FieldNumericalPropagator.
+ Fixes issue #350.
- Copy additional states through impulse maneuvers.
- Fixes issue #349.
+ Copy additional states through impulse maneuvers.
+ Fixes issue #349.
- Removed unused construction parameters in ShiftingTransformProvider
- and InterpolatingTransformProvider.
- Fixes issue #356.
+ Removed unused construction parameters in ShiftingTransformProvider
+ and InterpolatingTransformProvider.
+ Fixes issue #356.
- Fixed wrong inertial frame for Earth retrieved from CelestialBodyFactory.
- Fixes issue #355.
+ Fixed wrong inertial frame for Earth retrieved from CelestialBodyFactory.
+ Fixes issue #355.
Use a git-flow like branching workflow, with a develop branch for bleeding-edge
@@ -3195,7 +3396,7 @@
Disabled XML external resources when parsing rapid XML TDM files.
@@ -3220,24 +3421,24 @@
JB2008 atmosphere model, NRL MSISE 2000 atmosphere model, boolean combination of events
detectors, ephemeris writer, speed improvements when tens of thousands of measurements
are used in orbit determination, Danish translations. Several bugs have been fixed.">
-
- Added on-board antenna phase center effect on inter-satellites range measurements.
-
-
- Added on-board antenna phase center effect on turn-around range measurements.
-
-
- Added on-board antenna phase center effect on range measurements.
-
-
- Moved Bias and OutlierFilter classes together with the other estimation modifiers.
-
-
- Forced states derivatives to be dimension 6 rather than either 6 or 7. The
- additional mass was not really useful, it was intended for maneuvers calibration,
- but in fact during maneuver calibration we adjust either flow rate or specific
- impulse but not directly mass itself.
-
+
+ Added on-board antenna phase center effect on inter-satellites range measurements.
+
+
+ Added on-board antenna phase center effect on turn-around range measurements.
+
+
+ Added on-board antenna phase center effect on range measurements.
+
+
+ Moved Bias and OutlierFilter classes together with the other estimation modifiers.
+
+
+ Forced states derivatives to be dimension 6 rather than either 6 or 7. The
+ additional mass was not really useful, it was intended for maneuvers calibration,
+ but in fact during maneuver calibration we adjust either flow rate or specific
+ impulse but not directly mass itself.
+
Added parametric acceleration force models, where acceleration amplitude is a
simple parametric function. Acceleration direction is fixed in either inertial
@@ -3422,7 +3623,7 @@
Tutorials now all rely on orekit-data being in user home folder.
Fixes issue #245.
-
+
Apply delay corresponding to h = 0 when station altitude is below 0 in SaastamoinenModel.
Fixes issue #202.
@@ -3509,7 +3710,7 @@
Fixed outliers configuration parsing in orbit determination tutorial and test.
Fixes issue #249
-
+
Greatly improved orbit determination speed when a lot of measurements are used
(several thousands).
@@ -3531,7 +3732,7 @@
Disabled XML external resources when parsing rapid XML EOP files.
@@ -3622,7 +3823,7 @@
Disabled XML external resources when parsing rapid XML EOP files.
@@ -3813,7 +4014,7 @@
Fixed wrong ephemeris generation for analytical propagators with maneuvers.
Fixes issue #224.
-
+
Fixed date offset by one second for TLE built from their components,
if a leap second was introduced earlier in the same year.
Fixes issue #225.
@@ -4092,18 +4293,18 @@
Fixes issue #176.
- Added general relativity force model.
+ Added general relativity force model.
added Greek localization for error messages.
- Fixed incorrect partial derivatives for force models that depend on satellite velocity.
- Fixes #174.
+ Fixed incorrect partial derivatives for force models that depend on satellite velocity.
+ Fixes #174.
- Fixed incorrect parameters set in NumericalPropagatorBuilder.
- Fixes #175.
+ Fixed incorrect parameters set in NumericalPropagatorBuilder.
+ Fixes #175.
Significantly reduced size of various serialized objects.
@@ -4619,7 +4820,7 @@
Removed weak hash maps in frames (fixes bug #122).
-
+
Improved documentation of interpolation methods (fixes bug #123).
@@ -4727,7 +4928,7 @@
Removed too stringent test on trajectory in TLE propagator (fixes bug #86).
- Set the initial state for a TLEPropagator (fixes bug #85).
+ Set the initial state for a TLEPropagator (fixes bug #85).
Improved testing of error messages.
@@ -5379,7 +5580,7 @@
for users that did add their own implementations, but it is
simple to deal with (simply add one parameter in the signature
and ignore it) so its was considered acceptable.
-
+
added german localization for error messages
diff --git a/src/design/attitude-class-diagram.puml b/src/design/attitude-class-diagram.puml
index 9e1c1a3c79..25dfe9a2a5 100644
--- a/src/design/attitude-class-diagram.puml
+++ b/src/design/attitude-class-diagram.puml
@@ -71,7 +71,23 @@
#PVCoordinates getTargetPV
}
- Frame <-up- "1" Attitude
+ interface TargetProvider {
+ +getTargetDirection()
+ }
+
+ enum PredefinedTarget {
+ +SUN,
+ +EARTH,
+ +NADIR,
+ +NORTH,
+ +EAST,
+ +VELOCITY,
+ +MOMENTUM
+ }
+
+ class GroundPointTarget
+
+ Frame <-up- "1" Attitude
Attitude <-up- AttitudeProvider : create
TimeStamped <|.. Attitude
TimeShiftable_T_ <|.. Attitude
@@ -83,6 +99,8 @@
AttitudeProvider <--* "1" LofOffsetPointing
AttitudeProvider <|.. GroundPointing
LofOffsetPointing --|> GroundPointing
+ PredefinedTarget ..|> TargetProvider
+ GroundPointTarget ..|> TargetProvider
AttitudeProviderModifier <|-- SpinStabilized
AttitudeProviderModifier <|-- GroundPointingWrapper
@@ -98,6 +116,8 @@
AttitudeProvider <|-- LofOffset
AttitudeProvider <|-- TabulatedProvider
AttitudeProvider <|-- TorqueFree
+ AlignedAndConstrained --|> AttitudeProvider
+ TargetProvider "2" <--* AlignedAndConstrained
}
diff --git a/src/main/java/org/orekit/attitudes/AlignedAndConstrained.java b/src/main/java/org/orekit/attitudes/AlignedAndConstrained.java
new file mode 100644
index 0000000000..80ff1dfc25
--- /dev/null
+++ b/src/main/java/org/orekit/attitudes/AlignedAndConstrained.java
@@ -0,0 +1,203 @@
+/* Copyright 2002-2024 Luc Maisonobe
+ * Licensed to CS GROUP (CS) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.orekit.attitudes;
+
+import org.hipparchus.CalculusFieldElement;
+import org.hipparchus.Field;
+import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2;
+import org.hipparchus.analysis.differentiation.UnivariateDerivative2;
+import org.hipparchus.analysis.differentiation.UnivariateDerivative2Field;
+import org.hipparchus.geometry.euclidean.threed.FieldRotation;
+import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
+import org.hipparchus.geometry.euclidean.threed.Rotation;
+import org.hipparchus.geometry.euclidean.threed.Vector3D;
+import org.orekit.bodies.OneAxisEllipsoid;
+import org.orekit.frames.Frame;
+import org.orekit.time.AbsoluteDate;
+import org.orekit.time.FieldAbsoluteDate;
+import org.orekit.utils.AngularCoordinates;
+import org.orekit.utils.ExtendedPositionProvider;
+import org.orekit.utils.FieldAngularCoordinates;
+import org.orekit.utils.FieldPVCoordinatesProvider;
+import org.orekit.utils.PVCoordinatesProvider;
+import org.orekit.utils.TimeStampedFieldPVCoordinates;
+import org.orekit.utils.TimeStampedPVCoordinates;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Attitude provider with one satellite vector aligned and another one constrained to two targets.
+ * @author Luc Maisonobe
+ * @since 12.2
+ */
+public class AlignedAndConstrained implements AttitudeProvider
+{
+
+ /** Satellite vector for primary target. */
+ private final FieldVector3D primarySat;
+
+ /** Primary target. */
+ private final TargetProvider primaryTarget;
+
+ /** Satellite vector for secondary target. */
+ private final FieldVector3D secondarySat;
+
+ /** Secondary target. */
+ private final TargetProvider secondaryTarget;
+
+ /** Sun model. */
+ private final ExtendedPositionProvider sun;
+
+ /** Earth model. */
+ private final OneAxisEllipsoid earth;
+
+ /** Cached field-based satellite vectors. */
+ private final transient Map>, Cache extends CalculusFieldElement>>>
+ cachedSatelliteVectors;
+
+ /**
+ * Simple constructor.
+ * @param primarySat satellite vector for primary target
+ * @param primaryTarget primary target
+ * @param secondarySat satellite vector for secondary target
+ * @param secondaryTarget secondary target
+ * @param sun Sun model
+ * @param earth Earth model
+ */
+ public AlignedAndConstrained(final Vector3D primarySat, final TargetProvider primaryTarget,
+ final Vector3D secondarySat, final TargetProvider secondaryTarget,
+ final ExtendedPositionProvider sun,
+ final OneAxisEllipsoid earth)
+ {
+ this.primarySat = new FieldVector3D<>(UnivariateDerivative2Field.getInstance(), primarySat);
+ this.primaryTarget = primaryTarget;
+ this.secondarySat = new FieldVector3D<>(UnivariateDerivative2Field.getInstance(), secondarySat);
+ this.secondaryTarget = secondaryTarget;
+ this.sun = sun;
+ this.earth = earth;
+ this.cachedSatelliteVectors = new HashMap<>();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Rotation getAttitudeRotation(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) {
+ final TimeStampedPVCoordinates satPV = pvProv.getPVCoordinates(date, frame);
+
+ // compute targets references at the specified date
+ final Vector3D primaryDirection = primaryTarget.getTargetDirection(sun, earth, satPV, frame);
+ final Vector3D secondaryDirection = secondaryTarget.getTargetDirection(sun, earth, satPV, frame);
+
+ // compute transform from inertial frame to satellite frame
+ return new Rotation(primaryDirection, secondaryDirection, primarySat.toVector3D(), secondarySat.toVector3D());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Attitude getAttitude(final PVCoordinatesProvider pvProv,
+ final AbsoluteDate date,
+ final Frame frame)
+ {
+ final TimeStampedPVCoordinates satPV = pvProv.getPVCoordinates(date, frame);
+
+ // compute targets references at the specified date
+ final FieldVector3D primaryDirection = primaryTarget.getDerivative2TargetDirection(sun, earth, satPV, frame);
+ final FieldVector3D secondaryDirection = secondaryTarget.getDerivative2TargetDirection(sun, earth, satPV, frame);
+
+ // compute transform from inertial frame to satellite frame
+ final FieldRotation inertToSatRotation =
+ new FieldRotation<>(primaryDirection, secondaryDirection, primarySat, secondarySat);
+
+ // build the attitude
+ return new Attitude(date, frame, new AngularCoordinates(inertToSatRotation));
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public > FieldRotation getAttitudeRotation(final FieldPVCoordinatesProvider pvProv,
+ final FieldAbsoluteDate date,
+ final Frame frame) {
+ final TimeStampedFieldPVCoordinates satPV = pvProv.getPVCoordinates(date, frame);
+
+ // compute targets references at the specified date
+ final FieldVector3D primaryDirection = primaryTarget.getTargetDirection(sun, earth, satPV, frame);
+ final FieldVector3D secondaryDirection = secondaryTarget.getTargetDirection(sun, earth, satPV, frame);
+
+ // compute transform from inertial frame to satellite frame
+ final Field field = date.getField();
+ return new FieldRotation<>(primaryDirection, secondaryDirection,
+ new FieldVector3D<>(field, primarySat.toVector3D()), new FieldVector3D<>(field, secondarySat.toVector3D()));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public > FieldAttitude getAttitude(final FieldPVCoordinatesProvider pvProv,
+ final FieldAbsoluteDate date,
+ final Frame frame)
+ {
+ // get the satellite vectors for specified field
+ @SuppressWarnings("unchecked")
+ final Cache satVectors =
+ (Cache) cachedSatelliteVectors.computeIfAbsent(date.getField(),
+ f -> new Cache<>(date.getField(), primarySat, secondarySat));
+
+ final TimeStampedFieldPVCoordinates satPV = pvProv.getPVCoordinates(date, frame);
+
+ // compute targets references at the specified date
+ final FieldVector3D> primaryDirection = primaryTarget.getDerivative2TargetDirection(sun, earth, satPV, frame);
+ final FieldVector3D> secondaryDirection = secondaryTarget.getDerivative2TargetDirection(sun, earth, satPV, frame);
+
+ // compute transform from inertial frame to satellite frame
+ final FieldRotation> inertToSatRotation =
+ new FieldRotation<>(primaryDirection, secondaryDirection, satVectors.primarySat, satVectors.secondarySat);
+
+ // build the attitude
+ return new FieldAttitude<>(date, frame, new FieldAngularCoordinates<>(inertToSatRotation));
+
+ }
+
+ /** Container for cached satellite vectors. */
+ private static class Cache> {
+
+ /** Satellite vector for primary target. */
+ private final FieldVector3D> primarySat;
+
+ /** Satellite vector for primary target. */
+ private final FieldVector3D> secondarySat;
+
+ /** Simple constructor.
+ * @param field field to which the elements belong
+ * @param primarySat satellite vector for primary target
+ * @param secondarySat satellite vector for primary target
+ */
+ Cache(final Field field,
+ final FieldVector3D primarySat,
+ final FieldVector3D secondarySat) {
+ final FieldUnivariateDerivative2 zero =
+ new FieldUnivariateDerivative2<>(field.getZero(), field.getZero(), field.getZero());
+ this.primarySat = new FieldVector3D<>(zero.newInstance(primarySat.getX().getValue()),
+ zero.newInstance(primarySat.getY().getValue()),
+ zero.newInstance(primarySat.getZ().getValue()));
+ this.secondarySat = new FieldVector3D<>(zero.newInstance(secondarySat.getX().getValue()),
+ zero.newInstance(secondarySat.getY().getValue()),
+ zero.newInstance(secondarySat.getZ().getValue()));
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/orekit/attitudes/AttitudesSequence.java b/src/main/java/org/orekit/attitudes/AttitudesSequence.java
index 1ffb291d41..adfa3a7865 100644
--- a/src/main/java/org/orekit/attitudes/AttitudesSequence.java
+++ b/src/main/java/org/orekit/attitudes/AttitudesSequence.java
@@ -333,8 +333,17 @@ public > FieldRotation getAttitudeRotation(
return activated.get(date.toAbsoluteDate()).getAttitudeRotation(pvProv, date, frame);
}
+ /**
+ * Gets a deep copy of the switches stored in this instance.
+ *
+ * @return deep copy of the switches stored in this instance
+ */
+ public List getSwitches() {
+ return new ArrayList<>(switches);
+ }
+
/** Switch specification. */
- private class Switch implements EventDetector, EventHandler {
+ public class Switch implements EventDetector, EventHandler {
/** Event. */
private final EventDetector event;
@@ -363,23 +372,23 @@ private class Switch implements EventDetector, EventHandler {
/** Propagation direction. */
private boolean forward;
- /** Simple constructor.
+ /**
+ * Simple constructor.
+ *
* @param event event
* @param switchOnIncrease if true, switch is triggered on increasing event
- * @param switchOnDecrease if true, switch is triggered on decreasing event
- * otherwise switch is triggered on decreasing event
+ * @param switchOnDecrease if true, switch is triggered on decreasing event otherwise switch is triggered on
+ * decreasing event
* @param past attitude provider applicable for times in the switch event occurrence past
* @param future attitude provider applicable for times in the switch event occurrence future
* @param transitionTime duration of the transition between the past and future attitude laws
- * @param transitionFilter order at which the transition law time derivatives
- * should match past and future attitude laws
+ * @param transitionFilter order at which the transition law time derivatives should match past and future attitude
+ * laws
* @param switchHandler handler to call for notifying when switch occurs (may be null)
*/
- Switch(final EventDetector event,
- final boolean switchOnIncrease, final boolean switchOnDecrease,
- final AttitudeProvider past, final AttitudeProvider future,
- final double transitionTime, final AngularDerivativesFilter transitionFilter,
- final SwitchHandler switchHandler) {
+ private Switch(final EventDetector event, final boolean switchOnIncrease, final boolean switchOnDecrease,
+ final AttitudeProvider past, final AttitudeProvider future, final double transitionTime,
+ final AngularDerivativesFilter transitionFilter, final SwitchHandler switchHandler) {
this.event = event;
this.switchOnIncrease = switchOnIncrease;
this.switchOnDecrease = switchOnDecrease;
diff --git a/src/main/java/org/orekit/attitudes/FixedRate.java b/src/main/java/org/orekit/attitudes/FixedRate.java
index c6a270e307..0921f161ee 100644
--- a/src/main/java/org/orekit/attitudes/FixedRate.java
+++ b/src/main/java/org/orekit/attitudes/FixedRate.java
@@ -18,7 +18,12 @@
import org.hipparchus.Field;
import org.hipparchus.CalculusFieldElement;
+import org.hipparchus.geometry.euclidean.threed.FieldRotation;
+import org.hipparchus.geometry.euclidean.threed.Rotation;
+import org.hipparchus.geometry.euclidean.threed.RotationConvention;
+import org.orekit.frames.FieldStaticTransform;
import org.orekit.frames.Frame;
+import org.orekit.frames.StaticTransform;
import org.orekit.time.AbsoluteDate;
import org.orekit.time.FieldAbsoluteDate;
import org.orekit.utils.FieldPVCoordinatesProvider;
@@ -46,21 +51,61 @@ public FixedRate(final Attitude referenceAttitude) {
}
/** {@inheritDoc} */
+ @Override
+ public Rotation getAttitudeRotation(final PVCoordinatesProvider pvProv, final AbsoluteDate date,
+ final Frame frame) {
+ final Rotation rotation = getShiftedAttitude(date).getRotation();
+ final StaticTransform transform = referenceAttitude.getReferenceFrame().getStaticTransformTo(frame, date);
+ return rotation.compose(transform.getRotation(), RotationConvention.FRAME_TRANSFORM);
+ }
+
+ /** {@inheritDoc} */
+ @Override
public Attitude getAttitude(final PVCoordinatesProvider pvProv,
final AbsoluteDate date, final Frame frame) {
- final double timeShift = date.durationFrom(referenceAttitude.getDate());
- final Attitude shifted = referenceAttitude.shiftedBy(timeShift);
+ final Attitude shifted = getShiftedAttitude(date);
return shifted.withReferenceFrame(frame);
}
+ /**
+ * Get shifted reference attitude.
+ * @param date date of shift
+ * @return shifted attitude
+ */
+ private Attitude getShiftedAttitude(final AbsoluteDate date) {
+ final double timeShift = date.durationFrom(referenceAttitude.getDate());
+ return referenceAttitude.shiftedBy(timeShift);
+ }
+
/** {@inheritDoc} */
+ @Override
+ public > FieldRotation getAttitudeRotation(final FieldPVCoordinatesProvider pvProv,
+ final FieldAbsoluteDate date,
+ final Frame frame) {
+ final FieldRotation rotation = getShiftedAttitude(date).getRotation();
+ final FieldStaticTransform transform = referenceAttitude.getReferenceFrame().getStaticTransformTo(frame, date);
+ return rotation.compose(transform.getRotation(), RotationConvention.FRAME_TRANSFORM);
+ }
+
+ /** {@inheritDoc} */
+ @Override
public > FieldAttitude getAttitude(final FieldPVCoordinatesProvider pvProv,
- final FieldAbsoluteDate date,
- final Frame frame) {
+ final FieldAbsoluteDate date,
+ final Frame frame) {
+ final FieldAttitude shifted = getShiftedAttitude(date);
+ return shifted.withReferenceFrame(frame);
+ }
+
+ /**
+ * Get shifted reference attitude.
+ * @param date date of shift
+ * @param field type
+ * @return shifted attitude
+ */
+ private > FieldAttitude getShiftedAttitude(final FieldAbsoluteDate date) {
final Field field = date.getField();
final T timeShift = date.durationFrom(referenceAttitude.getDate());
- final FieldAttitude shifted = new FieldAttitude<>(field, referenceAttitude).shiftedBy(timeShift);
- return shifted.withReferenceFrame(frame);
+ return new FieldAttitude<>(field, referenceAttitude).shiftedBy(timeShift);
}
/** Get the reference attitude.
diff --git a/src/main/java/org/orekit/attitudes/GroundPointTarget.java b/src/main/java/org/orekit/attitudes/GroundPointTarget.java
new file mode 100644
index 0000000000..a045b27116
--- /dev/null
+++ b/src/main/java/org/orekit/attitudes/GroundPointTarget.java
@@ -0,0 +1,120 @@
+/* Copyright 2002-2024 Luc Maisonobe
+ * Licensed to CS GROUP (CS) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.orekit.attitudes;
+
+import org.hipparchus.CalculusFieldElement;
+import org.hipparchus.Field;
+import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2;
+import org.hipparchus.analysis.differentiation.UnivariateDerivative2;
+import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
+import org.hipparchus.geometry.euclidean.threed.Vector3D;
+import org.orekit.bodies.OneAxisEllipsoid;
+import org.orekit.frames.FieldTransform;
+import org.orekit.frames.FieldStaticTransform;
+import org.orekit.frames.Frame;
+import org.orekit.frames.StaticTransform;
+import org.orekit.frames.Transform;
+import org.orekit.utils.ExtendedPositionProvider;
+import org.orekit.utils.FieldPVCoordinates;
+import org.orekit.utils.PVCoordinates;
+import org.orekit.utils.TimeStampedFieldPVCoordinates;
+import org.orekit.utils.TimeStampedPVCoordinates;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Ground point target for {@link AlignedAndConstrained}.
+ * @author Luc Maisonobe
+ * @since 12.2
+ */
+public class GroundPointTarget implements TargetProvider
+{
+
+ /** Location of the target in Earth frame. */
+ private final PVCoordinates location;
+
+ /** Cached field-based locations. */
+ private final transient Map>, FieldPVCoordinates>>
+ cachedLocations;
+
+ /** Simple constructor.
+ * @param location location of the target in Earth frame
+ */
+ public GroundPointTarget(final Vector3D location)
+ {
+ this.location = new PVCoordinates(location, Vector3D.ZERO, Vector3D.ZERO);
+ this.cachedLocations = new HashMap<>();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldVector3D getDerivative2TargetDirection(final ExtendedPositionProvider sun,
+ final OneAxisEllipsoid earth,
+ final TimeStampedPVCoordinates pv,
+ final Frame frame) {
+ final Transform earthToInert = earth.getFrame().getTransformTo(frame, pv.getDate());
+ return new PVCoordinates(pv, earthToInert.transformPVCoordinates(location)).
+ toUnivariateDerivative2Vector().
+ normalize();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Vector3D getTargetDirection(final ExtendedPositionProvider sun, final OneAxisEllipsoid earth,
+ final TimeStampedPVCoordinates pv, final Frame frame) {
+ final StaticTransform earthToInert = earth.getFrame().getStaticTransformTo(frame, pv.getDate());
+ return earthToInert.transformPosition(location.getPosition()).subtract(pv.getPosition()).normalize();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public > FieldVector3D> getDerivative2TargetDirection(final ExtendedPositionProvider sun,
+ final OneAxisEllipsoid earth,
+ final TimeStampedFieldPVCoordinates pv,
+ final Frame frame) {
+ final Field field = pv.getDate().getField();
+
+ // get the target location for specified field
+ @SuppressWarnings("unchecked")
+ final FieldPVCoordinates l =
+ (FieldPVCoordinates) cachedLocations.computeIfAbsent(field, f -> {
+ final T zero = field.getZero();
+ return new FieldPVCoordinates<>(new FieldVector3D<>(zero.newInstance(location.getPosition().getX()),
+ zero.newInstance(location.getPosition().getY()),
+ zero.newInstance(location.getPosition().getZ())),
+ FieldVector3D.getZero(field),
+ FieldVector3D.getZero(field));
+ });
+
+ final FieldTransform earthToInert = earth.getFrame().getTransformTo(frame, pv.getDate());
+ return new FieldPVCoordinates<>(pv, earthToInert.transformPVCoordinates(l)).
+ toUnivariateDerivative2Vector().
+ normalize();
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public > FieldVector3D getTargetDirection(final ExtendedPositionProvider sun,
+ final OneAxisEllipsoid earth,
+ final TimeStampedFieldPVCoordinates pv,
+ final Frame frame) {
+ final FieldStaticTransform earthToInert = earth.getFrame().getStaticTransformTo(frame, pv.getDate());
+ return earthToInert.transformPosition(location.getPosition()).subtract(pv.getPosition()).normalize();
+ }
+}
diff --git a/src/main/java/org/orekit/attitudes/NadirPointing.java b/src/main/java/org/orekit/attitudes/NadirPointing.java
index 5a35b670db..465d033f28 100644
--- a/src/main/java/org/orekit/attitudes/NadirPointing.java
+++ b/src/main/java/org/orekit/attitudes/NadirPointing.java
@@ -178,9 +178,7 @@ public > TimeStampedFieldPVCoordinates getT
} else { // use automatic differentiation
// build time dependent transform
final FieldUnivariateDerivative2Field ud2Field = FieldUnivariateDerivative2Field.getUnivariateDerivative2Field(field);
- final T shift = date.durationFrom(date.toAbsoluteDate());
- final FieldUnivariateDerivative2 dt = new FieldUnivariateDerivative2<>(shift, field.getOne(), field.getZero());
- final FieldAbsoluteDate> ud2Date = new FieldAbsoluteDate<>(ud2Field, date.toAbsoluteDate()).shiftedBy(dt);
+ final FieldAbsoluteDate> ud2Date = date.toFUD2Field();
final FieldStaticTransform> refToBody = frame.getStaticTransformTo(shape.getBodyFrame(), ud2Date);
final FieldVector3D> positionInRefFrame = pvCoordinatesInRef.toUnivariateDerivative2Vector();
diff --git a/src/main/java/org/orekit/attitudes/PredefinedTarget.java b/src/main/java/org/orekit/attitudes/PredefinedTarget.java
new file mode 100644
index 0000000000..a69a2de74a
--- /dev/null
+++ b/src/main/java/org/orekit/attitudes/PredefinedTarget.java
@@ -0,0 +1,329 @@
+/* Copyright 2002-2024 Luc Maisonobe
+ * Licensed to CS GROUP (CS) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.orekit.attitudes;
+
+import org.hipparchus.CalculusFieldElement;
+import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2;
+import org.hipparchus.analysis.differentiation.UnivariateDerivative2;
+import org.hipparchus.analysis.differentiation.UnivariateDerivative2Field;
+import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
+import org.hipparchus.geometry.euclidean.threed.Vector3D;
+import org.orekit.bodies.FieldGeodeticPoint;
+import org.orekit.bodies.GeodeticPoint;
+import org.orekit.bodies.OneAxisEllipsoid;
+import org.orekit.frames.FieldStaticTransform;
+import org.orekit.frames.Frame;
+import org.orekit.frames.StaticTransform;
+import org.orekit.time.AbsoluteDate;
+import org.orekit.time.FieldAbsoluteDate;
+import org.orekit.utils.ExtendedPositionProvider;
+import org.orekit.utils.PVCoordinates;
+import org.orekit.utils.TimeStampedFieldPVCoordinates;
+import org.orekit.utils.TimeStampedPVCoordinates;
+
+/**
+ * Predefined targets for {@link AlignedAndConstrained}.
+ * @author Luc Maisonobe
+ * @since 12.2
+ */
+public enum PredefinedTarget implements TargetProvider
+{
+
+ /** Sun direction. */
+ SUN {
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldVector3D getDerivative2TargetDirection(final ExtendedPositionProvider sun,
+ final OneAxisEllipsoid earth,
+ final TimeStampedPVCoordinates pv,
+ final Frame frame) {
+ return new PVCoordinates(pv, sun.getPVCoordinates(pv.getDate(), frame)).
+ toUnivariateDerivative2Vector().
+ normalize();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Vector3D getTargetDirection(final ExtendedPositionProvider sun, final OneAxisEllipsoid earth,
+ final TimeStampedPVCoordinates pv, final Frame frame) {
+ return sun.getPosition(pv.getDate(), frame).subtract(pv.getPosition()).normalize();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public > FieldVector3D getTargetDirection(final ExtendedPositionProvider sun,
+ final OneAxisEllipsoid earth,
+ final TimeStampedFieldPVCoordinates pv,
+ final Frame frame) {
+ return sun.getPosition(pv.getDate(), frame).subtract(pv.getPosition()).normalize();
+ }
+ },
+
+ /** Earth direction (assumes the frame is Earth centered). */
+ EARTH {
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldVector3D getDerivative2TargetDirection(final ExtendedPositionProvider sun,
+ final OneAxisEllipsoid earth,
+ final TimeStampedPVCoordinates pv,
+ final Frame frame) {
+ return pv.toUnivariateDerivative2Vector().negate().normalize();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Vector3D getTargetDirection(final ExtendedPositionProvider sun, final OneAxisEllipsoid earth,
+ final TimeStampedPVCoordinates pv, final Frame frame) {
+ return pv.getPosition().negate().normalize();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public > FieldVector3D> getDerivative2TargetDirection(final ExtendedPositionProvider sun,
+ final OneAxisEllipsoid earth,
+ final TimeStampedFieldPVCoordinates pv,
+ final Frame frame) {
+ return pv.toUnivariateDerivative2Vector().negate().normalize();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public > FieldVector3D getTargetDirection(final ExtendedPositionProvider sun,
+ final OneAxisEllipsoid earth,
+ final TimeStampedFieldPVCoordinates pv,
+ final Frame frame) {
+ return pv.getPosition().negate().normalize();
+ }
+ },
+
+ /** Nadir. */
+ NADIR {
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldVector3D getDerivative2TargetDirection(final ExtendedPositionProvider sun,
+ final OneAxisEllipsoid earth,
+ final TimeStampedPVCoordinates pv,
+ final Frame frame) {
+ final FieldStaticTransform inert2Earth = inert2Earth(earth, pv.getDate(), frame);
+ final FieldGeodeticPoint gp = toGeodeticPoint(earth, pv, inert2Earth);
+ return inert2Earth.getStaticInverse().transformVector(gp.getNadir());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Vector3D getTargetDirection(final ExtendedPositionProvider sun, final OneAxisEllipsoid earth,
+ final TimeStampedPVCoordinates pv, final Frame frame) {
+ final StaticTransform inert2Earth = frame.getStaticTransformTo(earth.getBodyFrame(), pv.getDate());
+ final GeodeticPoint geodeticPoint = earth.transform(inert2Earth.transformPosition(pv.getPosition()),
+ earth.getBodyFrame(), pv.getDate());
+ return inert2Earth.getStaticInverse().transformVector(geodeticPoint.getNadir());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public > FieldVector3D getTargetDirection(final ExtendedPositionProvider sun,
+ final OneAxisEllipsoid earth,
+ final TimeStampedFieldPVCoordinates pv,
+ final Frame frame) {
+ final FieldStaticTransform inert2Earth = frame.getStaticTransformTo(earth.getBodyFrame(), pv.getDate());
+ final FieldGeodeticPoint geodeticPoint = earth.transform(inert2Earth.transformPosition(pv.getPosition()),
+ earth.getBodyFrame(), pv.getDate());
+ return inert2Earth.getStaticInverse().transformVector(geodeticPoint.getNadir());
+ }
+ },
+
+ /** North direction. */
+ NORTH {
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldVector3D getDerivative2TargetDirection(final ExtendedPositionProvider sun,
+ final OneAxisEllipsoid earth,
+ final TimeStampedPVCoordinates pv,
+ final Frame frame) {
+ final FieldStaticTransform inert2Earth = inert2Earth(earth, pv.getDate(), frame);
+ final FieldGeodeticPoint gp = toGeodeticPoint(earth, pv, inert2Earth);
+ return inert2Earth.getStaticInverse().transformVector(gp.getNorth());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Vector3D getTargetDirection(final ExtendedPositionProvider sun, final OneAxisEllipsoid earth,
+ final TimeStampedPVCoordinates pv, final Frame frame) {
+ final StaticTransform inert2Earth = frame.getStaticTransformTo(earth.getBodyFrame(), pv.getDate());
+ final GeodeticPoint geodeticPoint = earth.transform(inert2Earth.transformPosition(pv.getPosition()),
+ earth.getBodyFrame(), pv.getDate());
+ return inert2Earth.getStaticInverse().transformVector(geodeticPoint.getNorth());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public > FieldVector3D getTargetDirection(final ExtendedPositionProvider sun,
+ final OneAxisEllipsoid earth,
+ final TimeStampedFieldPVCoordinates pv,
+ final Frame frame) {
+ final FieldStaticTransform inert2Earth = frame.getStaticTransformTo(earth.getBodyFrame(), pv.getDate());
+ final FieldGeodeticPoint geodeticPoint = earth.transform(inert2Earth.transformPosition(pv.getPosition()),
+ earth.getBodyFrame(), pv.getDate());
+ return inert2Earth.getStaticInverse().transformVector(geodeticPoint.getNorth());
+ }
+ },
+
+ /** East direction. */
+ EAST {
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldVector3D getDerivative2TargetDirection(final ExtendedPositionProvider sun,
+ final OneAxisEllipsoid earth,
+ final TimeStampedPVCoordinates pv,
+ final Frame frame) {
+ final FieldStaticTransform inert2Earth = inert2Earth(earth, pv.getDate(), frame);
+ final FieldGeodeticPoint gp = toGeodeticPoint(earth, pv, inert2Earth);
+ return inert2Earth.getStaticInverse().transformVector(gp.getEast());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Vector3D getTargetDirection(final ExtendedPositionProvider sun, final OneAxisEllipsoid earth,
+ final TimeStampedPVCoordinates pv, final Frame frame) {
+ final StaticTransform inert2Earth = frame.getStaticTransformTo(earth.getBodyFrame(), pv.getDate());
+ final GeodeticPoint geodeticPoint = earth.transform(inert2Earth.transformPosition(pv.getPosition()),
+ earth.getBodyFrame(), pv.getDate());
+ return inert2Earth.getStaticInverse().transformVector(geodeticPoint.getEast());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public > FieldVector3D getTargetDirection(final ExtendedPositionProvider sun,
+ final OneAxisEllipsoid earth,
+ final TimeStampedFieldPVCoordinates pv,
+ final Frame frame) {
+ final FieldStaticTransform inert2Earth = frame.getStaticTransformTo(earth.getBodyFrame(), pv.getDate());
+ final FieldGeodeticPoint geodeticPoint = earth.transform(inert2Earth.transformPosition(pv.getPosition()),
+ earth.getBodyFrame(), pv.getDate());
+ return inert2Earth.getStaticInverse().transformVector(geodeticPoint.getEast());
+ }
+ },
+
+ /** Satellite velocity. */
+ VELOCITY {
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldVector3D getDerivative2TargetDirection(final ExtendedPositionProvider sun,
+ final OneAxisEllipsoid earth,
+ final TimeStampedPVCoordinates pv,
+ final Frame frame) {
+ return pv.toUnivariateDerivative2PV().getVelocity().normalize();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Vector3D getTargetDirection(final ExtendedPositionProvider sun, final OneAxisEllipsoid earth,
+ final TimeStampedPVCoordinates pv, final Frame frame) {
+ return pv.getVelocity().normalize();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public > FieldVector3D> getDerivative2TargetDirection(final ExtendedPositionProvider sun,
+ final OneAxisEllipsoid earth,
+ final TimeStampedFieldPVCoordinates pv,
+ final Frame frame) {
+ return pv.toUnivariateDerivative2PV().getVelocity().normalize();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public > FieldVector3D getTargetDirection(final ExtendedPositionProvider sun,
+ final OneAxisEllipsoid earth,
+ final TimeStampedFieldPVCoordinates pv,
+ final Frame frame) {
+ return pv.getVelocity().normalize();
+ }
+ },
+
+ /** Satellite orbital momentum. */
+ MOMENTUM {
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldVector3D getDerivative2TargetDirection(final ExtendedPositionProvider sun,
+ final OneAxisEllipsoid earth,
+ final TimeStampedPVCoordinates pv,
+ final Frame frame) {
+ return pv.toUnivariateDerivative2PV().getMomentum().normalize();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Vector3D getTargetDirection(final ExtendedPositionProvider sun, final OneAxisEllipsoid earth,
+ final TimeStampedPVCoordinates pv, final Frame frame) {
+ return pv.getMomentum().normalize();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public > FieldVector3D> getDerivative2TargetDirection(final ExtendedPositionProvider sun,
+ final OneAxisEllipsoid earth,
+ final TimeStampedFieldPVCoordinates pv,
+ final Frame frame) {
+ return pv.toUnivariateDerivative2PV().getMomentum().normalize();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public > FieldVector3D getTargetDirection(final ExtendedPositionProvider sun,
+ final OneAxisEllipsoid earth,
+ final TimeStampedFieldPVCoordinates pv,
+ final Frame frame) {
+ return pv.getMomentum().normalize();
+ }
+ };
+
+ /** Get transform from inertial frame to Earth frame.
+ * @param earth Earth model
+ * @param date date
+ * @param frame inertial frame
+ * @return geodetic point with derivatives
+ */
+ private static FieldStaticTransform inert2Earth(final OneAxisEllipsoid earth,
+ final AbsoluteDate date,
+ final Frame frame) {
+ final FieldAbsoluteDate dateU2 =
+ new FieldAbsoluteDate<>(UnivariateDerivative2Field.getInstance(), date).
+ shiftedBy(new UnivariateDerivative2(0.0, 1.0, 0.0));
+ return frame.getStaticTransformTo(earth.getBodyFrame(), dateU2);
+ }
+
+ /** Convert to geodetic point with derivatives.
+ * @param earth Earth model
+ * @param pv spacecraft position and velocity
+ * @param inert2Earth transform from inertial frame to Earth frame
+ * @return geodetic point with derivatives
+ */
+ private static FieldGeodeticPoint toGeodeticPoint(final OneAxisEllipsoid earth,
+ final TimeStampedPVCoordinates pv,
+ final FieldStaticTransform inert2Earth) {
+ return earth.transform(inert2Earth.transformPosition(pv.toUnivariateDerivative2Vector()),
+ earth.getBodyFrame(), inert2Earth.getFieldDate());
+ }
+}
diff --git a/src/main/java/org/orekit/attitudes/TargetProvider.java b/src/main/java/org/orekit/attitudes/TargetProvider.java
new file mode 100644
index 0000000000..51e86390dd
--- /dev/null
+++ b/src/main/java/org/orekit/attitudes/TargetProvider.java
@@ -0,0 +1,104 @@
+/* Copyright 2002-2024 Luc Maisonobe
+ * Licensed to CS GROUP (CS) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.orekit.attitudes;
+
+import org.hipparchus.CalculusFieldElement;
+import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2;
+import org.hipparchus.analysis.differentiation.UnivariateDerivative2;
+import org.hipparchus.analysis.differentiation.UnivariateDerivative2Field;
+import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
+import org.hipparchus.geometry.euclidean.threed.Vector3D;
+import org.orekit.bodies.OneAxisEllipsoid;
+import org.orekit.frames.Frame;
+import org.orekit.time.FieldAbsoluteDate;
+import org.orekit.utils.ExtendedPositionProvider;
+import org.orekit.utils.FieldPVCoordinates;
+import org.orekit.utils.TimeStampedFieldPVCoordinates;
+import org.orekit.utils.TimeStampedPVCoordinates;
+
+/**
+ * Provider for target vector.
+ * @author Luc Maisonobe
+ * @since 12.2
+ */
+public interface TargetProvider
+{
+
+ /**
+ * Get a target vector.
+ * @param sun Sun model
+ * @param earth Earth model
+ * @param pv spacecraft position and velocity
+ * @param frame inertial frame
+ * @return target direction in the spacecraft state frame, with second order time derivative
+ */
+ default FieldVector3D getDerivative2TargetDirection(ExtendedPositionProvider sun,
+ OneAxisEllipsoid earth,
+ TimeStampedPVCoordinates pv,
+ Frame frame) {
+ final FieldPVCoordinates ud2PV = pv.toUnivariateDerivative2PV();
+ final UnivariateDerivative2Field field = UnivariateDerivative2Field.getInstance();
+ final UnivariateDerivative2 dt = new UnivariateDerivative2(0., 1., 0.);
+ final FieldAbsoluteDate ud2Date = new FieldAbsoluteDate<>(field, pv.getDate()).shiftedBy(dt);
+ return getTargetDirection(sun, earth, new TimeStampedFieldPVCoordinates<>(ud2Date, ud2PV), frame);
+ }
+
+ /**
+ * Get a target vector.
+ * @param sun Sun model
+ * @param earth Earth model
+ * @param pv spacecraft position and velocity
+ * @param frame inertial frame
+ * @return target direction in the spacecraft state frame
+ */
+ default Vector3D getTargetDirection(ExtendedPositionProvider sun, OneAxisEllipsoid earth,
+ TimeStampedPVCoordinates pv, Frame frame) {
+ return getDerivative2TargetDirection(sun, earth, pv, frame).toVector3D();
+ }
+
+ /**
+ * Get a target vector.
+ * @param type of the field element
+ * @param sun Sun model
+ * @param earth Earth model
+ * @param pv spacecraft position and velocity
+ * @param frame inertial frame
+ * @return target direction in the spacecraft state frame, with second order time derivative
+ */
+ default > FieldVector3D> getDerivative2TargetDirection(ExtendedPositionProvider sun,
+ OneAxisEllipsoid earth,
+ TimeStampedFieldPVCoordinates pv,
+ Frame frame) {
+ final FieldPVCoordinates> ud2PV = pv.toUnivariateDerivative2PV();
+ final FieldAbsoluteDate> ud2Date = pv.getDate().toFUD2Field();
+ return getTargetDirection(sun, earth, new TimeStampedFieldPVCoordinates<>(ud2Date, ud2PV), frame);
+ }
+
+ /**
+ * Get a target vector.
+ * @param type of the field element
+ * @param sun Sun model
+ * @param earth Earth model
+ * @param pv spacecraft position and velocity
+ * @param frame inertial frame
+ * @return target direction in the spacecraft state frame
+ */
+ > FieldVector3D getTargetDirection(ExtendedPositionProvider sun,
+ OneAxisEllipsoid earth,
+ TimeStampedFieldPVCoordinates pv,
+ Frame frame);
+}
diff --git a/src/main/java/org/orekit/bodies/AnalyticalSolarPositionProvider.java b/src/main/java/org/orekit/bodies/AnalyticalSolarPositionProvider.java
new file mode 100644
index 0000000000..1137d99e6b
--- /dev/null
+++ b/src/main/java/org/orekit/bodies/AnalyticalSolarPositionProvider.java
@@ -0,0 +1,136 @@
+/* Copyright 2022-2024 Romain Serra
+ * Licensed to CS GROUP (CS) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.orekit.bodies;
+
+import org.hipparchus.CalculusFieldElement;
+import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
+import org.hipparchus.geometry.euclidean.threed.Vector3D;
+import org.hipparchus.util.FastMath;
+import org.hipparchus.util.FieldSinCos;
+import org.hipparchus.util.SinCos;
+import org.orekit.annotation.DefaultDataContext;
+import org.orekit.data.DataContext;
+import org.orekit.frames.Frame;
+import org.orekit.frames.FieldStaticTransform;
+import org.orekit.frames.StaticTransform;
+import org.orekit.time.AbsoluteDate;
+import org.orekit.time.FieldAbsoluteDate;
+import org.orekit.time.TimeScale;
+import org.orekit.utils.ExtendedPositionProvider;
+
+/**
+ * Class computing low-fidelity positions for the Sun. They should only be used in the decades around the year 2000.
+ *
Reference: Montenbruck, Oliver, and Gill, Eberhard. Satellite orbits : models, methods, and
+ * applications. Berlin New York: Springer, 2000.
+ *
+ * @author Romain Serra
+ * @since 12.2
+ */
+public class AnalyticalSolarPositionProvider implements ExtendedPositionProvider {
+
+ /** Sine anc cosine of approximate ecliptic angle used when converting from ecliptic to EME2000. */
+ private static final SinCos SIN_COS_ECLIPTIC_ANGLE_EME2000 = FastMath.sinCos(FastMath.toRadians(23.43929111));
+
+ /** Precomputed constant angle used in calculations. */
+ private static final double INTERMEDIATE_ANGLE = FastMath.toRadians(282.9400);
+
+ /** EME2000 frame. */
+ private final Frame eme2000;
+
+ /** Time scale for Julian date. */
+ private final TimeScale timeScale;
+
+ /**
+ * Constructor.
+ * @param dataContext data context
+ */
+ public AnalyticalSolarPositionProvider(final DataContext dataContext) {
+ this.eme2000 = dataContext.getFrames().getEME2000();
+ this.timeScale = dataContext.getTimeScales().getUTC();
+ }
+
+ /**
+ * Constructor with default data context.
+ */
+ @DefaultDataContext
+ public AnalyticalSolarPositionProvider() {
+ this(DataContext.getDefault());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Vector3D getPosition(final AbsoluteDate date, final Frame frame) {
+ final Vector3D eme2000Position = getEME2000Position(date);
+ if (frame.equals(eme2000)) {
+ return eme2000Position;
+ } else {
+ final StaticTransform transform = eme2000.getStaticTransformTo(frame, date);
+ return transform.transformPosition(eme2000Position);
+ }
+ }
+
+ /**
+ * Computes the Sun's position vector in EME2000.
+ * @param date date
+ * @return solar position
+ */
+ private Vector3D getEME2000Position(final AbsoluteDate date) {
+ final double tt = (date.getJD(timeScale) - 2451545.0) / 36525.0;
+ final double M = FastMath.toRadians(357.5256 + 35999.049 * tt);
+ final SinCos sinCosM = FastMath.sinCos(M);
+ final SinCos sinCos2M = FastMath.sinCos(2 * M);
+ final double r = (149.619 - 2.499 * sinCosM.cos() - 0.021 * sinCos2M.cos()) * 1.0e9;
+ final double lambda = INTERMEDIATE_ANGLE + M + FastMath.toRadians(6892.0 * sinCosM.sin() + 72.0 * sinCos2M.sin()) / 3600.0;
+ final SinCos sinCosLambda = FastMath.sinCos(lambda);
+ return new Vector3D(r * sinCosLambda.cos(), r * sinCosLambda.sin() * SIN_COS_ECLIPTIC_ANGLE_EME2000.cos(),
+ r * sinCosLambda.sin() * SIN_COS_ECLIPTIC_ANGLE_EME2000.sin());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public > FieldVector3D getPosition(final FieldAbsoluteDate date,
+ final Frame frame) {
+ final FieldVector3D eme2000Position = getFieldEME2000Position(date);
+ if (frame.equals(eme2000)) {
+ return eme2000Position;
+ } else {
+ final FieldStaticTransform transform = eme2000.getStaticTransformTo(frame, date);
+ return transform.transformPosition(eme2000Position);
+ }
+ }
+
+ /**
+ * Computes the Sun's position vector in EME2000.
+ * @param date date
+ * @param field type
+ * @return solar position
+ */
+ private > FieldVector3D getFieldEME2000Position(final FieldAbsoluteDate date) {
+ final T tt = date.getJD(timeScale).subtract(2451545.0).divide(36525.0);
+ final T M = FastMath.toRadians(tt.multiply(35999.049).add(357.5256));
+ final FieldSinCos sinCosM = FastMath.sinCos(M);
+ final FieldSinCos sinCos2M = FastMath.sinCos(M.multiply(2));
+ final T r = (sinCosM.cos().multiply(-2.499).subtract(sinCos2M.cos().multiply(0.021)).add(149.619)).multiply(1.0e9);
+ final T lambda = M.add(INTERMEDIATE_ANGLE).add(FastMath.toRadians(
+ sinCosM.sin().multiply(6892.0).add(sinCos2M.sin().multiply(72.0)).divide(3600.0)));
+ final FieldSinCos sinCosLambda = FastMath.sinCos(lambda);
+ return new FieldVector3D<>(r.multiply(sinCosLambda.cos()),
+ r.multiply(sinCosLambda.sin()).multiply(SIN_COS_ECLIPTIC_ANGLE_EME2000.cos()),
+ r.multiply(sinCosLambda.sin()).multiply(SIN_COS_ECLIPTIC_ANGLE_EME2000.sin()));
+ }
+}
diff --git a/src/main/java/org/orekit/bodies/FieldGeodeticPoint.java b/src/main/java/org/orekit/bodies/FieldGeodeticPoint.java
index 805e82a4aa..d1ded20b41 100644
--- a/src/main/java/org/orekit/bodies/FieldGeodeticPoint.java
+++ b/src/main/java/org/orekit/bodies/FieldGeodeticPoint.java
@@ -204,6 +204,15 @@ public FieldVector3D getWest() {
return west;
}
+ /**
+ * Get non-Field equivalent.
+ * @return geodetic point
+ * @since 12.2
+ */
+ public GeodeticPoint toGeodeticPoint() {
+ return new GeodeticPoint(latitude.getReal(), longitude.getReal(), altitude.getReal());
+ }
+
@Override
public boolean equals(final Object object) {
if (object instanceof FieldGeodeticPoint>) {
diff --git a/src/main/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointDerivativesProvider.java b/src/main/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointDerivativesProvider.java
new file mode 100644
index 0000000000..025d11b87c
--- /dev/null
+++ b/src/main/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointDerivativesProvider.java
@@ -0,0 +1,66 @@
+/* Copyright 2022-2024 Romain Serra
+ * Licensed to CS GROUP (CS) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.orekit.control.indirect.adjoint;
+
+import org.orekit.control.indirect.adjoint.cost.CartesianCost;
+import org.orekit.propagation.integration.AdditionalDerivativesProvider;
+
+/**
+ * Abstract class defining common things for Cartesian adjoint dynamics between standard and Field versions.
+ * @author Romain Serra
+ * @see AdditionalDerivativesProvider
+ * @see org.orekit.propagation.numerical.NumericalPropagator
+ * @since 12.2
+ */
+public class AbstractCartesianAdjointDerivativesProvider {
+
+ /** Name of the additional variables. */
+ private final String name;
+
+ /** Cost function. */
+ private final CartesianCost cost;
+
+ /**
+ * Constructor.
+ * @param cost cost function
+ */
+ public AbstractCartesianAdjointDerivativesProvider(final CartesianCost cost) {
+ this.name = cost.getAdjointName();
+ this.cost = cost;
+ }
+
+ /**
+ * Getter for the cost.
+ * @return cost
+ */
+ public CartesianCost getCost() {
+ return cost;
+ }
+
+ /** Getter for the name.
+ * @return name */
+ public String getName() {
+ return name;
+ }
+
+ /** Getter for the dimension.
+ * @return dimension
+ */
+ public int getDimension() {
+ return cost.getAdjointDimension();
+ }
+}
diff --git a/src/main/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointEquationTerm.java b/src/main/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointEquationTerm.java
new file mode 100644
index 0000000000..5caf4c1746
--- /dev/null
+++ b/src/main/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointEquationTerm.java
@@ -0,0 +1,158 @@
+/* Copyright 2022-2024 Romain Serra
+ * Licensed to CS GROUP (CS) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.orekit.control.indirect.adjoint;
+
+import org.hipparchus.CalculusFieldElement;
+import org.hipparchus.analysis.differentiation.FieldGradient;
+import org.hipparchus.analysis.differentiation.FieldGradientField;
+import org.hipparchus.analysis.differentiation.Gradient;
+import org.hipparchus.analysis.differentiation.GradientField;
+import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
+import org.hipparchus.geometry.euclidean.threed.Vector3D;
+import org.hipparchus.util.MathArrays;
+import org.orekit.frames.Frame;
+import org.orekit.time.AbsoluteDate;
+import org.orekit.time.FieldAbsoluteDate;
+
+/**
+ * Abstract class to define terms in the adjoint equations and Hamiltonian for Cartesian coordinates.
+ * @author Romain Serra
+ * @see CartesianAdjointDerivativesProvider
+ * @see FieldCartesianAdjointDerivativesProvider
+ * @since 12.2
+ */
+public abstract class AbstractCartesianAdjointEquationTerm implements CartesianAdjointEquationTerm {
+
+ /** Dimension of gradient. */
+ private static final int GRADIENT_DIMENSION = 6;
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] getRatesContribution(final AbsoluteDate date, final double[] stateVariables,
+ final double[] adjointVariables, final Frame frame) {
+ final GradientField field = GradientField.getField(GRADIENT_DIMENSION);
+ final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(field, date);
+ final Gradient[] stateAsGradients = buildGradientCartesianVector(stateVariables);
+ final FieldVector3D acceleration = getFieldAcceleration(fieldDate, stateAsGradients, frame);
+ final double[] accelerationXgradient = acceleration.getX().getGradient();
+ final double[] accelerationYgradient = acceleration.getY().getGradient();
+ final double[] accelerationZgradient = acceleration.getZ().getGradient();
+ final double[] contribution = new double[adjointVariables.length];
+ for (int i = 0; i < 6; i++) {
+ contribution[i] = -(accelerationXgradient[i] * adjointVariables[3] + accelerationYgradient[i] * adjointVariables[4] + accelerationZgradient[i] * adjointVariables[5]);
+ }
+ return contribution;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getHamiltonianContribution(final AbsoluteDate date, final double[] stateVariables,
+ final double[] adjointVariables, final Frame frame) {
+ final Vector3D acceleration = getAcceleration(date, stateVariables, frame);
+ return acceleration.getX() * adjointVariables[3] + acceleration.getY() * adjointVariables[4] + acceleration.getZ() * adjointVariables[5];
+ }
+
+ /**
+ * Compute the acceleration vector.
+ *
+ * @param date date
+ * @param stateVariables state variables
+ * @param frame propagation frame
+ * @return acceleration vector
+ */
+ protected abstract Vector3D getAcceleration(AbsoluteDate date, double[] stateVariables,
+ Frame frame);
+
+ /** {@inheritDoc} */
+ @Override
+ public > T[] getFieldRatesContribution(final FieldAbsoluteDate date,
+ final T[] stateVariables,
+ final T[] adjointVariables,
+ final Frame frame) {
+ final FieldGradientField field = FieldGradientField.getField(date.getField(), GRADIENT_DIMENSION);
+ final FieldAbsoluteDate> fieldDate = new FieldAbsoluteDate<>(field, date.toAbsoluteDate());
+ final FieldGradient[] gradients = buildFieldGradientCartesianVector(stateVariables);
+ final FieldVector3D> acceleration = getFieldAcceleration(fieldDate, gradients, frame);
+ final T[] contribution = MathArrays.buildArray(date.getField(), adjointVariables.length);
+ final T[] accelerationXgradient = acceleration.getX().getGradient();
+ final T[] accelerationYgradient = acceleration.getY().getGradient();
+ final T[] accelerationZgradient = acceleration.getZ().getGradient();
+ for (int i = 0; i < 6; i++) {
+ contribution[i] = (accelerationXgradient[i].multiply(adjointVariables[3])
+ .add(accelerationYgradient[i].multiply(adjointVariables[4])).add(accelerationZgradient[i].multiply(adjointVariables[5]))).negate();
+ }
+ return contribution;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public > T getFieldHamiltonianContribution(final FieldAbsoluteDate date,
+ final T[] stateVariables,
+ final T[] adjointVariables,
+ final Frame frame) {
+ final FieldVector3D acceleration = getFieldAcceleration(date, stateVariables, frame);
+ return acceleration.dotProduct(new FieldVector3D<>(adjointVariables[3], adjointVariables[4], adjointVariables[5]));
+ }
+
+ /**
+ * Compute the acceleration vector.
+ *
+ * @param field type
+ * @param date date
+ * @param stateVariables state variables
+ * @param frame propagation frame
+ * @return acceleration vector
+ */
+ protected abstract > FieldVector3D getFieldAcceleration(FieldAbsoluteDate date,
+ T[] stateVariables,
+ Frame frame);
+
+ /**
+ * Build a Cartesian vector whose components are independent variables for automatic differentiation at order 1.
+ * @param stateVariables Cartesian variables
+ * @return vector of independent variables
+ */
+ protected static Gradient[] buildGradientCartesianVector(final double[] stateVariables) {
+ final GradientField field = GradientField.getField(GRADIENT_DIMENSION);
+ final Gradient[] gradients = MathArrays.buildArray(field, GRADIENT_DIMENSION);
+ gradients[0] = Gradient.variable(GRADIENT_DIMENSION, 0, stateVariables[0]);
+ gradients[1] = Gradient.variable(GRADIENT_DIMENSION, 1, stateVariables[1]);
+ gradients[2] = Gradient.variable(GRADIENT_DIMENSION, 2, stateVariables[2]);
+ gradients[3] = Gradient.variable(GRADIENT_DIMENSION, 3, stateVariables[3]);
+ gradients[4] = Gradient.variable(GRADIENT_DIMENSION, 4, stateVariables[4]);
+ gradients[5] = Gradient.variable(GRADIENT_DIMENSION, 5, stateVariables[5]);
+ return gradients;
+ }
+
+ /**
+ * Build a Cartesian vector whose components are independent variables for automatic differentiation at order 1.
+ * @param stateVariables Cartesian variables
+ * @param field type
+ * @return vector of independent variables
+ */
+ protected static > FieldGradient[] buildFieldGradientCartesianVector(final T[] stateVariables) {
+ final FieldGradientField field = FieldGradientField.getField(stateVariables[0].getField(), GRADIENT_DIMENSION);
+ final FieldGradient[] gradients = MathArrays.buildArray(field, GRADIENT_DIMENSION);
+ gradients[0] = FieldGradient.variable(GRADIENT_DIMENSION, 0, stateVariables[0]);
+ gradients[1] = FieldGradient.variable(GRADIENT_DIMENSION, 1, stateVariables[1]);
+ gradients[2] = FieldGradient.variable(GRADIENT_DIMENSION, 2, stateVariables[2]);
+ gradients[3] = FieldGradient.variable(GRADIENT_DIMENSION, 3, stateVariables[3]);
+ gradients[4] = FieldGradient.variable(GRADIENT_DIMENSION, 4, stateVariables[4]);
+ gradients[5] = FieldGradient.variable(GRADIENT_DIMENSION, 5, stateVariables[5]);
+ return gradients;
+ }
+}
diff --git a/src/main/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointGravitationalTerm.java b/src/main/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointGravitationalTerm.java
new file mode 100644
index 0000000000..a4110bc446
--- /dev/null
+++ b/src/main/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointGravitationalTerm.java
@@ -0,0 +1,102 @@
+/* Copyright 2022-2024 Romain Serra
+ * Licensed to CS GROUP (CS) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.orekit.control.indirect.adjoint;
+
+import org.hipparchus.CalculusFieldElement;
+import org.hipparchus.util.MathArrays;
+import org.orekit.frames.Frame;
+import org.orekit.time.AbsoluteDate;
+import org.orekit.time.FieldAbsoluteDate;
+
+/**
+ * Abstract class for common computations regarding adjoint dynamics and gravity for Cartesian coordinates.
+ *
+ * @author Romain Serra
+ * @see CartesianAdjointEquationTerm
+ * @since 12.2
+ */
+public abstract class AbstractCartesianAdjointGravitationalTerm extends AbstractCartesianAdjointEquationTerm {
+
+ /** Body gravitational parameter. */
+ private final double mu;
+
+ /**
+ * Constructor.
+ * @param mu body gravitational parameter
+ */
+ protected AbstractCartesianAdjointGravitationalTerm(final double mu) {
+ this.mu = mu;
+ }
+
+ /**
+ * Getter for the gravitational constant.
+ * @return mu
+ */
+ public double getMu() {
+ return mu;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] getRatesContribution(final AbsoluteDate date, final double[] stateVariables,
+ final double[] adjointVariables, final Frame frame) {
+ final double[] contribution = new double[adjointVariables.length];
+ final double[] positionAdjointContribution = getPositionAdjointContribution(date, stateVariables,
+ adjointVariables, frame);
+ System.arraycopy(positionAdjointContribution, 0, contribution, 0, positionAdjointContribution.length);
+ return contribution;
+ }
+
+ /**
+ * Computes the contribution to position adjoint derivatives.
+ *
+ * @param date date
+ * @param stateVariables state variables
+ * @param adjointVariables adjoint variables
+ * @param frame propagation frame
+ * @return contribution to position adjoint derivatives
+ */
+ protected abstract double[] getPositionAdjointContribution(AbsoluteDate date, double[] stateVariables,
+ double[] adjointVariables, Frame frame);
+
+ /** {@inheritDoc} */
+ @Override
+ public > T[] getFieldRatesContribution(final FieldAbsoluteDate date,
+ final T[] stateVariables,
+ final T[] adjointVariables, final Frame frame) {
+ final T[] contribution = MathArrays.buildArray(date.getField(), adjointVariables.length);
+ final T[] positionAdjointFieldContribution = getPositionAdjointFieldContribution(date, stateVariables,
+ adjointVariables, frame);
+ System.arraycopy(positionAdjointFieldContribution, 0, contribution, 0, positionAdjointFieldContribution.length);
+ return contribution;
+ }
+
+ /**
+ * Computes the contribution to position adjoint derivatives.
+ *
+ * @param field type
+ * @param date date
+ * @param stateVariables state variables
+ * @param adjointVariables adjoint variables
+ * @param frame propagation frame
+ * @return contribution to position adjoint derivatives
+ */
+ protected abstract > T[] getPositionAdjointFieldContribution(FieldAbsoluteDate date,
+ T[] stateVariables,
+ T[] adjointVariables,
+ Frame frame);
+}
diff --git a/src/main/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointNewtonianTerm.java b/src/main/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointNewtonianTerm.java
new file mode 100644
index 0000000000..eed243accd
--- /dev/null
+++ b/src/main/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointNewtonianTerm.java
@@ -0,0 +1,136 @@
+/* Copyright 2022-2024 Romain Serra
+ * Licensed to CS GROUP (CS) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.orekit.control.indirect.adjoint;
+
+import org.hipparchus.CalculusFieldElement;
+import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
+import org.hipparchus.geometry.euclidean.threed.Vector3D;
+import org.hipparchus.util.FastMath;
+import org.hipparchus.util.MathArrays;
+
+/**
+ * Abstract class for common computations regarding adjoint dynamics and Newtonian gravity for Cartesian coordinates.
+ *
+ * @author Romain Serra
+ * @see CartesianAdjointEquationTerm
+ * @since 12.2
+ */
+public abstract class AbstractCartesianAdjointNewtonianTerm extends AbstractCartesianAdjointGravitationalTerm {
+
+ /** Minus three. */
+ private static final double MINUS_THREE = -3;
+
+ /**
+ * Constructor.
+ * @param mu body gravitational parameter
+ */
+ protected AbstractCartesianAdjointNewtonianTerm(final double mu) {
+ super(mu);
+ }
+
+ /**
+ * Computes the generic term of a Newtonian attraction (central or not).
+ * @param relativePosition relative position w.r.t. the body
+ * @param adjointVariables adjoint variables
+ * @return contribution to velocity adjoint derivatives
+ */
+ protected double[] getNewtonianVelocityAdjointContribution(final double[] relativePosition,
+ final double[] adjointVariables) {
+ final double[] contribution = new double[3];
+ final double x = relativePosition[0];
+ final double y = relativePosition[1];
+ final double z = relativePosition[2];
+ final double x2 = x * x;
+ final double y2 = y * y;
+ final double z2 = z * z;
+ final double r2 = x2 + y2 + z2;
+ final double r = FastMath.sqrt(r2);
+ final double factor = getMu() / (r2 * r2 * r);
+ final double xy = x * y;
+ final double xz = x * z;
+ final double yz = y * z;
+ final double pvx = adjointVariables[3];
+ final double pvy = adjointVariables[4];
+ final double pvz = adjointVariables[5];
+ contribution[0] = ((x2 * MINUS_THREE + r2) * pvx + xy * MINUS_THREE * pvy + xz * MINUS_THREE * pvz) * factor;
+ contribution[1] = ((y2 * MINUS_THREE + r2) * pvy + xy * MINUS_THREE * pvx + yz * MINUS_THREE * pvz) * factor;
+ contribution[2] = ((z2 * MINUS_THREE + r2) * pvz + yz * MINUS_THREE * pvy + xz * MINUS_THREE * pvx) * factor;
+ return contribution;
+ }
+
+ /**
+ * Computes the generic term of a Newtonian attraction (central or not).
+ * @param relativePosition relative position w.r.t. the body
+ * @param adjointVariables adjoint variables
+ * @param field type
+ * @return contribution to velocity adjoint derivatives
+ */
+ protected > T[] getFieldNewtonianVelocityAdjointContribution(final T[] relativePosition,
+ final T[] adjointVariables) {
+ final T[] contribution = MathArrays.buildArray(adjointVariables[0].getField(), 3);
+ final T x = relativePosition[0];
+ final T y = relativePosition[1];
+ final T z = relativePosition[2];
+ final T x2 = x.multiply(x);
+ final T y2 = y.multiply(y);
+ final T z2 = z.multiply(z);
+ final T r2 = x2.add(y2).add(z2);
+ final T r = r2.sqrt();
+ final T factor = (r2.multiply(r2).multiply(r)).reciprocal().multiply(getMu());
+ final T xy = x.multiply(y);
+ final T xz = x.multiply(z);
+ final T yz = y.multiply(z);
+ final T pvx = adjointVariables[3];
+ final T pvy = adjointVariables[4];
+ final T pvz = adjointVariables[5];
+ contribution[0] = ((x2.multiply(MINUS_THREE).add(r2)).multiply(pvx).
+ add((xy.multiply(MINUS_THREE)).multiply(pvy)).
+ add((xz.multiply(MINUS_THREE)).multiply(pvz))).multiply(factor);
+ contribution[1] = ((xy.multiply(MINUS_THREE)).multiply(pvx).
+ add((y2.multiply(MINUS_THREE).add(r2)).multiply(pvy)).
+ add((yz.multiply(MINUS_THREE)).multiply(pvz))).multiply(factor);
+ contribution[2] = ((xz.multiply(MINUS_THREE)).multiply(pvx).
+ add((yz.multiply(MINUS_THREE)).multiply(pvy)).
+ add((z2.multiply(MINUS_THREE).add(r2)).multiply(pvz))).multiply(factor);
+ return contribution;
+ }
+
+ /**
+ * Compute the Newtonian acceleration.
+ * @param position position vector as array
+ * @return Newtonian acceleration vector
+ */
+ protected Vector3D getNewtonianAcceleration(final double[] position) {
+ final Vector3D positionVector = new Vector3D(position[0], position[1], position[2]);
+ final double squaredRadius = positionVector.getNormSq();
+ final double factor = -getMu() / (squaredRadius * FastMath.sqrt(squaredRadius));
+ return positionVector.scalarMultiply(factor);
+ }
+
+ /**
+ * Compute the Newtonian acceleration.
+ * @param position position vector as array
+ * @param field type
+ * @return Newtonian acceleration vector
+ */
+ protected > FieldVector3D getFieldNewtonianAcceleration(final T[] position) {
+ final FieldVector3D positionVector = new FieldVector3D<>(position[0], position[1], position[2]);
+ final T squaredRadius = positionVector.getNormSq();
+ final T factor = (squaredRadius.multiply(FastMath.sqrt(squaredRadius))).reciprocal().multiply(-getMu());
+ return positionVector.scalarMultiply(factor);
+ }
+}
diff --git a/src/main/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointNonCentralBodyTerm.java b/src/main/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointNonCentralBodyTerm.java
new file mode 100644
index 0000000000..7264d4a308
--- /dev/null
+++ b/src/main/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointNonCentralBodyTerm.java
@@ -0,0 +1,126 @@
+/* Copyright 2022-2024 Romain Serra
+ * Licensed to CS GROUP (CS) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.orekit.control.indirect.adjoint;
+
+import org.hipparchus.CalculusFieldElement;
+import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
+import org.hipparchus.geometry.euclidean.threed.Vector3D;
+import org.hipparchus.util.MathArrays;
+import org.orekit.frames.Frame;
+import org.orekit.time.AbsoluteDate;
+import org.orekit.time.FieldAbsoluteDate;
+import org.orekit.utils.ExtendedPositionProvider;
+
+/**
+ * Abstract class defining the contributions of a point-mass, single body gravity in the adjoint equations for Cartesian coordinates.
+ * @author Romain Serra
+ * @see CartesianAdjointEquationTerm
+ * @since 12.2
+ */
+public abstract class AbstractCartesianAdjointNonCentralBodyTerm extends AbstractCartesianAdjointNewtonianTerm {
+
+ /** Extended position provider for the body. */
+ private final ExtendedPositionProvider bodyPositionProvider;
+
+ /**
+ * Constructor.
+ * @param mu body gravitational parameter.
+ * @param bodyPositionProvider body position provider
+ */
+ protected AbstractCartesianAdjointNonCentralBodyTerm(final double mu,
+ final ExtendedPositionProvider bodyPositionProvider) {
+ super(mu);
+ this.bodyPositionProvider = bodyPositionProvider;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] getPositionAdjointContribution(final AbsoluteDate date, final double[] stateVariables,
+ final double[] adjointVariables, final Frame frame) {
+ return getNewtonianVelocityAdjointContribution(formRelativePosition(date, stateVariables, frame),
+ adjointVariables);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public > T[] getPositionAdjointFieldContribution(final FieldAbsoluteDate date,
+ final T[] stateVariables,
+ final T[] adjointVariables,
+ final Frame frame) {
+ return getFieldNewtonianVelocityAdjointContribution(formFieldRelativePosition(date, stateVariables, frame),
+ adjointVariables);
+ }
+
+ /**
+ * Get body's position.
+ * @param date date
+ * @param frame frame
+ * @return position vector
+ */
+ protected Vector3D getBodyPosition(final AbsoluteDate date, final Frame frame) {
+ return bodyPositionProvider.getPosition(date, frame);
+ }
+
+ /**
+ * Get body's position.
+ * @param field type
+ * @param date date
+ * @param frame frame
+ * @return position vector
+ */
+ protected > FieldVector3D getFieldBodyPosition(final FieldAbsoluteDate date,
+ final Frame frame) {
+ return bodyPositionProvider.getPosition(date, frame);
+ }
+
+ /**
+ * Form relative position vector w.r.t. body.
+ * @param date date
+ * @param stateVariables Cartesian variables
+ * @param frame frame where Cartesian coordinates apply
+ * @return relative position vector as array
+ */
+ protected double[] formRelativePosition(final AbsoluteDate date, final double[] stateVariables, final Frame frame) {
+ final Vector3D bodyPosition = getBodyPosition(date, frame);
+ final double x = stateVariables[0] - bodyPosition.getX();
+ final double y = stateVariables[1] - bodyPosition.getY();
+ final double z = stateVariables[2] - bodyPosition.getZ();
+ return new double[] { x, y, z };
+ }
+
+ /**
+ * Form relative position vector w.r.t. body.
+ * @param date date
+ * @param stateVariables Cartesian variables
+ * @param frame frame where Cartesian coordinates apply
+ * @param field type
+ * @return relative position vector as array
+ */
+ protected > T[] formFieldRelativePosition(final FieldAbsoluteDate date,
+ final T[] stateVariables,
+ final Frame frame) {
+ final FieldVector3D bodyPosition = getFieldBodyPosition(date, frame);
+ final T x = stateVariables[0].subtract(bodyPosition.getX());
+ final T y = stateVariables[1].subtract(bodyPosition.getY());
+ final T z = stateVariables[2].subtract(bodyPosition.getZ());
+ final T[] relativePosition = MathArrays.buildArray(date.getField(), 3);
+ relativePosition[0] = x;
+ relativePosition[1] = y;
+ relativePosition[2] = z;
+ return relativePosition;
+ }
+}
diff --git a/src/main/java/org/orekit/control/indirect/adjoint/CartesianAdjointDerivativesProvider.java b/src/main/java/org/orekit/control/indirect/adjoint/CartesianAdjointDerivativesProvider.java
new file mode 100644
index 0000000000..057191fa21
--- /dev/null
+++ b/src/main/java/org/orekit/control/indirect/adjoint/CartesianAdjointDerivativesProvider.java
@@ -0,0 +1,146 @@
+/* Copyright 2022-2024 Romain Serra
+ * Licensed to CS GROUP (CS) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.orekit.control.indirect.adjoint;
+
+import org.hipparchus.geometry.euclidean.threed.Vector3D;
+import org.hipparchus.util.FastMath;
+import org.orekit.control.indirect.adjoint.cost.CartesianCost;
+import org.orekit.errors.OrekitException;
+import org.orekit.errors.OrekitMessages;
+import org.orekit.frames.Frame;
+import org.orekit.orbits.OrbitType;
+import org.orekit.propagation.SpacecraftState;
+import org.orekit.propagation.integration.AdditionalDerivativesProvider;
+import org.orekit.propagation.integration.CombinedDerivatives;
+import org.orekit.time.AbsoluteDate;
+import org.orekit.utils.PVCoordinates;
+
+/**
+ * Class defining the adjoint dynamics, as defined in the Pontryagin Maximum Principle, in the case where Cartesian coordinates in an inertial frame are the dependent variable.
+ * The time derivatives of the adjoint variables are obtained by differentiating the so-called Hamiltonian.
+ * They depend on the force model and the cost being minimized.
+ * For the former, it is the user's responsibility to make sure the provided {@link CartesianAdjointEquationTerm} are consistent with the {@link org.orekit.forces.ForceModel}.
+ * For the latter, the cost function is represented through the interface {@link CartesianCost}.
+ * @author Romain Serra
+ * @see AdditionalDerivativesProvider
+ * @see org.orekit.propagation.numerical.NumericalPropagator
+ * @since 12.2
+ */
+public class CartesianAdjointDerivativesProvider extends AbstractCartesianAdjointDerivativesProvider implements AdditionalDerivativesProvider {
+
+ /** Contributing terms to the adjoint equation. */
+ private final CartesianAdjointEquationTerm[] adjointEquationTerms;
+
+ /**
+ * Constructor.
+ * @param cost cost function
+ * @param adjointEquationTerms terms contributing to the adjoint equations. If none, then the propagator should have no forces, not even a Newtonian attraction.
+ */
+ public CartesianAdjointDerivativesProvider(final CartesianCost cost,
+ final CartesianAdjointEquationTerm... adjointEquationTerms) {
+ super(cost);
+ this.adjointEquationTerms = adjointEquationTerms;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void init(final SpacecraftState initialState, final AbsoluteDate target) {
+ AdditionalDerivativesProvider.super.init(initialState, target);
+ if (initialState.isOrbitDefined() && initialState.getOrbit().getType() != OrbitType.CARTESIAN) {
+ throw new OrekitException(OrekitMessages.WRONG_COORDINATES_FOR_ADJOINT_EQUATION);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public CombinedDerivatives combinedDerivatives(final SpacecraftState state) {
+ // pre-computations
+ final double[] adjointVariables = state.getAdditionalState(getName());
+ final int adjointDimension = getDimension();
+ final double[] additionalDerivatives = new double[adjointDimension];
+ final double[] cartesianVariablesAndMass = formCartesianAndMassVector(state);
+ final double mass = state.getMass();
+
+ // mass flow rate and control acceleration
+ final double[] mainDerivativesIncrements = new double[7];
+ final Vector3D thrustAccelerationVector = getCost().getThrustAccelerationVector(adjointVariables, mass);
+ mainDerivativesIncrements[3] = thrustAccelerationVector.getX();
+ mainDerivativesIncrements[4] = thrustAccelerationVector.getY();
+ mainDerivativesIncrements[5] = thrustAccelerationVector.getZ();
+ mainDerivativesIncrements[6] = -getCost().getMassFlowRateFactor() * thrustAccelerationVector.getNorm() * mass;
+
+ // Cartesian position adjoint
+ additionalDerivatives[3] = -adjointVariables[0];
+ additionalDerivatives[4] = -adjointVariables[1];
+ additionalDerivatives[5] = -adjointVariables[2];
+
+ // update position and velocity adjoint derivatives
+ final AbsoluteDate date = state.getDate();
+ final Frame propagationFrame = state.getFrame();
+ for (final CartesianAdjointEquationTerm equationTerm: adjointEquationTerms) {
+ final double[] contribution = equationTerm.getRatesContribution(date, cartesianVariablesAndMass, adjointVariables,
+ propagationFrame);
+ for (int i = 0; i < FastMath.min(adjointDimension, contribution.length); i++) {
+ additionalDerivatives[i] += contribution[i];
+ }
+ }
+
+ // other
+ getCost().updateAdjointDerivatives(adjointVariables, mass, additionalDerivatives);
+
+ return new CombinedDerivatives(additionalDerivatives, mainDerivativesIncrements);
+ }
+
+ /**
+ * Gather Cartesian variables and mass in same vector.
+ * @param state propagation state
+ * @return Cartesian variables and mass
+ */
+ private double[] formCartesianAndMassVector(final SpacecraftState state) {
+ final double[] cartesianVariablesAndMass = new double[7];
+ final PVCoordinates pvCoordinates = state.getPVCoordinates();
+ System.arraycopy(pvCoordinates.getPosition().toArray(), 0, cartesianVariablesAndMass, 0, 3);
+ System.arraycopy(pvCoordinates.getVelocity().toArray(), 0, cartesianVariablesAndMass, 3, 3);
+ final double mass = state.getMass();
+ cartesianVariablesAndMass[6] = mass;
+ return cartesianVariablesAndMass;
+ }
+
+ /**
+ * Evaluate the Hamiltonian from Pontryagin's Maximum Principle.
+ * @param state state assumed to hold the adjoint variables
+ * @return Hamiltonian
+ */
+ public double evaluateHamiltonian(final SpacecraftState state) {
+ final double[] cartesianAndMassVector = formCartesianAndMassVector(state);
+ final double[] adjointVariables = state.getAdditionalState(getName());
+ double hamiltonian = adjointVariables[0] * cartesianAndMassVector[3] + adjointVariables[1] * cartesianAndMassVector[4] + adjointVariables[2] * cartesianAndMassVector[5];
+ final AbsoluteDate date = state.getDate();
+ final Frame propagationFrame = state.getFrame();
+ for (final CartesianAdjointEquationTerm adjointEquationTerm : adjointEquationTerms) {
+ hamiltonian += adjointEquationTerm.getHamiltonianContribution(date, adjointVariables, adjointVariables,
+ propagationFrame);
+ }
+ if (adjointVariables.length != 6) {
+ final double mass = state.getMass();
+ final double thrustAccelerationNorm = getCost().getThrustAccelerationVector(adjointVariables, mass).getNorm();
+ hamiltonian -= getCost().getMassFlowRateFactor() * adjointVariables[6] * thrustAccelerationNorm * mass;
+ }
+ hamiltonian += getCost().getHamiltonianContribution(adjointVariables, state.getMass());
+ return hamiltonian;
+ }
+}
diff --git a/src/main/java/org/orekit/control/indirect/adjoint/CartesianAdjointEquationTerm.java b/src/main/java/org/orekit/control/indirect/adjoint/CartesianAdjointEquationTerm.java
new file mode 100644
index 0000000000..6962cd5c8c
--- /dev/null
+++ b/src/main/java/org/orekit/control/indirect/adjoint/CartesianAdjointEquationTerm.java
@@ -0,0 +1,81 @@
+/* Copyright 2022-2024 Romain Serra
+ * Licensed to CS GROUP (CS) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.orekit.control.indirect.adjoint;
+
+import org.hipparchus.CalculusFieldElement;
+import org.orekit.frames.Frame;
+import org.orekit.time.AbsoluteDate;
+import org.orekit.time.FieldAbsoluteDate;
+
+/**
+ * Interface to define terms in the adjoint equations and Hamiltonian for Cartesian coordinates.
+ * @author Romain Serra
+ * @see CartesianAdjointDerivativesProvider
+ * @see FieldCartesianAdjointDerivativesProvider
+ * @since 12.2
+ */
+public interface CartesianAdjointEquationTerm {
+
+ /**
+ * Computes the contribution to the rates of the adjoint variables.
+ *
+ * @param date date
+ * @param stateVariables state variables
+ * @param adjointVariables adjoint variables
+ * @param frame propagation frame
+ * @return contribution to the adjoint derivative vector
+ */
+ double[] getRatesContribution(AbsoluteDate date, double[] stateVariables, double[] adjointVariables, Frame frame);
+
+ /**
+ * Computes the contribution to the rates of the adjoint variables.
+ *
+ * @param field type
+ * @param date date
+ * @param stateVariables state variables
+ * @param adjointVariables adjoint variables
+ * @param frame propagation frame
+ * @return contribution to the adjoint derivative vector
+ */
+ > T[] getFieldRatesContribution(FieldAbsoluteDate date, T[] stateVariables, T[] adjointVariables, Frame frame);
+
+ /**
+ * Computes the contribution to the Hamiltonian.
+ *
+ * @param date date
+ * @param stateVariables state variables
+ * @param adjointVariables adjoint variables
+ * @param frame propagation frame
+ * @return contribution to the Hamiltonian
+ */
+ double getHamiltonianContribution(AbsoluteDate date, double[] stateVariables, double[] adjointVariables, Frame frame);
+
+ /**
+ * Computes the contribution to the Hamiltonian.
+ *
+ * @param date date
+ * @param stateVariables state variables
+ * @param adjointVariables adjoint variables
+ * @param frame propagation frame
+ * @param field type
+ * @return contribution to the Hamiltonian
+ */
+ > T getFieldHamiltonianContribution(FieldAbsoluteDate date,
+ T[] stateVariables,
+ T[] adjointVariables, Frame frame);
+
+}
diff --git a/src/main/java/org/orekit/control/indirect/adjoint/CartesianAdjointInertialTerm.java b/src/main/java/org/orekit/control/indirect/adjoint/CartesianAdjointInertialTerm.java
new file mode 100644
index 0000000000..cb90090084
--- /dev/null
+++ b/src/main/java/org/orekit/control/indirect/adjoint/CartesianAdjointInertialTerm.java
@@ -0,0 +1,169 @@
+/* Copyright 2022-2024 Romain Serra
+ * Licensed to CS GROUP (CS) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.orekit.control.indirect.adjoint;
+
+import org.hipparchus.CalculusFieldElement;
+import org.hipparchus.analysis.differentiation.FieldGradient;
+import org.hipparchus.analysis.differentiation.Gradient;
+import org.hipparchus.geometry.euclidean.threed.FieldRotation;
+import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
+import org.hipparchus.geometry.euclidean.threed.Rotation;
+import org.hipparchus.geometry.euclidean.threed.Vector3D;
+import org.hipparchus.util.MathArrays;
+import org.orekit.errors.OrekitIllegalArgumentException;
+import org.orekit.errors.OrekitMessages;
+import org.orekit.frames.FieldTransform;
+import org.orekit.frames.Frame;
+import org.orekit.frames.Transform;
+import org.orekit.time.AbsoluteDate;
+import org.orekit.time.FieldAbsoluteDate;
+
+/**
+ * Class defining inertial forces' contributions in the adjoint equations for Cartesian coordinates.
+ * If present, then the propagator should also include inertial forces.
+ * @author Romain Serra
+ * @see CartesianAdjointEquationTerm
+ * @see org.orekit.forces.inertia.InertialForces
+ * @since 12.2
+ */
+public class CartesianAdjointInertialTerm extends AbstractCartesianAdjointEquationTerm {
+
+ /** Reference frame for inertial forces. Must be inertial. */
+ private final Frame referenceInertialFrame;
+
+ /**
+ * Constructor.
+ * @param referenceInertialFrame reference inertial frame
+ */
+ public CartesianAdjointInertialTerm(final Frame referenceInertialFrame) {
+ this.referenceInertialFrame = referenceInertialFrame;
+ if (!referenceInertialFrame.isPseudoInertial()) {
+ throw new OrekitIllegalArgumentException(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME_NOT_SUITABLE_AS_REFERENCE_FOR_INERTIAL_FORCES,
+ referenceInertialFrame.getName());
+ }
+ }
+
+ /**
+ * Getter for reference frame.
+ * @return frame
+ */
+ public Frame getReferenceInertialFrame() {
+ return referenceInertialFrame;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] getRatesContribution(final AbsoluteDate date, final double[] stateVariables,
+ final double[] adjointVariables, final Frame frame) {
+ final double[] contribution = new double[adjointVariables.length];
+ final Gradient[] gradients = buildGradientCartesianVector(stateVariables);
+ final Transform transform = getReferenceInertialFrame().getTransformTo(frame, date);
+ final FieldTransform fieldTransform = new FieldTransform<>(gradients[0].getField(), transform);
+ final FieldVector3D acceleration = getFieldAcceleration(fieldTransform, gradients);
+ final double[] accelerationXgradient = acceleration.getX().getGradient();
+ final double[] accelerationYgradient = acceleration.getY().getGradient();
+ final double[] accelerationZgradient = acceleration.getZ().getGradient();
+ for (int i = 0; i < 6; i++) {
+ contribution[i] = -(accelerationXgradient[i] * adjointVariables[3] + accelerationYgradient[i] * adjointVariables[4] + accelerationZgradient[i] * adjointVariables[5]);
+ }
+ return contribution;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public > T[] getFieldRatesContribution(final FieldAbsoluteDate date,
+ final T[] stateVariables,
+ final T[] adjointVariables, final Frame frame) {
+ final T[] contribution = MathArrays.buildArray(date.getField(), 6);
+ final FieldGradient[] gradients = buildFieldGradientCartesianVector(stateVariables);
+ final FieldTransform transform = getReferenceInertialFrame().getTransformTo(frame, date);
+ final FieldTransform> fieldTransform = new FieldTransform<>(gradients[0].getField(),
+ new Transform(date.toAbsoluteDate(), transform.getAngular().toAngularCoordinates()));
+ final FieldVector3D> acceleration = getFieldAcceleration(fieldTransform, gradients);
+ final T[] accelerationXgradient = acceleration.getX().getGradient();
+ final T[] accelerationYgradient = acceleration.getY().getGradient();
+ final T[] accelerationZgradient = acceleration.getZ().getGradient();
+ for (int i = 0; i < 6; i++) {
+ contribution[i] = (accelerationXgradient[i].multiply(adjointVariables[3])
+ .add(accelerationYgradient[i].multiply(adjointVariables[4])).add(accelerationZgradient[i].multiply(adjointVariables[5]))).negate();
+ }
+ return contribution;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected Vector3D getAcceleration(final AbsoluteDate date, final double[] stateVariables,
+ final Frame frame) {
+ final Transform transform = getReferenceInertialFrame().getTransformTo(frame, date);
+ return getAcceleration(transform, stateVariables);
+ }
+
+ /**
+ * Evaluates the inertial acceleration vector.
+ * @param inertialToPropagationFrame transform from inertial to propagation frame
+ * @param stateVariables state variables
+ * @return acceleration
+ */
+ public Vector3D getAcceleration(final Transform inertialToPropagationFrame, final double[] stateVariables) {
+ final Vector3D a1 = inertialToPropagationFrame.getCartesian().getAcceleration();
+ final Rotation r1 = inertialToPropagationFrame.getAngular().getRotation();
+ final Vector3D o1 = inertialToPropagationFrame.getAngular().getRotationRate();
+ final Vector3D oDot1 = inertialToPropagationFrame.getAngular().getRotationAcceleration();
+
+ final Vector3D p2 = new Vector3D(stateVariables[0], stateVariables[1], stateVariables[2]);
+ final Vector3D v2 = new Vector3D(stateVariables[3], stateVariables[4], stateVariables[5]);
+
+ final Vector3D crossCrossP = Vector3D.crossProduct(o1, Vector3D.crossProduct(o1, p2));
+ final Vector3D crossV = Vector3D.crossProduct(o1, v2);
+ final Vector3D crossDotP = Vector3D.crossProduct(oDot1, p2);
+
+ return r1.applyTo(a1).subtract(new Vector3D(2, crossV, 1, crossCrossP, 1, crossDotP));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected > FieldVector3D getFieldAcceleration(final FieldAbsoluteDate date,
+ final T[] stateVariables,
+ final Frame frame) {
+ final FieldTransform transform = getReferenceInertialFrame().getTransformTo(frame, date);
+ return getFieldAcceleration(transform, stateVariables);
+ }
+
+ /**
+ * Evaluates the inertial acceleration vector in Field.
+ * @param inertialToPropagationFrame transform from inertial to propagation frame
+ * @param stateVariables state variables
+ * @param field type
+ * @return acceleration
+ */
+ private > FieldVector3D getFieldAcceleration(final FieldTransform inertialToPropagationFrame,
+ final T[] stateVariables) {
+ final FieldVector3D a1 = inertialToPropagationFrame.getCartesian().getAcceleration();
+ final FieldRotation r1 = inertialToPropagationFrame.getAngular().getRotation();
+ final FieldVector3D o1 = inertialToPropagationFrame.getAngular().getRotationRate();
+ final FieldVector3D oDot1 = inertialToPropagationFrame.getAngular().getRotationAcceleration();
+
+ final FieldVector3D p2 = new FieldVector3D<>(stateVariables[0], stateVariables[1], stateVariables[2]);
+ final FieldVector3D v2 = new FieldVector3D<>(stateVariables[3], stateVariables[4], stateVariables[5]);
+
+ final FieldVector3D crossCrossP = FieldVector3D.crossProduct(o1, FieldVector3D.crossProduct(o1, p2));
+ final FieldVector3D crossV = FieldVector3D.crossProduct(o1, v2);
+ final FieldVector3D crossDotP = FieldVector3D.crossProduct(oDot1, p2);
+
+ return r1.applyTo(a1).subtract(new FieldVector3D<>(2, crossV, 1, crossCrossP, 1, crossDotP));
+ }
+}
diff --git a/src/main/java/org/orekit/control/indirect/adjoint/CartesianAdjointJ2Term.java b/src/main/java/org/orekit/control/indirect/adjoint/CartesianAdjointJ2Term.java
new file mode 100644
index 0000000000..bd12b2c7fe
--- /dev/null
+++ b/src/main/java/org/orekit/control/indirect/adjoint/CartesianAdjointJ2Term.java
@@ -0,0 +1,170 @@
+/* Copyright 2022-2024 Romain Serra
+ * Licensed to CS GROUP (CS) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.orekit.control.indirect.adjoint;
+
+import org.hipparchus.CalculusFieldElement;
+import org.hipparchus.Field;
+import org.hipparchus.analysis.differentiation.FieldGradient;
+import org.hipparchus.analysis.differentiation.FieldGradientField;
+import org.hipparchus.analysis.differentiation.Gradient;
+import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
+import org.hipparchus.geometry.euclidean.threed.Vector3D;
+import org.hipparchus.util.MathArrays;
+import org.orekit.forces.gravity.J2OnlyPerturbation;
+import org.orekit.frames.FieldStaticTransform;
+import org.orekit.frames.Frame;
+import org.orekit.frames.StaticTransform;
+import org.orekit.time.AbsoluteDate;
+import org.orekit.time.FieldAbsoluteDate;
+
+/**
+ * Class defining a (constant) J2 contributions in the adjoint equations for Cartesian coordinates.
+ * If present, then the propagator should also include a constant J2 term (oblateness) of the central body.
+ * @author Romain Serra
+ * @see CartesianAdjointEquationTerm
+ * @see org.orekit.forces.gravity.J2OnlyPerturbation
+ * @since 12.2
+ */
+public class CartesianAdjointJ2Term extends AbstractCartesianAdjointGravitationalTerm {
+
+ /** J2 coefficient. */
+ private final double j2;
+
+ /** Equatorial radius of central body. */
+ private final double rEq;
+
+ /** Frame where J2 applies. */
+ private final Frame j2Frame;
+
+ /**
+ * Constructor.
+ * @param mu central body gravitational parameter.
+ * @param rEq equatorial radius
+ * @param j2 J2 coefficient
+ * @param j2Frame J2 frame
+ */
+ public CartesianAdjointJ2Term(final double mu, final double rEq, final double j2,
+ final Frame j2Frame) {
+ super(mu);
+ this.j2 = j2;
+ this.rEq = rEq;
+ this.j2Frame = j2Frame;
+ }
+
+ /**
+ * Getter for central body equatorial radius.
+ * @return equatorial radius
+ */
+ public double getrEq() {
+ return rEq;
+ }
+
+ /**
+ * Getter for J2.
+ * @return J2 coefficient
+ */
+ public double getJ2() {
+ return j2;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] getPositionAdjointContribution(final AbsoluteDate date, final double[] stateVariables,
+ final double[] adjointVariables, final Frame frame) {
+ final double[] contribution = new double[3];
+ final int numberOfGradientVariables = 3;
+ final FieldVector3D