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>> + 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 position = new FieldVector3D<>(Gradient.variable(numberOfGradientVariables, 0, stateVariables[0]), + Gradient.variable(numberOfGradientVariables, 1, stateVariables[1]), + Gradient.variable(numberOfGradientVariables, 2, stateVariables[2])); + final StaticTransform transform = frame.getStaticTransformTo(j2Frame, date); + final FieldVector3D positionInJ2Frame = transform.transformPosition(position); + final Gradient fieldJ2 = Gradient.constant(numberOfGradientVariables, j2); + final FieldVector3D accelerationInJ2Frame = J2OnlyPerturbation.computeAccelerationInJ2Frame(positionInJ2Frame, + getMu(), rEq, fieldJ2); + final FieldVector3D acceleration = transform.getStaticInverse().transformVector(accelerationInJ2Frame); + final double pvx = adjointVariables[3]; + final double pvy = adjointVariables[4]; + final double pvz = adjointVariables[5]; + final double[] gradientAccelerationX = acceleration.getX().getGradient(); + final double[] gradientAccelerationY = acceleration.getY().getGradient(); + final double[] gradientAccelerationZ = acceleration.getZ().getGradient(); + contribution[0] = -(gradientAccelerationX[0] * pvx + gradientAccelerationY[0] * pvy + gradientAccelerationZ[0] * pvz); + contribution[1] = -(gradientAccelerationX[1] * pvx + gradientAccelerationY[1] * pvy + gradientAccelerationZ[1] * pvz); + contribution[2] = -(gradientAccelerationX[2] * pvx + gradientAccelerationY[2] * pvy + gradientAccelerationZ[2] * pvz); + return contribution; + } + + /** {@inheritDoc} */ + @Override + public > T[] getPositionAdjointFieldContribution(final FieldAbsoluteDate date, + final T[] stateVariables, + final T[] adjointVariables, + final Frame frame) { + final Field field = adjointVariables[0].getField(); + final T[] contribution = MathArrays.buildArray(field, 3); + final int numberOfGradientVariables = 3; + final FieldVector3D> position = new FieldVector3D<>(FieldGradient.variable(numberOfGradientVariables, 0, stateVariables[0]), + FieldGradient.variable(numberOfGradientVariables, 1, stateVariables[1]), + FieldGradient.variable(numberOfGradientVariables, 2, stateVariables[2])); + final T shift = date.durationFrom(date.toAbsoluteDate()); + final FieldGradientField gradientField = FieldGradientField.getField(field, 3); + final FieldAbsoluteDate> gradientDate = new FieldAbsoluteDate<>(gradientField, date.toAbsoluteDate()) + .shiftedBy(FieldGradient.constant(numberOfGradientVariables, shift)); + final FieldStaticTransform> transform = frame.getStaticTransformTo(j2Frame, gradientDate); + final FieldVector3D> positionInJ2Frame = transform.transformPosition(position); + final FieldGradient fieldJ2 = FieldGradient.constant(numberOfGradientVariables, field.getZero().newInstance(j2)); + final FieldVector3D> accelerationInJ2Frame = J2OnlyPerturbation.computeAccelerationInJ2Frame(positionInJ2Frame, + getMu(), rEq, fieldJ2); + final FieldVector3D> acceleration = transform.getStaticInverse().transformVector(accelerationInJ2Frame); + final T pvx = adjointVariables[3]; + final T pvy = adjointVariables[4]; + final T pvz = adjointVariables[5]; + final T[] gradientAccelerationX = acceleration.getX().getGradient(); + final T[] gradientAccelerationY = acceleration.getY().getGradient(); + final T[] gradientAccelerationZ = acceleration.getZ().getGradient(); + contribution[0] = gradientAccelerationX[0].multiply(pvx).add(gradientAccelerationY[0].multiply(pvy)).add(gradientAccelerationZ[0].multiply(pvz)); + contribution[1] = gradientAccelerationX[1].multiply(pvx).add(gradientAccelerationY[1].multiply(pvy)).add(gradientAccelerationZ[1].multiply(pvz)); + contribution[2] = gradientAccelerationX[2].multiply(pvx).add(gradientAccelerationY[2].multiply(pvy)).add(gradientAccelerationZ[2].multiply(pvz)); + contribution[0] = contribution[0].negate(); + contribution[1] = contribution[1].negate(); + contribution[2] = contribution[2].negate(); + return contribution; + } + + /** {@inheritDoc} */ + @Override + public Vector3D getAcceleration(final AbsoluteDate date, final double[] stateVariables, + final Frame frame) { + final StaticTransform transform = frame.getStaticTransformTo(j2Frame, date); + final Vector3D positionInJ2Frame = transform.transformPosition(new Vector3D(stateVariables[0], stateVariables[1], stateVariables[2])); + final Vector3D accelerationInJ2Frame = J2OnlyPerturbation.computeAccelerationInJ2Frame(positionInJ2Frame, + getMu(), rEq, getJ2()); + return transform.getStaticInverse().transformVector(accelerationInJ2Frame); + } + + /** {@inheritDoc} */ + @Override + public > FieldVector3D getFieldAcceleration(final FieldAbsoluteDate date, + final T[] stateVariables, + final Frame frame) { + final FieldStaticTransform transform = frame.getStaticTransformTo(j2Frame, date); + final FieldVector3D positionInJ2Frame = transform.transformPosition(new FieldVector3D<>(stateVariables[0], stateVariables[1], stateVariables[2])); + final FieldVector3D accelerationInJ2Frame = J2OnlyPerturbation.computeAccelerationInJ2Frame(positionInJ2Frame, + getMu(), rEq, date.getField().getZero().newInstance(getJ2())); + return transform.getStaticInverse().transformVector(accelerationInJ2Frame); + } +} diff --git a/src/main/java/org/orekit/control/indirect/adjoint/CartesianAdjointKeplerianTerm.java b/src/main/java/org/orekit/control/indirect/adjoint/CartesianAdjointKeplerianTerm.java new file mode 100644 index 0000000000..aaf7b12ad1 --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/adjoint/CartesianAdjointKeplerianTerm.java @@ -0,0 +1,74 @@ +/* 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.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; + +/** + * Class defining the Keplerian contributions in the adjoint equations for Cartesian coordinates. + * If present, then the propagator should also include the Newtonian attraction of a central body. + * @author Romain Serra + * @see CartesianAdjointEquationTerm + * @see org.orekit.forces.gravity.NewtonianAttraction + * @since 12.2 + */ +public class CartesianAdjointKeplerianTerm extends AbstractCartesianAdjointNewtonianTerm { + + /** + * Constructor. + * @param mu central body gravitational parameter + */ + public CartesianAdjointKeplerianTerm(final double mu) { + super(mu); + } + + /** {@inheritDoc} */ + @Override + public double[] getPositionAdjointContribution(final AbsoluteDate date, final double[] stateVariables, + final double[] adjointVariables, final Frame frame) { + return getNewtonianVelocityAdjointContribution(stateVariables, adjointVariables); + } + + /** {@inheritDoc} */ + @Override + public > T[] getPositionAdjointFieldContribution(final FieldAbsoluteDate date, + final T[] stateVariables, + final T[] adjointVariables, + final Frame frame) { + return getFieldNewtonianVelocityAdjointContribution(stateVariables, adjointVariables); + } + + /** {@inheritDoc} */ + @Override + protected Vector3D getAcceleration(final AbsoluteDate date, final double[] stateVariables, + final Frame frame) { + return getNewtonianAcceleration(stateVariables); + } + + /** {@inheritDoc} */ + @Override + protected > FieldVector3D getFieldAcceleration(final FieldAbsoluteDate date, + final T[] stateVariables, + final Frame frame) { + return getFieldNewtonianAcceleration(stateVariables); + } +} diff --git a/src/main/java/org/orekit/control/indirect/adjoint/CartesianAdjointSingleBodyTerm.java b/src/main/java/org/orekit/control/indirect/adjoint/CartesianAdjointSingleBodyTerm.java new file mode 100644 index 0000000000..8e4e8446b9 --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/adjoint/CartesianAdjointSingleBodyTerm.java @@ -0,0 +1,63 @@ +/* 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.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.ExtendedPositionProvider; + +/** + * Class defining the contributions of a point-mass, single body gravity in the adjoint equations for Cartesian coordinates. + * If present, then the propagator should also include the Newtonian attraction of a body. + * This is similar to {@link CartesianAdjointKeplerianTerm} but with the body not necessarily a central one. + * @author Romain Serra + * @see CartesianAdjointEquationTerm + * @see org.orekit.forces.gravity.SingleBodyAbsoluteAttraction + * @since 12.2 + */ +public class CartesianAdjointSingleBodyTerm extends AbstractCartesianAdjointNonCentralBodyTerm { + + /** + * Constructor. + * @param mu body gravitational parameter. + * @param bodyPositionProvider body position provider + */ + public CartesianAdjointSingleBodyTerm(final double mu, final ExtendedPositionProvider bodyPositionProvider) { + super(mu, bodyPositionProvider); + } + + /** {@inheritDoc} */ + @Override + public Vector3D getAcceleration(final AbsoluteDate date, final double[] stateVariables, + final Frame frame) { + final double[] relativePosition = formRelativePosition(date, stateVariables, frame); + return getNewtonianAcceleration(relativePosition); + } + + /** {@inheritDoc} */ + @Override + public > FieldVector3D getFieldAcceleration(final FieldAbsoluteDate date, + final T[] stateVariables, + final Frame frame) { + final T[] relativePosition = formFieldRelativePosition(date, stateVariables, frame); + return getFieldNewtonianAcceleration(relativePosition); + } +} diff --git a/src/main/java/org/orekit/control/indirect/adjoint/CartesianAdjointThirdBodyTerm.java b/src/main/java/org/orekit/control/indirect/adjoint/CartesianAdjointThirdBodyTerm.java new file mode 100644 index 0000000000..1151793b94 --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/adjoint/CartesianAdjointThirdBodyTerm.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.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; +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; + +/** + * Class defining the contributions of a point-mass, third body in the adjoint equations for Cartesian coordinates. + * If present, then the propagator should also include a {@link org.orekit.forces.gravity.ThirdBodyAttraction}. + * @author Romain Serra + * @see CartesianAdjointEquationTerm + * @see org.orekit.forces.gravity.ThirdBodyAttraction + * @since 12.2 + */ +public class CartesianAdjointThirdBodyTerm extends AbstractCartesianAdjointNonCentralBodyTerm { + + /** + * Constructor. + * @param mu body gravitational parameter. + * @param bodyPositionProvider body position provider + */ + public CartesianAdjointThirdBodyTerm(final double mu, final ExtendedPositionProvider bodyPositionProvider) { + super(mu, bodyPositionProvider); + } + + /** {@inheritDoc} */ + @Override + public Vector3D getAcceleration(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(); + final Vector3D newtonianAcceleration = getNewtonianAcceleration(new double[] {x, y, z}); + final double rBody2 = bodyPosition.getNormSq(); + final Vector3D bodyCentralAcceleration = bodyPosition.scalarMultiply(getMu() / (rBody2 * FastMath.sqrt(rBody2))); + return newtonianAcceleration.subtract(bodyCentralAcceleration); + } + + /** {@inheritDoc} */ + @Override + public > FieldVector3D getFieldAcceleration(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; + final FieldVector3D newtonianAcceleration = getFieldNewtonianAcceleration(relativePosition); + final T rBody2 = bodyPosition.getNormSq(); + final T factor = rBody2.multiply(rBody2.sqrt()).reciprocal().multiply(getMu()); + final FieldVector3D bodyCentralAcceleration = bodyPosition.scalarMultiply(factor); + return newtonianAcceleration.subtract(bodyCentralAcceleration); + } +} diff --git a/src/main/java/org/orekit/control/indirect/adjoint/FieldCartesianAdjointDerivativesProvider.java b/src/main/java/org/orekit/control/indirect/adjoint/FieldCartesianAdjointDerivativesProvider.java new file mode 100644 index 0000000000..b0b334f254 --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/adjoint/FieldCartesianAdjointDerivativesProvider.java @@ -0,0 +1,152 @@ +/* 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.util.FastMath; +import org.hipparchus.util.MathArrays; +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.FieldSpacecraftState; +import org.orekit.propagation.integration.FieldAdditionalDerivativesProvider; +import org.orekit.propagation.integration.FieldCombinedDerivatives; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldPVCoordinates; + +/** + * Class defining the Field version of the adjoint dynamics for Cartesian coordinates, as defined in the Pontryagin Maximum Principle. + * @author Romain Serra + * @see FieldAdditionalDerivativesProvider + * @see org.orekit.propagation.numerical.FieldNumericalPropagator + * @see CartesianAdjointDerivativesProvider + * @since 12.2 + */ +public class FieldCartesianAdjointDerivativesProvider> extends AbstractCartesianAdjointDerivativesProvider implements FieldAdditionalDerivativesProvider { + + /** Contributing terms to the adjoint equation. */ + private final CartesianAdjointEquationTerm[] adjointEquationTerms; + + /** + * Constructor. + * @param cost cost function + * @param adjointEquationTerms terms contributing to the adjoint equations + */ + public FieldCartesianAdjointDerivativesProvider(final CartesianCost cost, + final CartesianAdjointEquationTerm... adjointEquationTerms) { + super(cost); + this.adjointEquationTerms = adjointEquationTerms; + } + + /** {@inheritDoc} */ + @Override + public void init(final FieldSpacecraftState initialState, final FieldAbsoluteDate target) { + FieldAdditionalDerivativesProvider.super.init(initialState, target); + if (initialState.isOrbitDefined() && initialState.getOrbit().getType() != OrbitType.CARTESIAN) { + throw new OrekitException(OrekitMessages.WRONG_COORDINATES_FOR_ADJOINT_EQUATION); + } + } + + /** {@inheritDoc} */ + @Override + public FieldCombinedDerivatives combinedDerivatives(final FieldSpacecraftState state) { + // pre-computations + final T mass = state.getMass(); + final T[] adjointVariables = state.getAdditionalState(getName()); + final int adjointDimension = getDimension(); + final T[] additionalDerivatives = MathArrays.buildArray(mass.getField(), adjointDimension); + final T[] cartesianVariablesAndMass = formCartesianAndMassVector(state); + + // mass flow rate and control acceleration + final T[] mainDerivativesIncrements = MathArrays.buildArray(mass.getField(), 7); + final FieldVector3D thrustAccelerationVector = getCost().getFieldThrustAccelerationVector(adjointVariables, mass); + mainDerivativesIncrements[3] = thrustAccelerationVector.getX(); + mainDerivativesIncrements[4] = thrustAccelerationVector.getY(); + mainDerivativesIncrements[5] = thrustAccelerationVector.getZ(); + final T thrustAccelerationNorm = thrustAccelerationVector.getNorm(); + if (thrustAccelerationVector.getNorm().getReal() != 0.) { + final T thrustForceMagnitude = thrustAccelerationNorm.multiply(mass); + mainDerivativesIncrements[6] = thrustForceMagnitude.multiply(-getCost().getMassFlowRateFactor()); + } + + // Cartesian position adjoint + additionalDerivatives[3] = adjointVariables[0].negate(); + additionalDerivatives[4] = adjointVariables[1].negate(); + additionalDerivatives[5] = adjointVariables[2].negate(); + + // Cartesian velocity adjoint + final FieldAbsoluteDate date = state.getDate(); + final Frame propagationFrame = state.getFrame(); + for (final CartesianAdjointEquationTerm equationTerm: adjointEquationTerms) { + final T[] contribution = equationTerm.getFieldRatesContribution(date, cartesianVariablesAndMass, adjointVariables, + propagationFrame); + for (int i = 0; i < FastMath.min(adjointDimension, contribution.length); i++) { + additionalDerivatives[i] = additionalDerivatives[i].add(contribution[i]); + } + } + + // other + getCost().updateFieldAdjointDerivatives(adjointVariables, mass, additionalDerivatives); + + return new FieldCombinedDerivatives<>(additionalDerivatives, mainDerivativesIncrements); + } + + /** + * Gather Cartesian variables and mass in same vector. + * @param state propagation state + * @return Cartesian variables and mass + */ + private T[] formCartesianAndMassVector(final FieldSpacecraftState state) { + final T mass = state.getMass(); + final T[] cartesianVariablesAndMass = MathArrays.buildArray(mass.getField(), 7); + final FieldPVCoordinates pvCoordinates = state.getPVCoordinates(); + System.arraycopy(pvCoordinates.getPosition().toArray(), 0, cartesianVariablesAndMass, 0, 3); + System.arraycopy(pvCoordinates.getVelocity().toArray(), 0, cartesianVariablesAndMass, 3, 3); + 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 T evaluateHamiltonian(final FieldSpacecraftState state) { + final T[] cartesianAndMassVector = formCartesianAndMassVector(state); + final T[] adjointVariables = state.getAdditionalState(getName()); + T hamiltonian = adjointVariables[0].multiply(cartesianAndMassVector[3]).add(adjointVariables[1].multiply(cartesianAndMassVector[4])) + .add(adjointVariables[2].multiply(cartesianAndMassVector[5])); + final FieldAbsoluteDate date = state.getDate(); + final Frame propagationFrame = state.getFrame(); + for (final CartesianAdjointEquationTerm adjointEquationTerm : adjointEquationTerms) { + final T contribution = adjointEquationTerm.getFieldHamiltonianContribution(date, cartesianAndMassVector, + adjointVariables, propagationFrame); + hamiltonian = hamiltonian.add(contribution); + } + if (adjointVariables.length != 6) { + final T mass = state.getMass(); + final T thrustAccelerationNorm = getCost().getFieldThrustAccelerationVector(adjointVariables, mass).getNorm(); + final T thrustForceNorm = thrustAccelerationNorm.multiply(mass); + hamiltonian = hamiltonian.subtract(adjointVariables[6].multiply(getCost().getMassFlowRateFactor()).multiply(thrustForceNorm)); + } + hamiltonian = hamiltonian.add(getCost().getFieldHamiltonianContribution(adjointVariables, state.getMass())); + return hamiltonian; + } +} diff --git a/src/main/java/org/orekit/control/indirect/adjoint/cost/AbstractCartesianEnergy.java b/src/main/java/org/orekit/control/indirect/adjoint/cost/AbstractCartesianEnergy.java new file mode 100644 index 0000000000..a6b0541c5a --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/adjoint/cost/AbstractCartesianEnergy.java @@ -0,0 +1,83 @@ +/* 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.cost; + + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.FastMath; + +/** + * Abstract class for energy cost with Cartesian coordinates. + * An energy cost is proportional to the integral over time of the Euclidean norm of the control vector, often scaled with 1/2. + * This type of cost is not optimal in terms of mass consumption, however its solutions showcase a smoother behavior favorable for convergence in shooting techniques. + * + * @author Romain Serra + * @see CartesianCost + * @since 12.2 + */ +public abstract class AbstractCartesianEnergy implements CartesianCost { + + /** Name of adjoint vector. */ + private final String name; + + /** Mass flow rate factor (always positive). */ + private final double massFlowRateFactor; + + /** + * Constructor. + * @param name name + * @param massFlowRateFactor mass flow rate factor + */ + protected AbstractCartesianEnergy(final String name, final double massFlowRateFactor) { + this.name = name; + this.massFlowRateFactor = FastMath.abs(massFlowRateFactor); + } + + /** + * Getter for adjoint vector name. + * @return name + */ + @Override + public String getAdjointName() { + return name; + } + + /** {@inheritDoc} */ + @Override + public double getMassFlowRateFactor() { + return massFlowRateFactor; + } + + /** + * Computes the Euclidean norm of the adjoint velocity vector. + * @param adjointVariables adjoint vector + * @return norm of adjoint velocity + */ + protected double getAdjointVelocityNorm(final double[] adjointVariables) { + return FastMath.sqrt(adjointVariables[3] * adjointVariables[3] + adjointVariables[4] * adjointVariables[4] + adjointVariables[5] * adjointVariables[5]); + } + + /** + * Computes the Euclidean norm of the adjoint velocity vector. + * @param adjointVariables adjoint vector + * @param field type + * @return norm of adjoint velocity + */ + protected > T getFieldAdjointVelocityNorm(final T[] adjointVariables) { + return FastMath.sqrt(adjointVariables[3].square().add(adjointVariables[4].square()).add(adjointVariables[5].square())); + } +} diff --git a/src/main/java/org/orekit/control/indirect/adjoint/cost/BoundedCartesianEnergy.java b/src/main/java/org/orekit/control/indirect/adjoint/cost/BoundedCartesianEnergy.java new file mode 100644 index 0000000000..2f6d513def --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/adjoint/cost/BoundedCartesianEnergy.java @@ -0,0 +1,116 @@ +/* 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.cost; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.util.FastMath; +import org.orekit.propagation.events.EventDetector; +import org.orekit.propagation.events.EventDetectionSettings; +import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.propagation.events.FieldEventDetectionSettings; +import org.orekit.propagation.events.handlers.FieldResetDerivativesOnEvent; +import org.orekit.propagation.events.handlers.ResetDerivativesOnEvent; + +import java.util.stream.Stream; + +/** + * Class for bounded energy cost with Cartesian coordinates. + * Here, the control vector is chosen as the thrust force divided by the maximum thrust magnitude and expressed in the propagation frame. + * It has a unit Euclidean norm. + * @author Romain Serra + * @see UnboundedCartesianEnergyNeglectingMass + * @since 12.2 + */ +public class BoundedCartesianEnergy extends CartesianEnergyConsideringMass { + + /** Maximum value of thrust force Euclidean norm. */ + private final double maximumThrustMagnitude; + + /** + * Constructor. + * @param name name + * @param massFlowRateFactor mass flow rate factor + * @param maximumThrustMagnitude maximum thrust magnitude + * @param eventDetectionSettings singularity event detection settings + */ + public BoundedCartesianEnergy(final String name, final double massFlowRateFactor, + final double maximumThrustMagnitude, + final EventDetectionSettings eventDetectionSettings) { + super(name, massFlowRateFactor, eventDetectionSettings); + this.maximumThrustMagnitude = FastMath.abs(maximumThrustMagnitude); + } + + /** + * Constructor. + * @param name name + * @param massFlowRateFactor mass flow rate factor + * @param maximumThrustMagnitude maximum thrust magnitude + */ + public BoundedCartesianEnergy(final String name, final double massFlowRateFactor, + final double maximumThrustMagnitude) { + this(name, massFlowRateFactor, maximumThrustMagnitude, EventDetectionSettings.getDefaultEventDetectionSettings()); + } + + /** {@inheritDoc} */ + @Override + protected double getThrustForceNorm(final double[] adjointVariables, final double mass) { + final double adjointVelocityNorm = getAdjointVelocityNorm(adjointVariables); + final double factor = adjointVelocityNorm / mass - getMassFlowRateFactor() * adjointVariables[6]; + if (factor > maximumThrustMagnitude) { + return maximumThrustMagnitude; + } else { + return FastMath.max(0., factor); + } + } + + /** {@inheritDoc} */ + @Override + protected > T getFieldThrustForceNorm(final T[] adjointVariables, + final T mass) { + final T adjointVelocityNorm = getFieldAdjointVelocityNorm(adjointVariables); + final T factor = adjointVelocityNorm.divide(mass).subtract(adjointVariables[6].multiply(getMassFlowRateFactor())); + final double factorReal = factor.getReal(); + final T zero = mass.getField().getZero(); + if (factorReal > maximumThrustMagnitude) { + return zero.newInstance(maximumThrustMagnitude); + } else if (factorReal < 0.) { + return zero; + } else { + return factor; + } + } + + /** {@inheritDoc} */ + @Override + public Stream getEventDetectors() { + final EventDetectionSettings detectionSettings = getEventDetectionSettings(); + return Stream.of(new SingularityDetector(detectionSettings, new ResetDerivativesOnEvent(), 0.), + new SingularityDetector(detectionSettings, new ResetDerivativesOnEvent(), maximumThrustMagnitude)); + } + + /** {@inheritDoc} */ + @Override + public > Stream> getFieldEventDetectors(final Field field) { + final FieldEventDetectionSettings detectionSettings = new FieldEventDetectionSettings<>(field, getEventDetectionSettings()); + final T zero = field.getZero(); + final T maximumThrustMagnitudeForEvent = zero.newInstance(maximumThrustMagnitude); + return Stream.of(new FieldSingularityDetector<>(detectionSettings, new FieldResetDerivativesOnEvent<>(), zero), + new FieldSingularityDetector<>(detectionSettings, new FieldResetDerivativesOnEvent<>(), maximumThrustMagnitudeForEvent)); + } + +} diff --git a/src/main/java/org/orekit/control/indirect/adjoint/cost/CartesianCost.java b/src/main/java/org/orekit/control/indirect/adjoint/cost/CartesianCost.java new file mode 100644 index 0000000000..80bf1ddf58 --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/adjoint/cost/CartesianCost.java @@ -0,0 +1,106 @@ +/* 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.cost; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.control.indirect.adjoint.CartesianAdjointDerivativesProvider; +import org.orekit.propagation.events.EventDetectorsProvider; + +/** + * Interface to definite cost function in the frame of Pontryagin's Maximum Principle using Cartesian coordinates. + * It provides the link between the optimal control and the adjoint variables. This relationship is obtained by maximizing the Hamiltonian. + * The choice of control vector impacts on it. + * Both standard (double type) and (Calculus)Field versions are to be implemented by inheritors. + * @author Romain Serra + * @see CartesianAdjointDerivativesProvider + * @since 12.2 + */ +public interface CartesianCost extends EventDetectorsProvider { + + /** Getter for adjoint vector name. + * @return adjoint vector name + */ + String getAdjointName(); + + /** Getter for adjoint vector dimension. Default is 7 (six for Cartesian coordinates and one for mass). + * @return adjoint dimension + */ + default int getAdjointDimension() { + return 7; + } + + /** Getter for mass flow rate factor. It is negated and multiplied by the thrust force magnitude to obtain the mass time derivative. + * The fact that it is a constant means that the exhaust speed is assumed to be independent of time. + * @return mass flow rate factor + */ + double getMassFlowRateFactor(); + + /** + * Computes the thrust acceleration vector in propagation frame from the adjoint variables and the mass. + * @param adjointVariables adjoint vector + * @param mass mass + * @return thrust vector + */ + Vector3D getThrustAccelerationVector(double[] adjointVariables, double mass); + + /** + * Computes the thrust acceleration vector in propagation frame from the adjoint variables and the mass. + * @param adjointVariables adjoint vector + * @param mass mass + * @param field type + * @return thrust vector + */ + > FieldVector3D getFieldThrustAccelerationVector(T[] adjointVariables, T mass); + + /** + * Update the adjoint derivatives if necessary. + * + * @param adjointVariables adjoint vector + * @param mass mass + * @param adjointDerivatives derivatives to update + */ + void updateAdjointDerivatives(double[] adjointVariables, double mass, double[] adjointDerivatives); + + /** + * Update the adjoint derivatives if necessary. + * + * @param field type + * @param adjointVariables adjoint vector + * @param mass mass + * @param adjointDerivatives derivatives to update + */ + > void updateFieldAdjointDerivatives(T[] adjointVariables, T mass, T[] adjointDerivatives); + + /** + * Computes the Hamiltonian contribution of the cost function. + * @param adjointVariables adjoint vector + * @param mass mass + * @return contribution to Hamiltonian + */ + double getHamiltonianContribution(double[] adjointVariables, double mass); + + /** + * Computes the Hamiltonian contribution of the cost function. + * @param adjointVariables adjoint vector + * @param mass mass + * @param field type + * @return contribution to Hamiltonian + */ + > T getFieldHamiltonianContribution(T[] adjointVariables, T mass); +} diff --git a/src/main/java/org/orekit/control/indirect/adjoint/cost/CartesianEnergyConsideringMass.java b/src/main/java/org/orekit/control/indirect/adjoint/cost/CartesianEnergyConsideringMass.java new file mode 100644 index 0000000000..64eca57b05 --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/adjoint/cost/CartesianEnergyConsideringMass.java @@ -0,0 +1,235 @@ +/* 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.cost; + + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.AbstractDetector; +import org.orekit.propagation.events.AdaptableInterval; +import org.orekit.propagation.events.EventDetectionSettings; +import org.orekit.propagation.events.FieldAbstractDetector; +import org.orekit.propagation.events.FieldAdaptableInterval; +import org.orekit.propagation.events.FieldEventDetectionSettings; +import org.orekit.propagation.events.handlers.EventHandler; +import org.orekit.propagation.events.handlers.FieldEventHandler; + +/** + * Abstract class for energy cost with Cartesian coordinates and non-zero mass flow rate. + * @author Romain Serra + * @see AbstractCartesianEnergy + * @since 12.2 + */ +abstract class CartesianEnergyConsideringMass extends AbstractCartesianEnergy { + + /** Detection settings for singularity detection. */ + private final EventDetectionSettings eventDetectionSettings; + + /** + * Constructor. + * @param name name + * @param massFlowRateFactor mass flow rate factor + * @param eventDetectionSettings settings for singularity detections + */ + protected CartesianEnergyConsideringMass(final String name, final double massFlowRateFactor, + final EventDetectionSettings eventDetectionSettings) { + super(name, massFlowRateFactor); + this.eventDetectionSettings = eventDetectionSettings; + } + + /** + * Getter for event detection settings. + * @return detection settings. + */ + public EventDetectionSettings getEventDetectionSettings() { + return eventDetectionSettings; + } + + /** {@inheritDoc} */ + @Override + public Vector3D getThrustAccelerationVector(final double[] adjointVariables, final double mass) { + return getThrustDirection(adjointVariables).scalarMultiply(getThrustForceNorm(adjointVariables, mass) / mass); + } + + /** {@inheritDoc} */ + @Override + public > FieldVector3D getFieldThrustAccelerationVector(final T[] adjointVariables, + final T mass) { + return getFieldThrustDirection(adjointVariables).scalarMultiply(getFieldThrustForceNorm(adjointVariables, mass).divide(mass)); + } + + /** + * Computes the direction of thrust. + * @param adjointVariables adjoint vector + * @return thrust direction + */ + protected Vector3D getThrustDirection(final double[] adjointVariables) { + return new Vector3D(adjointVariables[3], adjointVariables[4], adjointVariables[5]).normalize(); + } + + /** + * Computes the direction of thrust. + * @param adjointVariables adjoint vector + * @param field type + * @return thrust direction + */ + protected > FieldVector3D getFieldThrustDirection(final T[] adjointVariables) { + return new FieldVector3D<>(adjointVariables[3], adjointVariables[4], adjointVariables[5]).normalize(); + } + + /** + * Computes the Euclidean norm of the thrust force. + * @param adjointVariables adjoint vector + * @param mass mass + * @return norm of thrust + */ + protected abstract double getThrustForceNorm(double[] adjointVariables, double mass); + + /** + * Computes the Euclidean norm of the thrust force. + * @param adjointVariables adjoint vector + * @param mass mass + * @param field type + * @return norm of thrust + */ + protected abstract > T getFieldThrustForceNorm(T[] adjointVariables, T mass); + + /** {@inheritDoc} */ + @Override + public void updateAdjointDerivatives(final double[] adjointVariables, final double mass, + final double[] adjointDerivatives) { + adjointDerivatives[6] += getThrustForceNorm(adjointVariables, mass) * getAdjointVelocityNorm(adjointVariables) / (mass * mass); + } + + /** {@inheritDoc} */ + @Override + public > void updateFieldAdjointDerivatives(final T[] adjointVariables, final T mass, + final T[] adjointDerivatives) { + adjointDerivatives[6] = adjointDerivatives[6].add(getFieldThrustForceNorm(adjointVariables, mass) + .multiply(getFieldAdjointVelocityNorm(adjointVariables)).divide(mass.square())); + } + + /** {@inheritDoc} */ + @Override + public double getHamiltonianContribution(final double[] adjointVariables, final double mass) { + final Vector3D thrustForce = getThrustAccelerationVector(adjointVariables, mass).scalarMultiply(mass); + return -thrustForce.getNormSq() / 2.; + } + + /** {@inheritDoc} */ + @Override + public > T getFieldHamiltonianContribution(final T[] adjointVariables, final T mass) { + final FieldVector3D thrustForce = getFieldThrustAccelerationVector(adjointVariables, mass).scalarMultiply(mass); + return thrustForce.getNormSq().multiply(-1. / 2.); + } + + /** + * Event detector for singularities in adjoint dynamics. + */ + class SingularityDetector extends AbstractDetector { + + /** Value to detect. */ + private final double detectionValue; + + /** + * Constructor. + * @param eventDetectionSettings detection settings + * @param handler event handler + * @param detectionValue value to detect + */ + SingularityDetector(final EventDetectionSettings eventDetectionSettings, final EventHandler handler, + final double detectionValue) { + super(eventDetectionSettings, handler); + this.detectionValue = detectionValue; + } + + /** {@inheritDoc} */ + @Override + public double g(final SpacecraftState state) { + final double[] adjoint = state.getAdditionalState(getAdjointName()); + return evaluateVariablePart(adjoint, state.getMass()) - detectionValue; + } + + /** + * Evaluate variable part of singularity function. + * @param adjointVariables adjoint vector + * @param mass mass + * @return singularity function without the constant part + */ + private double evaluateVariablePart(final double[] adjointVariables, final double mass) { + final double adjointVelocityNorm = getAdjointVelocityNorm(adjointVariables); + return adjointVelocityNorm / mass - getMassFlowRateFactor() * adjointVariables[6]; + } + + @Override + protected SingularityDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, final EventHandler newHandler) { + return new SingularityDetector(new EventDetectionSettings(newMaxCheck, newThreshold, newMaxIter), newHandler, + detectionValue); + } + } + + /** + * Field event detector for singularities in adjoint dynamics. + */ + class FieldSingularityDetector> + extends FieldAbstractDetector, T> { + + /** Value to detect. */ + private final T detectionValue; + + /** + * Constructor. + * @param eventDetectionSettings detection settings + * @param handler event handler + * @param detectionValue value to detect + */ + protected FieldSingularityDetector(final FieldEventDetectionSettings eventDetectionSettings, + final FieldEventHandler handler, final T detectionValue) { + super(eventDetectionSettings, handler); + this.detectionValue = detectionValue; + } + + /** {@inheritDoc} */ + @Override + public T g(final FieldSpacecraftState state) { + final T[] adjoint = state.getAdditionalState(getAdjointName()); + return evaluateVariablePart(adjoint, state.getMass()).subtract(detectionValue); + } + + /** + * Evaluate variable part of singularity function. + * @param adjointVariables adjoint vector + * @param mass mass + * @return singularity function without the constant part + */ + private T evaluateVariablePart(final T[] adjointVariables, final T mass) { + final T adjointVelocityNorm = getFieldAdjointVelocityNorm(adjointVariables); + return adjointVelocityNorm.divide(mass).subtract(adjointVariables[6].multiply(getMassFlowRateFactor())); + } + + @Override + protected FieldSingularityDetector create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, + final int newMaxIter, final FieldEventHandler newHandler) { + return new FieldSingularityDetector<>(new FieldEventDetectionSettings<>(newMaxCheck, newThreshold, newMaxIter), + newHandler, detectionValue); + } + } +} diff --git a/src/main/java/org/orekit/control/indirect/adjoint/cost/UnboundedCartesianEnergy.java b/src/main/java/org/orekit/control/indirect/adjoint/cost/UnboundedCartesianEnergy.java new file mode 100644 index 0000000000..c30e957cc0 --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/adjoint/cost/UnboundedCartesianEnergy.java @@ -0,0 +1,94 @@ +/* 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.cost; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.util.FastMath; +import org.orekit.propagation.events.EventDetectionSettings; +import org.orekit.propagation.events.EventDetector; +import org.orekit.propagation.events.FieldEventDetectionSettings; +import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.propagation.events.handlers.FieldResetDerivativesOnEvent; +import org.orekit.propagation.events.handlers.ResetDerivativesOnEvent; + +import java.util.stream.Stream; + +/** + * Class for unbounded energy cost with Cartesian coordinates. + * Here, the control vector is chosen as the thrust force, expressed in the propagation frame. + * This leads to the optimal thrust being in the same direction as the adjoint velocity. + * @author Romain Serra + * @see UnboundedCartesianEnergyNeglectingMass + * @since 12.2 + */ +public class UnboundedCartesianEnergy extends CartesianEnergyConsideringMass { + + /** + * Constructor. + * @param name name + * @param massFlowRateFactor mass flow rate factor + * @param eventDetectionSettings detection settings for singularity detections + */ + public UnboundedCartesianEnergy(final String name, final double massFlowRateFactor, + final EventDetectionSettings eventDetectionSettings) { + super(name, massFlowRateFactor, eventDetectionSettings); + } + + /** + * Constructor. + * @param name name + * @param massFlowRateFactor mass flow rate factor + */ + public UnboundedCartesianEnergy(final String name, final double massFlowRateFactor) { + this(name, massFlowRateFactor, EventDetectionSettings.getDefaultEventDetectionSettings()); + } + + /** {@inheritDoc} */ + @Override + protected double getThrustForceNorm(final double[] adjointVariables, final double mass) { + final double adjointVelocityNorm = getAdjointVelocityNorm(adjointVariables); + final double factor = adjointVelocityNorm / mass - getMassFlowRateFactor() * adjointVariables[6]; + return FastMath.max(0., factor); + } + + /** {@inheritDoc} */ + @Override + protected > T getFieldThrustForceNorm(final T[] adjointVariables, final T mass) { + final T adjointVelocityNorm = getFieldAdjointVelocityNorm(adjointVariables); + final T factor = adjointVelocityNorm.divide(mass).subtract(adjointVariables[6].multiply(getMassFlowRateFactor())); + if (factor.getReal() < 0.) { + return adjointVelocityNorm.getField().getZero(); + } else { + return factor; + } + } + + /** {@inheritDoc} */ + @Override + public Stream getEventDetectors() { + return Stream.of(new SingularityDetector(getEventDetectionSettings(), new ResetDerivativesOnEvent(), + 0.)); + } + + /** {@inheritDoc} */ + @Override + public > Stream> getFieldEventDetectors(final Field field) { + return Stream.of(new FieldSingularityDetector<>(new FieldEventDetectionSettings<>(field, getEventDetectionSettings()), + new FieldResetDerivativesOnEvent<>(), field.getZero())); + } +} diff --git a/src/main/java/org/orekit/control/indirect/adjoint/cost/UnboundedCartesianEnergyNeglectingMass.java b/src/main/java/org/orekit/control/indirect/adjoint/cost/UnboundedCartesianEnergyNeglectingMass.java new file mode 100644 index 0000000000..df669357ee --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/adjoint/cost/UnboundedCartesianEnergyNeglectingMass.java @@ -0,0 +1,108 @@ +/* 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.cost; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.propagation.events.EventDetector; +import org.orekit.propagation.events.FieldEventDetector; + +import java.util.stream.Stream; + +/** + * Class for unbounded energy cost with Cartesian coordinates neglecting the mass consumption. + * Under this assumption, the mass is constant and there is no need to consider the corresponding adjoint variable. + * Here, the control vector is chosen as the acceleration given by thrusting, expressed in the propagation frame. + * This leads to the optimal thrust force being equal to the adjoint velocity vector times the mass. + * @author Romain Serra + * @since 12.2 + */ +public class UnboundedCartesianEnergyNeglectingMass extends AbstractCartesianEnergy { + + /** + * Constructor. + * @param name name + */ + public UnboundedCartesianEnergyNeglectingMass(final String name) { + super(name, 0.); + } + + /** {@inheritDoc} */ + @Override + public int getAdjointDimension() { + return 6; + } + + /** {@inheritDoc} */ + @Override + public double getMassFlowRateFactor() { + return 0; + } + + /** {@inheritDoc} */ + @Override + public Vector3D getThrustAccelerationVector(final double[] adjointVariables, final double mass) { + return new Vector3D(adjointVariables[3], adjointVariables[4], adjointVariables[5]); + } + + /** {@inheritDoc} */ + @Override + public > FieldVector3D getFieldThrustAccelerationVector(final T[] adjointVariables, + final T mass) { + return new FieldVector3D<>(adjointVariables[3], adjointVariables[4], adjointVariables[5]); + } + + @Override + public void updateAdjointDerivatives(final double[] adjointVariables, final double mass, + final double[] adjointDerivatives) { + // nothing to do + } + + @Override + public > void updateFieldAdjointDerivatives(final T[] adjointVariables, final T mass, + final T[] adjointDerivatives) { + // nothing to do + } + + /** {@inheritDoc} */ + @Override + public double getHamiltonianContribution(final double[] adjointVariables, final double mass) { + final Vector3D thrustAcceleration = getThrustAccelerationVector(adjointVariables, mass); + return -thrustAcceleration.getNormSq() / 2.; + } + + /** {@inheritDoc} */ + @Override + public > T getFieldHamiltonianContribution(final T[] adjointVariables, final T mass) { + final FieldVector3D thrustAcceleration = getFieldThrustAccelerationVector(adjointVariables, mass); + return thrustAcceleration.getNormSq().multiply(-1. / 2.); + } + + /** {@inheritDoc} */ + @Override + public Stream getEventDetectors() { + return Stream.empty(); + } + + /** {@inheritDoc} */ + @Override + public > Stream> getFieldEventDetectors(final Field field) { + return Stream.empty(); + } +} diff --git a/src/main/java/org/orekit/control/indirect/adjoint/cost/package-info.java b/src/main/java/org/orekit/control/indirect/adjoint/cost/package-info.java new file mode 100644 index 0000000000..977852b937 --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/adjoint/cost/package-info.java @@ -0,0 +1,26 @@ +/* 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. + */ +/** + * + * This package provides routines to model the control cost in adjoint equations as in the Pontryagin Maximum Principle. + * The cost function (to be minimized) appears in the Hamiltonian and the maximization of the latter provides a relationship between adjoint variables and the optimal control law. + * + * @author Romain Serra + * @since 12.2 + * + */ +package org.orekit.control.indirect.adjoint.cost; diff --git a/src/main/java/org/orekit/control/indirect/adjoint/package-info.java b/src/main/java/org/orekit/control/indirect/adjoint/package-info.java new file mode 100644 index 0000000000..a74f99ec96 --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/adjoint/package-info.java @@ -0,0 +1,28 @@ +/* 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. + */ +/** + * + * This package provides routines to model the adjoint dynamics as in the Pontryagin Maximum Principle, as used + * in indirect control. There is one adjoint variable for each dependent variable in the equations of motion (a.k.a. the state variables), + * so in orbital mechanics that is typically the position-velocity vector (or its equivalent e.g. orbital elements) and mass. + * The adjoint vector is the solution to a differential system coupled with the state vector. + * + * @author Romain Serra + * @since 12.2 + * + */ +package org.orekit.control.indirect.adjoint; diff --git a/src/main/java/org/orekit/control/indirect/package-info.java b/src/main/java/org/orekit/control/indirect/package-info.java new file mode 100644 index 0000000000..78e2995535 --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/package-info.java @@ -0,0 +1,38 @@ +/* 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. + */ +/** + * + * This package provides routines to perform so-called indirect optimal control within the frame of orbital mechanics. + *
+ * Indirect means that optimality conditions are obtained first, before attempting to solve them, usually numerically by way of some discretization. + * A common theorem to derive such conditions for optimality is the Pontryagin's Maximum Principle and its variants. It introduces so-called adjoint variables which are closely linked to the optimal solution. + * This is in contrast with direct methods, which consist in performing discretization first, before resorting to finite-dimension, local optimization techniques. + *
+ *
+ * Some references: + *
    + *
  • CERF, Max. Optimization Techniques II: Discrete and Functional Optimization. In : Optimization Techniques II. EDP Sciences, 2023. + *
  • TRÉLAT, Emmanuel. Optimal control and applications to aerospace: some results and challenges. Journal of Optimization Theory and Applications, 2012, vol. 154, p. 713-758. + *
  • COLASURDO, Guido and CASALINO, Lorenzo. Indirect methods for the optimization of spacecraft trajectories. Modeling and Optimization in Space Engineering, 2013, p. 141-158. + *
  • MAREC, Jean-Pierre. Optimal space trajectories. Elsevier, 2012. + *
+ * + * @author Romain Serra + * @since 12.2 + * + */ +package org.orekit.control.indirect; diff --git a/src/main/java/org/orekit/control/indirect/shooting/AbstractFixedBoundaryCartesianSingleShooting.java b/src/main/java/org/orekit/control/indirect/shooting/AbstractFixedBoundaryCartesianSingleShooting.java new file mode 100644 index 0000000000..b8d535c653 --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/shooting/AbstractFixedBoundaryCartesianSingleShooting.java @@ -0,0 +1,352 @@ +/* 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.shooting; + +import org.hipparchus.Field; +import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.analysis.differentiation.GradientField; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathArrays; +import org.orekit.attitudes.Attitude; +import org.orekit.control.indirect.adjoint.CartesianAdjointDerivativesProvider; +import org.orekit.control.indirect.adjoint.FieldCartesianAdjointDerivativesProvider; +import org.orekit.control.indirect.shooting.boundary.CartesianBoundaryConditionChecker; +import org.orekit.control.indirect.shooting.boundary.FixedTimeBoundaryOrbits; +import org.orekit.control.indirect.shooting.boundary.FixedTimeCartesianBoundaryStates; +import org.orekit.control.indirect.shooting.propagation.ShootingPropagationSettings; +import org.orekit.frames.Frame; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.numerical.FieldNumericalPropagator; +import org.orekit.propagation.numerical.NumericalPropagator; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.AbsolutePVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** + * Abstract class for indirect single shooting methods with Cartesian coordinates for fixed time fixed boundary. + * Inheritors must implement the iteration update, assuming derivatives are needed. + * Terminal mass is assumed to be free, thus corresponding adjoint must vanish at terminal time. + * On the other hand, other terminal adjoint variables are free because the Cartesian state is fixed. + * + * @author Romain Serra + * @since 12.2 + * @see org.orekit.control.indirect.adjoint.CartesianAdjointDerivativesProvider + * @see org.orekit.control.indirect.adjoint.FieldCartesianAdjointDerivativesProvider + */ +public abstract class AbstractFixedBoundaryCartesianSingleShooting extends AbstractIndirectShooting { + + /** Default value for defects scaling. */ + private static final double DEFAULT_SCALE = 1.; + + /** Template for initial state. Contains the initial Cartesian coordinates. */ + private final SpacecraftState initialSpacecraftStateTemplate; + + /** Terminal Cartesian coordinates. */ + private final TimeStampedPVCoordinates terminalCartesianState; + + /** Condition checker. */ + private final CartesianBoundaryConditionChecker conditionChecker; + + /** Scale for velocity defects (m). */ + private double scaleVelocityDefects; + + /** Scale for position defects (m/s). */ + private double scalePositionDefects; + + /** Tolerance for convergence on terminal mass adjoint, if applicable to dynamics. */ + private double toleranceMassAdjoint = DEFAULT_TOLERANCE_MASS_ADJOINT; + + /** + * Constructor with boundary conditions as orbits. + * @param propagationSettings propagation settings + * @param boundaryConditions boundary conditions as {@link FixedTimeCartesianBoundaryStates} + * @param conditionChecker boundary condition checker + */ + protected AbstractFixedBoundaryCartesianSingleShooting(final ShootingPropagationSettings propagationSettings, + final FixedTimeCartesianBoundaryStates boundaryConditions, + final CartesianBoundaryConditionChecker conditionChecker) { + super(propagationSettings); + this.conditionChecker = conditionChecker; + this.initialSpacecraftStateTemplate = buildInitialStateTemplate(boundaryConditions.getInitialCartesianState()); + this.terminalCartesianState = boundaryConditions.getTerminalCartesianState().getPVCoordinates(propagationSettings.getPropagationFrame()); + this.scalePositionDefects = DEFAULT_SCALE; + this.scaleVelocityDefects = DEFAULT_SCALE; + } + + /** + * Constructor with boundary conditions as orbits. + * @param propagationSettings propagation settings + * @param boundaryConditions boundary conditions as {@link FixedTimeBoundaryOrbits} + * @param conditionChecker boundary condition checker + */ + protected AbstractFixedBoundaryCartesianSingleShooting(final ShootingPropagationSettings propagationSettings, + final FixedTimeBoundaryOrbits boundaryConditions, + final CartesianBoundaryConditionChecker conditionChecker) { + super(propagationSettings); + this.conditionChecker = conditionChecker; + this.initialSpacecraftStateTemplate = buildInitialStateTemplate(boundaryConditions.getInitialOrbit()); + this.terminalCartesianState = boundaryConditions.getTerminalOrbit().getPVCoordinates(propagationSettings.getPropagationFrame()); + this.scalePositionDefects = DEFAULT_SCALE; + this.scaleVelocityDefects = DEFAULT_SCALE; + } + + /** + * Setter for scale of position defects. + * @param scalePositionDefects new scale + */ + public void setScalePositionDefects(final double scalePositionDefects) { + this.scalePositionDefects = scalePositionDefects; + } + + /** + * Getter for scale of position defects. + * @return scale + */ + public double getScalePositionDefects() { + return scalePositionDefects; + } + + /** + * Setter for scale of velocity defects. + * @param scaleVelocityDefects new scale + */ + public void setScaleVelocityDefects(final double scaleVelocityDefects) { + this.scaleVelocityDefects = scaleVelocityDefects; + } + + /** + * Getter for scale of velocity defects. + * @return scale + */ + public double getScaleVelocityDefects() { + return scaleVelocityDefects; + } + + /** + * Getter for the boundary condition checker. + * @return checker + */ + protected CartesianBoundaryConditionChecker getConditionChecker() { + return conditionChecker; + } + + /** + * Getter for the target terminal Cartesian state vector. + * @return expected terminal state + */ + protected TimeStampedPVCoordinates getTerminalCartesianState() { + return terminalCartesianState; + } + + /** + * Setter for mass adjoint tolerance. + * @param toleranceMassAdjoint new tolerance value + */ + public void setToleranceMassAdjoint(final double toleranceMassAdjoint) { + this.toleranceMassAdjoint = FastMath.abs(toleranceMassAdjoint); + } + + /** + * Create template initial state (without adjoint varialbles) for propagation from orbits. + * @param initialOrbit initial orbit + * @return template propagation state + */ + private SpacecraftState buildInitialStateTemplate(final Orbit initialOrbit) { + final Frame frame = getPropagationSettings().getPropagationFrame(); + final CartesianOrbit cartesianOrbit = new CartesianOrbit(initialOrbit.getPVCoordinates(frame), frame, + initialOrbit.getDate(), initialOrbit.getMu()); + final Attitude attitude = getPropagationSettings().getAttitudeProvider() + .getAttitude(cartesianOrbit, cartesianOrbit.getDate(), cartesianOrbit.getFrame()); + return new SpacecraftState(cartesianOrbit, attitude); + } + + /** + * Create template initial state (without adjoint varialbles) for propagation. + * @param initialCartesianState initial Cartesian state + * @return template propagation state + */ + private SpacecraftState buildInitialStateTemplate(final AbsolutePVCoordinates initialCartesianState) { + final Frame frame = getPropagationSettings().getPropagationFrame(); + final AbsolutePVCoordinates absolutePVCoordinates = new AbsolutePVCoordinates(frame, + initialCartesianState.getPVCoordinates(frame)); + final Attitude attitude = getPropagationSettings().getAttitudeProvider() + .getAttitude(absolutePVCoordinates, absolutePVCoordinates.getDate(), absolutePVCoordinates.getFrame()); + return new SpacecraftState(absolutePVCoordinates, attitude); + } + + /** {@inheritDoc} */ + @Override + public ShootingBoundaryOutput solve(final double initialMass, final double[] initialGuess) { + // check initial guess + final SpacecraftState initialState = createStateWithMassAndAdjoint(initialMass, initialGuess); + final ShootingBoundaryOutput initialGuessSolution = computeCandidateSolution(initialState, 0); + if (initialGuessSolution.isConverged()) { + return initialGuessSolution; + } else { + return iterate(initialMass, initialGuess); + } + } + + /** {@inheritDoc} */ + @Override + protected NumericalPropagator buildPropagator(final SpacecraftState initialState) { + final NumericalPropagator propagator = super.buildPropagator(initialState); + final CartesianAdjointDerivativesProvider derivativesProvider = (CartesianAdjointDerivativesProvider) + getPropagationSettings().getAdjointDynamicsProvider().buildAdditionalDerivativesProvider(); + derivativesProvider.getCost().getEventDetectors().forEach(propagator::addEventDetector); + return propagator; + } + + /** {@inheritDoc} */ + @Override + protected FieldNumericalPropagator buildFieldPropagator(final FieldSpacecraftState initialState) { + final FieldNumericalPropagator fieldPropagator = super.buildFieldPropagator(initialState); + final Field field = fieldPropagator.getField(); + final FieldCartesianAdjointDerivativesProvider derivativesProvider = (FieldCartesianAdjointDerivativesProvider) + getPropagationSettings().getAdjointDynamicsProvider().buildFieldAdditionalDerivativesProvider(field); + derivativesProvider.getCost().getFieldEventDetectors(field).forEach(fieldPropagator::addEventDetector); + return fieldPropagator; + } + + /** + * Form solution container with input initial state. + * @param initialState initial state + * @param iterationCount iteration count + * @return candidate solution + */ + private ShootingBoundaryOutput computeCandidateSolution(final SpacecraftState initialState, + final int iterationCount) { + final NumericalPropagator propagator = buildPropagator(initialState); + final SpacecraftState actualTerminalState = propagator.propagate(getTerminalCartesianState().getDate()); + final boolean converged = checkConvergence(actualTerminalState); + return new ShootingBoundaryOutput(converged, iterationCount, initialState, getPropagationSettings(), actualTerminalState); + } + + /** + * Iterate on initial guess to solve boundary problem. + * @param initialMass initial mass + * @param initialGuess initial guess for initial adjoint variables + * @return solution (or null pointer if not converged) + */ + private ShootingBoundaryOutput iterate(final double initialMass, final double[] initialGuess) { + double[] initialAdjoint = initialGuess.clone(); + int iterationCount = 0; + int maximumIterationCount = getConditionChecker().getMaximumIterationCount(); + SpacecraftState initialState = createStateWithMassAndAdjoint(initialMass, initialGuess); + while (iterationCount < maximumIterationCount) { + final FieldSpacecraftState fieldInitialState = createFieldStateWithMassAndAdjoint(initialMass, + initialAdjoint); + final Field field = fieldInitialState.getDate().getField(); + final FieldAbsoluteDate fieldTerminalDate = new FieldAbsoluteDate<>(field, getTerminalCartesianState().getDate()); + final FieldNumericalPropagator fieldPropagator = buildFieldPropagator(fieldInitialState); + final FieldSpacecraftState fieldTerminalState = fieldPropagator.propagate(fieldTerminalDate); + initialState = fieldInitialState.toSpacecraftState(); + if (checkConvergence(fieldTerminalState.toSpacecraftState())) { + // make sure non-Field version also satisfies convergence criterion + final ShootingBoundaryOutput solution = computeCandidateSolution(initialState, iterationCount); + if (solution.isConverged()) { + return solution; + } + } + initialAdjoint = updateAdjoint(initialAdjoint, fieldTerminalState); + if (Double.isNaN(initialAdjoint[0])) { + return computeCandidateSolution(initialState, iterationCount); + } + iterationCount++; + maximumIterationCount = getConditionChecker().getMaximumIterationCount(); + } + return computeCandidateSolution(initialState, maximumIterationCount); + } + + /** + * Checks convergence. + * @param actualTerminalState achieved terminal state + * @return convergence flag + */ + private boolean checkConvergence(final SpacecraftState actualTerminalState) { + final boolean isCartesianConverged = getConditionChecker().isConverged(getTerminalCartesianState(), + actualTerminalState.getPVCoordinates()); + if (isCartesianConverged) { + final String adjointName = getPropagationSettings().getAdjointDynamicsProvider().getAdjointName(); + final double[] terminalAdjoint = actualTerminalState.getAdditionalState(adjointName); + if (terminalAdjoint.length == 7) { + return FastMath.abs(terminalAdjoint[6]) < toleranceMassAdjoint; + } else { + return true; + } + } else { + return false; + } + } + + /** + * Create initial state with input mass and adjoint vector. + * @param initialMass initial mass + * @param initialAdjoint initial adjoint variables + * @return state + */ + protected SpacecraftState createStateWithMassAndAdjoint(final double initialMass, final double[] initialAdjoint) { + final String adjointName = getPropagationSettings().getAdjointDynamicsProvider().getAdjointName(); + return createStateWithMass(initialMass).addAdditionalState(adjointName, initialAdjoint); + } + + /** + * Create initial state with input mass. + * @param initialMass initial mass + * @return state + */ + private SpacecraftState createStateWithMass(final double initialMass) { + if (initialSpacecraftStateTemplate.isOrbitDefined()) { + return new SpacecraftState(initialSpacecraftStateTemplate.getOrbit(), + initialSpacecraftStateTemplate.getAttitude(), initialMass); + } else { + return new SpacecraftState(initialSpacecraftStateTemplate.getAbsPVA(), + initialSpacecraftStateTemplate.getAttitude(), initialMass); + } + } + + /** + * Create initial state with input mass and adjoint vector. + * @param initialMass initial mass + * @param initialAdjoint initial adjoint variables + * @return state + */ + protected FieldSpacecraftState createFieldStateWithMassAndAdjoint(final double initialMass, + final double[] initialAdjoint) { + final int parametersNumber = initialAdjoint.length; + final GradientField field = GradientField.getField(parametersNumber); + final FieldSpacecraftState stateWithoutAdjoint = new FieldSpacecraftState<>(field, createStateWithMass(initialMass)); + final Gradient[] fieldInitialAdjoint = MathArrays.buildArray(field, initialAdjoint.length); + for (int i = 0; i < parametersNumber; i++) { + fieldInitialAdjoint[i] = Gradient.variable(parametersNumber, i, initialAdjoint[i]); + } + final String adjointName = getPropagationSettings().getAdjointDynamicsProvider().getAdjointName(); + return stateWithoutAdjoint.addAdditionalState(adjointName, fieldInitialAdjoint); + } + + /** + * Update initial adjoint vector. + * @param originalInitialAdjoint original initial adjoint (before update) + * @param fieldTerminalState final state of gradient propagation + * @return updated initial adjoint vector + */ + protected abstract double[] updateAdjoint(double[] originalInitialAdjoint, + FieldSpacecraftState fieldTerminalState); +} diff --git a/src/main/java/org/orekit/control/indirect/shooting/AbstractIndirectShooting.java b/src/main/java/org/orekit/control/indirect/shooting/AbstractIndirectShooting.java new file mode 100644 index 0000000000..c8ce735192 --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/shooting/AbstractIndirectShooting.java @@ -0,0 +1,168 @@ +/* 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.shooting; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.ode.FieldODEIntegrator; +import org.hipparchus.ode.ODEIntegrator; +import org.orekit.control.indirect.shooting.propagation.ShootingPropagationSettings; +import org.orekit.forces.ForceModel; +import org.orekit.forces.gravity.NewtonianAttraction; +import org.orekit.orbits.FieldOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.conversion.FieldODEIntegratorBuilder; +import org.orekit.propagation.conversion.ODEIntegratorBuilder; +import org.orekit.propagation.integration.AdditionalDerivativesProvider; +import org.orekit.propagation.integration.FieldAdditionalDerivativesProvider; +import org.orekit.propagation.numerical.FieldNumericalPropagator; +import org.orekit.propagation.numerical.NumericalPropagator; + +/** + * Abstract class for indirect shooting methods with numerical propagation. + * + * @author Romain Serra + * @since 12.2 + */ +public abstract class AbstractIndirectShooting { + + /** Default value for convergence tolerance on mass adjoint variable. */ + public static final double DEFAULT_TOLERANCE_MASS_ADJOINT = 1e-10; + + /** Propagation settings. */ + private final ShootingPropagationSettings propagationSettings; + + /** + * Constructor. + * @param propagationSettings propagation settings + */ + protected AbstractIndirectShooting(final ShootingPropagationSettings propagationSettings) { + this.propagationSettings = propagationSettings; + } + + /** + * Getter for the propagation settings. + * @return propagation settings + */ + public ShootingPropagationSettings getPropagationSettings() { + return propagationSettings; + } + + /** + * Solve for the boundary conditions, given an initial mass and an initial guess for the adjoint variables. + * @param initialMass initial mass + * @param initialGuess initial guess + * @return boundary problem solution + */ + public abstract ShootingBoundaryOutput solve(double initialMass, double[] initialGuess); + + /** + * Create numerical propagator. + * @param initialState initial state + * @return numerical propagator + */ + protected NumericalPropagator buildPropagator(final SpacecraftState initialState) { + final ODEIntegrator integrator = buildIntegrator(initialState); + final NumericalPropagator propagator = + new NumericalPropagator(integrator, propagationSettings.getAttitudeProvider()); + propagator.setIgnoreCentralAttraction(true); + propagator.setInitialState(initialState); + propagator.setIgnoreCentralAttraction(false); + propagator.removeForceModels(); + if (initialState.isOrbitDefined()) { + propagator.setOrbitType(initialState.getOrbit().getType()); + } else { + if (propagationSettings.getForceModels().stream().noneMatch(NewtonianAttraction.class::isInstance)) { + propagator.setIgnoreCentralAttraction(true); + } + propagator.setOrbitType(null); + } + for (final ForceModel forceModel: propagationSettings.getForceModels()) { + propagator.addForceModel(forceModel); + } + final AdditionalDerivativesProvider derivativesProvider = propagationSettings.getAdjointDynamicsProvider() + .buildAdditionalDerivativesProvider(); + propagator.addAdditionalDerivativesProvider(derivativesProvider); + return propagator; + } + + /** + * Create integrator. + * @param initialState initial state + * @return integrator + */ + private ODEIntegrator buildIntegrator(final SpacecraftState initialState) { + final ODEIntegratorBuilder integratorBuilder = propagationSettings.getIntegrationSettings().getIntegratorBuilder(); + if (initialState.isOrbitDefined()) { + final Orbit orbit = initialState.getOrbit(); + return integratorBuilder.buildIntegrator(orbit, orbit.getType()); + } else { + return integratorBuilder.buildIntegrator(initialState.getAbsPVA()); + } + } + + /** + * Create Gradient numerical propagator. + * @param initialState initial state + * @return numerical propagator. + */ + protected FieldNumericalPropagator buildFieldPropagator(final FieldSpacecraftState initialState) { + final Field field = initialState.getDate().getField(); + final FieldODEIntegrator integrator = buildFieldIntegrator(initialState); + final FieldNumericalPropagator propagator = + new FieldNumericalPropagator<>(field, integrator, propagationSettings.getAttitudeProvider()); + propagator.setIgnoreCentralAttraction(true); + propagator.removeForceModels(); + propagator.setInitialState(initialState); + propagator.setIgnoreCentralAttraction(false); + if (initialState.isOrbitDefined()) { + propagator.setOrbitType(initialState.getOrbit().getType()); + } else { + propagator.setOrbitType(null); + if (propagationSettings.getForceModels().stream().noneMatch(NewtonianAttraction.class::isInstance)) { + propagator.setIgnoreCentralAttraction(true); + } + } + for (final ForceModel forceModel: propagationSettings.getForceModels()) { + propagator.addForceModel(forceModel); + } + final FieldAdditionalDerivativesProvider derivativesProvider = propagationSettings.getAdjointDynamicsProvider() + .buildFieldAdditionalDerivativesProvider(field); + propagator.addAdditionalDerivativesProvider(derivativesProvider); + return propagator; + } + + /** + * Create Field integrator. + * @param initialState initial state + * @param field type + * @return integrator. + */ + private > FieldODEIntegrator buildFieldIntegrator(final FieldSpacecraftState initialState) { + final FieldODEIntegratorBuilder integratorBuilder = propagationSettings.getIntegrationSettings() + .getFieldIntegratorBuilder(initialState.getMass().getField()); + if (initialState.isOrbitDefined()) { + final FieldOrbit orbit = initialState.getOrbit(); + return integratorBuilder.buildIntegrator(orbit, orbit.getType()); + } else { + return integratorBuilder.buildIntegrator(initialState.getAbsPVA()); + } + } +} diff --git a/src/main/java/org/orekit/control/indirect/shooting/NewtonFixedBoundaryCartesianSingleShooting.java b/src/main/java/org/orekit/control/indirect/shooting/NewtonFixedBoundaryCartesianSingleShooting.java new file mode 100644 index 0000000000..ec3544acb9 --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/shooting/NewtonFixedBoundaryCartesianSingleShooting.java @@ -0,0 +1,122 @@ +/* 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.shooting; + +import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.linear.DecompositionSolver; +import org.hipparchus.linear.LUDecomposition; +import org.hipparchus.linear.MatrixUtils; +import org.hipparchus.linear.RealMatrix; +import org.hipparchus.linear.RealVector; +import org.orekit.control.indirect.shooting.boundary.CartesianBoundaryConditionChecker; +import org.orekit.control.indirect.shooting.boundary.FixedTimeBoundaryOrbits; +import org.orekit.control.indirect.shooting.boundary.FixedTimeCartesianBoundaryStates; +import org.orekit.control.indirect.shooting.propagation.ShootingPropagationSettings; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.utils.FieldPVCoordinates; + +/** + * Class for indirect single shooting methods with Cartesian coordinates for fixed time fixed boundary. + * Update is the classical Newton-Raphson one. + * + * @author Romain Serra + * @since 12.2 + */ +public class NewtonFixedBoundaryCartesianSingleShooting extends AbstractFixedBoundaryCartesianSingleShooting { + + /** + * Constructor with boundary conditions as orbits. + * @param propagationSettings propagation settings + * @param boundaryConditions boundary conditions as {@link FixedTimeCartesianBoundaryStates} + * @param convergenceChecker convergence checker + */ + public NewtonFixedBoundaryCartesianSingleShooting(final ShootingPropagationSettings propagationSettings, + final FixedTimeCartesianBoundaryStates boundaryConditions, + final CartesianBoundaryConditionChecker convergenceChecker) { + super(propagationSettings, boundaryConditions, convergenceChecker); + } + + /** + * Constructor with boundary conditions as orbits. + * @param propagationSettings propagation settings + * @param boundaryConditions boundary conditions as {@link FixedTimeBoundaryOrbits} + * @param convergenceChecker convergence checker + */ + public NewtonFixedBoundaryCartesianSingleShooting(final ShootingPropagationSettings propagationSettings, + final FixedTimeBoundaryOrbits boundaryConditions, + final CartesianBoundaryConditionChecker convergenceChecker) { + super(propagationSettings, boundaryConditions, convergenceChecker); + } + + /** {@inheritDoc} */ + @Override + protected double[] updateAdjoint(final double[] originalInitialAdjoint, + final FieldSpacecraftState fieldTerminalState) { + // form defects and their Jacobian matrix + final double[] defects = new double[originalInitialAdjoint.length]; + final double[][] defectsJacobianData = new double[defects.length][defects.length]; + final double reciprocalScalePosition = 1. / getScalePositionDefects(); + final double reciprocalScaleVelocity = 1. / getScaleVelocityDefects(); + final FieldPVCoordinates terminalPV = fieldTerminalState.getPVCoordinates(); + final FieldVector3D fieldScaledTerminalPosition = terminalPV.getPosition().scalarMultiply(reciprocalScalePosition); + final FieldVector3D fieldScaledTerminalVelocity = terminalPV.getVelocity().scalarMultiply(reciprocalScaleVelocity); + final Vector3D terminalScaledPosition = fieldScaledTerminalPosition.toVector3D(); + final Vector3D terminalScaledVelocity = fieldScaledTerminalVelocity.toVector3D(); + final Vector3D targetScaledPosition = getTerminalCartesianState().getPosition().scalarMultiply(reciprocalScalePosition); + final Vector3D targetScaledVelocity = getTerminalCartesianState().getVelocity().scalarMultiply(reciprocalScaleVelocity); + defects[0] = terminalScaledPosition.getX() - targetScaledPosition.getX(); + defectsJacobianData[0] = fieldScaledTerminalPosition.getX().getGradient(); + defects[1] = terminalScaledPosition.getY() - targetScaledPosition.getY(); + defectsJacobianData[1] = fieldScaledTerminalPosition.getY().getGradient(); + defects[2] = terminalScaledPosition.getZ() - targetScaledPosition.getZ(); + defectsJacobianData[2] = fieldScaledTerminalPosition.getZ().getGradient(); + defects[3] = terminalScaledVelocity.getX() - targetScaledVelocity.getX(); + defectsJacobianData[3] = fieldScaledTerminalVelocity.getX().getGradient(); + defects[4] = terminalScaledVelocity.getY() - targetScaledVelocity.getY(); + defectsJacobianData[4] = fieldScaledTerminalVelocity.getY().getGradient(); + defects[5] = terminalScaledVelocity.getZ() - targetScaledVelocity.getZ(); + defectsJacobianData[5] = fieldScaledTerminalVelocity.getZ().getGradient(); + if (originalInitialAdjoint.length != 6) { + final String adjointName = getPropagationSettings().getAdjointDynamicsProvider().getAdjointName(); + final Gradient terminalMassAdjoint = fieldTerminalState.getAdditionalState(adjointName)[6]; + defects[6] = terminalMassAdjoint.getValue(); + defectsJacobianData[6] = terminalMassAdjoint.getGradient(); + } + // apply Newton's formula + final double[] correction = computeCorrection(defects, defectsJacobianData); + final double[] correctedAdjoint = originalInitialAdjoint.clone(); + for (int i = 0; i < correction.length; i++) { + correctedAdjoint[i] += correction[i]; + } + return correctedAdjoint; + } + + /** + * Compute Newton-Raphson correction. + * @param defects defects + * @param defectsJacobianData Jacobian matrix of defects w.r.t. shooting variables + * @return correction to shooting variables + */ + private double[] computeCorrection(final double[] defects, final double[][] defectsJacobianData) { + final RealMatrix defectsJacobian = MatrixUtils.createRealMatrix(defectsJacobianData); + final DecompositionSolver solver = new LUDecomposition(defectsJacobian).getSolver(); + final RealVector negatedDefects = MatrixUtils.createRealVector(defects).mapMultiply(-1); + return solver.solve(negatedDefects).toArray(); + } +} diff --git a/src/main/java/org/orekit/control/indirect/shooting/ShootingBoundaryOutput.java b/src/main/java/org/orekit/control/indirect/shooting/ShootingBoundaryOutput.java new file mode 100644 index 0000000000..19f645609f --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/shooting/ShootingBoundaryOutput.java @@ -0,0 +1,104 @@ +/* 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.shooting; + +import org.orekit.control.indirect.shooting.propagation.ShootingPropagationSettings; +import org.orekit.propagation.SpacecraftState; + +/** + * Data container for two-point boundary output of indirect shooting methods. + * + * @author Romain Serra + * @since 12.2 + * @see AbstractIndirectShooting + */ +public class ShootingBoundaryOutput { + + /** Initial propagation state. */ + private final SpacecraftState initialState; + + /** Terminal propagation state. */ + private final SpacecraftState terminalState; + + /** Propagation settings. */ + private final ShootingPropagationSettings shootingPropagationSettings; + + /** Convergence flag. */ + private final boolean converged; + + /** Iteration count. */ + private final int iterationCount; + + /** + * Constructor. + * @param converged convergence flag + * @param iterationCount iteration number + * @param initialState initial state + * @param terminalState terminal state + * @param shootingPropagationSettings propagation settings + */ + public ShootingBoundaryOutput(final boolean converged, final int iterationCount, + final SpacecraftState initialState, + final ShootingPropagationSettings shootingPropagationSettings, + final SpacecraftState terminalState) { + this.converged = converged; + this.iterationCount = iterationCount; + this.initialState = initialState; + this.terminalState = terminalState; + this.shootingPropagationSettings = shootingPropagationSettings; + } + + /** + * Getter for convergence flag. + * @return convergence flag + */ + public boolean isConverged() { + return converged; + } + + /** + * Getter for the iteration number. + * @return count + */ + public int getIterationCount() { + return iterationCount; + } + + /** + * Getter for the initial state. + * @return initial state + */ + public SpacecraftState getInitialState() { + return initialState; + } + + /** + * Getter for the terminal state. + * @return terminal state + */ + public SpacecraftState getTerminalState() { + return terminalState; + } + + /** + * Getter for the shooting propagation settings. + * @return propagation settings + */ + public ShootingPropagationSettings getShootingPropagationSettings() { + return shootingPropagationSettings; + } +} diff --git a/src/main/java/org/orekit/control/indirect/shooting/boundary/CartesianBoundaryConditionChecker.java b/src/main/java/org/orekit/control/indirect/shooting/boundary/CartesianBoundaryConditionChecker.java new file mode 100644 index 0000000000..006f7e8e67 --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/shooting/boundary/CartesianBoundaryConditionChecker.java @@ -0,0 +1,46 @@ +/* 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.shooting.boundary; + +import org.orekit.control.indirect.shooting.AbstractFixedBoundaryCartesianSingleShooting; +import org.orekit.utils.PVCoordinates; + +/** + * Interface defining convergence criterion when the terminal condition is on a Cartesian state. + * + * @author Romain Serra + * @since 12.2 + * @see AbstractFixedBoundaryCartesianSingleShooting + */ +public interface CartesianBoundaryConditionChecker { + + + /** + * Returns the maximum number of iterations. + * @return maximum iterations + */ + int getMaximumIterationCount(); + + /** + * Asserts convergence. + * @param targetPV target position-velocity + * @param actualPV actual position-velocity + * @return convergence flag + */ + boolean isConverged(PVCoordinates targetPV, PVCoordinates actualPV); + +} diff --git a/src/main/java/org/orekit/control/indirect/shooting/boundary/FixedTimeBoundaryOrbits.java b/src/main/java/org/orekit/control/indirect/shooting/boundary/FixedTimeBoundaryOrbits.java new file mode 100644 index 0000000000..b9d05488e7 --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/shooting/boundary/FixedTimeBoundaryOrbits.java @@ -0,0 +1,67 @@ +/* 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.shooting.boundary; + +import org.orekit.orbits.Orbit; + +/** + * Defines two-point boundary values for indirect shooting methods with Cartesian coordinates. + * This class represents the case where the initial and terminal times are fixed as well as the full + * Cartesian coordinates (position and velocity vectors in some frame), using {@link org.orekit.orbits.Orbit} as data holder. + *
+ * The terminal condition can be anterior in time to the initial one, it just means that the shooting method will perform backward propagation. + * Also note that any acceleration vector passed in the {@link org.orekit.orbits.Orbit} is ignored. + * + * @author Romain Serra + * @since 12.2 + * @see FixedTimeCartesianBoundaryStates + */ +public class FixedTimeBoundaryOrbits { + + /** Initial orbit (with date and frame). */ + private final Orbit initialOrbit; + + /** Terminal orbit (with date and frame). */ + private final Orbit terminalOrbit; + + /** + * Constructor. + * @param initialOrbit initial condition + * @param terminalOrbit terminal condition + */ + public FixedTimeBoundaryOrbits(final Orbit initialOrbit, + final Orbit terminalOrbit) { + this.initialOrbit = initialOrbit; + this.terminalOrbit = terminalOrbit; + } + + /** + * Getter for the initial condition. + * @return initial condition + */ + public Orbit getInitialOrbit() { + return initialOrbit; + } + + /** + * Getter for the terminal condition. + * @return terminal condition + */ + public Orbit getTerminalOrbit() { + return terminalOrbit; + } +} diff --git a/src/main/java/org/orekit/control/indirect/shooting/boundary/FixedTimeCartesianBoundaryStates.java b/src/main/java/org/orekit/control/indirect/shooting/boundary/FixedTimeCartesianBoundaryStates.java new file mode 100644 index 0000000000..cdbbd40510 --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/shooting/boundary/FixedTimeCartesianBoundaryStates.java @@ -0,0 +1,67 @@ +/* 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.shooting.boundary; + +import org.orekit.utils.AbsolutePVCoordinates; + +/** + * Defines two-point boundary values for indirect shooting methods with Cartesian coordinates. + * This class represents the case where the initial and terminal times are fixed as well as the full + * Cartesian coordinates (position and velocity vectors in some frame), using {@link AbsolutePVCoordinates} as data holder. + *
+ * The terminal condition can be anterior in time to the initial one, it just means that the shooting method will perform backward propagation. + * Also note that any acceleration vector passed in the {@link AbsolutePVCoordinates} is ignored. + * + * @author Romain Serra + * @since 12.2 + * @see FixedTimeBoundaryOrbits + */ +public class FixedTimeCartesianBoundaryStates { + + /** Initial Cartesian coordinates with date and frame. */ + private final AbsolutePVCoordinates initialCartesianState; + + /** Terminal Cartesian coordinates with date and frame. */ + private final AbsolutePVCoordinates terminalCartesianState; + + /** + * Constructor. + * @param initialCartesianState initial condition + * @param terminalCartesianState terminal condition + */ + public FixedTimeCartesianBoundaryStates(final AbsolutePVCoordinates initialCartesianState, + final AbsolutePVCoordinates terminalCartesianState) { + this.initialCartesianState = initialCartesianState; + this.terminalCartesianState = terminalCartesianState; + } + + /** + * Getter for the initial Cartesian condition. + * @return initial condition + */ + public AbsolutePVCoordinates getInitialCartesianState() { + return initialCartesianState; + } + + /** + * Getter for the terminal Cartesian condition. + * @return terminal condition + */ + public AbsolutePVCoordinates getTerminalCartesianState() { + return terminalCartesianState; + } +} diff --git a/src/main/java/org/orekit/control/indirect/shooting/boundary/NormBasedCartesianConditionChecker.java b/src/main/java/org/orekit/control/indirect/shooting/boundary/NormBasedCartesianConditionChecker.java new file mode 100644 index 0000000000..33ab1236f7 --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/shooting/boundary/NormBasedCartesianConditionChecker.java @@ -0,0 +1,68 @@ +/* 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.shooting.boundary; + +import org.hipparchus.util.FastMath; +import org.orekit.control.indirect.shooting.AbstractFixedBoundaryCartesianSingleShooting; +import org.orekit.utils.PVCoordinates; + +/** + * Class defining convergence criterion on the norm of relative position and velocity vectors, with absolute tolerances. + * + * @author Romain Serra + * @since 12.2 + * @see AbstractFixedBoundaryCartesianSingleShooting + */ +public class NormBasedCartesianConditionChecker implements CartesianBoundaryConditionChecker { + + /** Maximum iteration count. */ + private final int maximumIterationCount; + + /** Absolute tolerance when checking relative position norm. */ + private final double absoluteToleranceDistance; + + /** Absolute tolerance when checking relative velocity norm. */ + private final double absoluteToleranceSpeed; + + /** + * Constructor. + * @param maximumIterationCount maximum iteration count + * @param absoluteToleranceDistance absolute tolerance on distance + * @param absoluteToleranceSpeed absolute tolerance on speed + */ + public NormBasedCartesianConditionChecker(final int maximumIterationCount, + final double absoluteToleranceDistance, + final double absoluteToleranceSpeed) { + this.maximumIterationCount = maximumIterationCount; + this.absoluteToleranceDistance = FastMath.abs(absoluteToleranceDistance); + this.absoluteToleranceSpeed = FastMath.abs(absoluteToleranceSpeed); + } + + /** {@inheritDoc} */ + @Override + public int getMaximumIterationCount() { + return maximumIterationCount; + } + + /** {@inheritDoc} */ + @Override + public boolean isConverged(final PVCoordinates targetPV, final PVCoordinates actualPV) { + return targetPV.getPosition().subtract(actualPV.getPosition()).getNorm() < absoluteToleranceDistance && + targetPV.getVelocity().subtract(actualPV.getVelocity()).getNorm() < absoluteToleranceSpeed; + } + +} diff --git a/src/main/java/org/orekit/control/indirect/shooting/boundary/package-info.java b/src/main/java/org/orekit/control/indirect/shooting/boundary/package-info.java new file mode 100644 index 0000000000..10020e64f5 --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/shooting/boundary/package-info.java @@ -0,0 +1,25 @@ +/* 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. + */ +/** + * + * This package provides classes relative to the boundary conditions for indirect shooting. + * + * @author Romain Serra + * @since 12.2 + * + */ +package org.orekit.control.indirect.shooting.boundary; diff --git a/src/main/java/org/orekit/control/indirect/shooting/package-info.java b/src/main/java/org/orekit/control/indirect/shooting/package-info.java new file mode 100644 index 0000000000..837829d780 --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/shooting/package-info.java @@ -0,0 +1,25 @@ +/* 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. + */ +/** + * + * This package provides routines to solve indirect optimal control within the frame of orbital mechanics, using shooting methods. + * + * @author Romain Serra + * @since 12.2 + * + */ +package org.orekit.control.indirect.shooting; diff --git a/src/main/java/org/orekit/control/indirect/shooting/propagation/AdjointDynamicsProvider.java b/src/main/java/org/orekit/control/indirect/shooting/propagation/AdjointDynamicsProvider.java new file mode 100644 index 0000000000..8057b5a5ca --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/shooting/propagation/AdjointDynamicsProvider.java @@ -0,0 +1,51 @@ +/* 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.shooting.propagation; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.orekit.propagation.integration.AdditionalDerivativesProvider; +import org.orekit.propagation.integration.FieldAdditionalDerivativesProvider; + +/** + * Interface for adjoint derivatives provider (both standard and Field). + * + * @author Romain Serra + * @since 12.2 + */ +public interface AdjointDynamicsProvider { + + /** + * Getter for adjoint vector name. + * @return name + */ + String getAdjointName(); + + /** + * Builds adjoint derivatives provider. + * @return derivatives provider + */ + AdditionalDerivativesProvider buildAdditionalDerivativesProvider(); + + /** + * Builds Field adjoint derivatives provider. + * @param field type + * @param field input field + * @return derivatives provider + */ + > FieldAdditionalDerivativesProvider buildFieldAdditionalDerivativesProvider(Field field); +} diff --git a/src/main/java/org/orekit/control/indirect/shooting/propagation/CartesianAdjointDynamicsProvider.java b/src/main/java/org/orekit/control/indirect/shooting/propagation/CartesianAdjointDynamicsProvider.java new file mode 100644 index 0000000000..db9fe329c5 --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/shooting/propagation/CartesianAdjointDynamicsProvider.java @@ -0,0 +1,68 @@ +/* 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.shooting.propagation; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.orekit.control.indirect.adjoint.CartesianAdjointDerivativesProvider; +import org.orekit.control.indirect.adjoint.CartesianAdjointEquationTerm; +import org.orekit.control.indirect.adjoint.FieldCartesianAdjointDerivativesProvider; +import org.orekit.control.indirect.adjoint.cost.CartesianCost; + +/** + * Class for Cartesian adjoint derivatives provider (both standard and Field). + * + * @author Romain Serra + * @since 12.2 + */ +public class CartesianAdjointDynamicsProvider implements AdjointDynamicsProvider { + + /** Cartesian cost function. */ + private final CartesianCost cartesianCost; + + /** Cartesian adjoint terms. */ + private final CartesianAdjointEquationTerm[] equationTerms; + + /** + * Constructor. + * @param cartesianCost Cartesian cost + * @param equationTerms adjoint equation terms + */ + public CartesianAdjointDynamicsProvider(final CartesianCost cartesianCost, + final CartesianAdjointEquationTerm... equationTerms) { + this.cartesianCost = cartesianCost; + this.equationTerms = equationTerms; + } + + /** {@inheritDoc} */ + @Override + public String getAdjointName() { + return cartesianCost.getAdjointName(); + } + + /** {@inheritDoc} */ + @Override + public CartesianAdjointDerivativesProvider buildAdditionalDerivativesProvider() { + return new CartesianAdjointDerivativesProvider(cartesianCost, equationTerms); + } + + /** {@inheritDoc} */ + @Override + public > FieldCartesianAdjointDerivativesProvider buildFieldAdditionalDerivativesProvider(final Field field) { + return new FieldCartesianAdjointDerivativesProvider<>(cartesianCost, equationTerms); + } +} diff --git a/src/main/java/org/orekit/control/indirect/shooting/propagation/ClassicalRungeKuttaIntegrationSettings.java b/src/main/java/org/orekit/control/indirect/shooting/propagation/ClassicalRungeKuttaIntegrationSettings.java new file mode 100644 index 0000000000..bfb7cea0da --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/shooting/propagation/ClassicalRungeKuttaIntegrationSettings.java @@ -0,0 +1,55 @@ +/* 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.shooting.propagation; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.orekit.propagation.conversion.ClassicalRungeKuttaFieldIntegratorBuilder; +import org.orekit.propagation.conversion.ClassicalRungeKuttaIntegratorBuilder; + +/** + * Integration settings using the classical Runge-Kutta 4 scheme. + * + * @author Romain Serra + * @since 12.2 + * @see org.hipparchus.ode.nonstiff.ClassicalRungeKuttaIntegrator + */ +public class ClassicalRungeKuttaIntegrationSettings implements ShootingIntegrationSettings { + + /** Step-size for integrator builders. */ + private final double step; + + /** + * Constructor. + * @param step step-size for integrator builder + */ + public ClassicalRungeKuttaIntegrationSettings(final double step) { + this.step = step; + } + + /** {@inheritDoc} */ + @Override + public ClassicalRungeKuttaIntegratorBuilder getIntegratorBuilder() { + return new ClassicalRungeKuttaIntegratorBuilder(step); + } + + /** {@inheritDoc} */ + @Override + public > ClassicalRungeKuttaFieldIntegratorBuilder getFieldIntegratorBuilder(final Field field) { + return new ClassicalRungeKuttaFieldIntegratorBuilder<>(field.getZero().newInstance(step)); + } +} diff --git a/src/main/java/org/orekit/control/indirect/shooting/propagation/DormandPrince54IntegrationSettings.java b/src/main/java/org/orekit/control/indirect/shooting/propagation/DormandPrince54IntegrationSettings.java new file mode 100644 index 0000000000..dadd9d7d8b --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/shooting/propagation/DormandPrince54IntegrationSettings.java @@ -0,0 +1,71 @@ +/* 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.shooting.propagation; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.orekit.propagation.conversion.DormandPrince54FieldIntegratorBuilder; +import org.orekit.propagation.conversion.DormandPrince54IntegratorBuilder; + +/** + * Integration settings using the Dormand-Prince 5(4) scheme. + * + * @author Romain Serra + * @since 12.2 + * @see org.hipparchus.ode.nonstiff.ClassicalRungeKuttaIntegrator + */ +public class DormandPrince54IntegrationSettings implements ShootingIntegrationSettings { + + /** Minimum step-size for integrator builders. */ + private final double minStep; + + /** Maximum step-size for integrator builders. */ + private final double maxStep; + + /** Expected position error for integrator builders. */ + private final double dP; + + /** Expected velocity error for integrator builders. */ + private final double dV; + + /** + * Constructor. + * @param minStep minimum step-size for integrator + * @param maxStep maximum step-size for integrator + * @param dP expected position error for integrator + * @param dV expected velocity error for integrator + */ + public DormandPrince54IntegrationSettings(final double minStep, final double maxStep, + final double dP, final double dV) { + this.minStep = minStep; + this.maxStep = maxStep; + this.dP = dP; + this.dV = dV; + } + + /** {@inheritDoc} */ + @Override + public DormandPrince54IntegratorBuilder getIntegratorBuilder() { + return new DormandPrince54IntegratorBuilder(minStep, maxStep, dP, dV); + } + + /** {@inheritDoc} */ + @Override + public > DormandPrince54FieldIntegratorBuilder getFieldIntegratorBuilder(final Field field) { + return new DormandPrince54FieldIntegratorBuilder<>(minStep, maxStep, dP, dV); + } +} diff --git a/src/main/java/org/orekit/control/indirect/shooting/propagation/ShootingIntegrationSettings.java b/src/main/java/org/orekit/control/indirect/shooting/propagation/ShootingIntegrationSettings.java new file mode 100644 index 0000000000..a81f51016c --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/shooting/propagation/ShootingIntegrationSettings.java @@ -0,0 +1,48 @@ +/* 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.shooting.propagation; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.orekit.propagation.conversion.FieldODEIntegratorBuilder; +import org.orekit.propagation.conversion.ODEIntegratorBuilder; + +/** + * Defines integration settings for indirect shooting methods. Gives standard and Field integrator builders. + * + * @author Romain Serra + * @since 12.2 + * @see ShootingPropagationSettings + * @see ODEIntegratorBuilder + * @see FieldODEIntegratorBuilder + */ +public interface ShootingIntegrationSettings { + + /** + * Returns an ODE integrator builder. + * @return builder + */ + ODEIntegratorBuilder getIntegratorBuilder(); + + /** + * Returns a Field ODE integrator builder. + * @param field field for builder + * @return builder + * @param field type + */ + > FieldODEIntegratorBuilder getFieldIntegratorBuilder(Field field); +} diff --git a/src/main/java/org/orekit/control/indirect/shooting/propagation/ShootingPropagationSettings.java b/src/main/java/org/orekit/control/indirect/shooting/propagation/ShootingPropagationSettings.java new file mode 100644 index 0000000000..ec2051d19e --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/shooting/propagation/ShootingPropagationSettings.java @@ -0,0 +1,129 @@ +/* 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.shooting.propagation; + +import org.orekit.annotation.DefaultDataContext; +import org.orekit.attitudes.AttitudeProvider; +import org.orekit.attitudes.AttitudeProviderModifier; +import org.orekit.attitudes.FrameAlignedProvider; +import org.orekit.forces.ForceModel; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; + +import java.util.List; + +/** + * Defines propagation settings for indirect shooting methods. + * The provided list of {@link ForceModel} should have their counterpart in the provided adjoint equations encapsulated in {@link AdjointDynamicsProvider}. + * Note that in case of orbit-based propagation (with a central body), the Newtonian term still needs to be passed explicitly (with its adjoint equivalent). + * + * @author Romain Serra + * @since 12.2 + * @see org.orekit.propagation.numerical.NumericalPropagator + * @see org.orekit.propagation.numerical.FieldNumericalPropagator + */ +public class ShootingPropagationSettings { + + /** Force models. */ + private final List forceModels; + + /** Adjoint dynamics. */ + private final AdjointDynamicsProvider adjointDynamicsProvider; + + /** Attitude provider. */ + private final AttitudeProvider attitudeProvider; + + /** Propagation frame. */ + private final Frame propagationFrame; + + /** Integration settings. */ + private final ShootingIntegrationSettings integrationSettings; + + /** + * Simple constructor with default frame and attitude provider. + * @param forceModels forces for numerical propagation + * @param adjointDynamicsProvider adjoint derivatives provider + * @param integrationSettings integration settings + */ + @DefaultDataContext + public ShootingPropagationSettings(final List forceModels, + final AdjointDynamicsProvider adjointDynamicsProvider, + final ShootingIntegrationSettings integrationSettings) { + this(forceModels, adjointDynamicsProvider, FramesFactory.getGCRF(), integrationSettings, + AttitudeProviderModifier.getFrozenAttitudeProvider(new FrameAlignedProvider(FramesFactory.getGCRF()))); + } + + /** + * Constructor. + * @param forceModels forces for numerical propagation + * @param propagationFrame frame used as reference frame in equations of motion by integrator + * @param adjointDynamicsProvider adjoint derivatives provider + * @param integrationSettings integration settings + * @param attitudeProvider attitude provider + */ + public ShootingPropagationSettings(final List forceModels, + final AdjointDynamicsProvider adjointDynamicsProvider, + final Frame propagationFrame, + final ShootingIntegrationSettings integrationSettings, + final AttitudeProvider attitudeProvider) { + this.forceModels = forceModels; + this.adjointDynamicsProvider = adjointDynamicsProvider; + this.propagationFrame = propagationFrame; + this.integrationSettings = integrationSettings; + this.attitudeProvider = attitudeProvider; + } + + /** + * Getter for adjoint dynamics provider. + * @return adjoint dynamics + */ + public AdjointDynamicsProvider getAdjointDynamicsProvider() { + return adjointDynamicsProvider; + } + + /** + * Getter for the force models. + * @return forces + */ + public List getForceModels() { + return forceModels; + } + + /** + * Getter for the attitude provider. + * @return attitude provider. + */ + public AttitudeProvider getAttitudeProvider() { + return attitudeProvider; + } + + /** + * Getter for the propagation frame. + * @return propagation frame + */ + public Frame getPropagationFrame() { + return propagationFrame; + } + + /** + * Getter for the integration settings. + * @return integration settings + */ + public ShootingIntegrationSettings getIntegrationSettings() { + return integrationSettings; + } +} diff --git a/src/main/java/org/orekit/control/indirect/shooting/propagation/package-info.java b/src/main/java/org/orekit/control/indirect/shooting/propagation/package-info.java new file mode 100644 index 0000000000..35740c516a --- /dev/null +++ b/src/main/java/org/orekit/control/indirect/shooting/propagation/package-info.java @@ -0,0 +1,25 @@ +/* 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. + */ +/** + * + * This package provides classes relative to the propagation part of indirect shooting. + * + * @author Romain Serra + * @since 12.2 + * + */ +package org.orekit.control.indirect.shooting.propagation; diff --git a/src/main/java/org/orekit/control/package-info.java b/src/main/java/org/orekit/control/package-info.java new file mode 100644 index 0000000000..75b83eac84 --- /dev/null +++ b/src/main/java/org/orekit/control/package-info.java @@ -0,0 +1,25 @@ +/* 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. + */ +/** + * + * This package provides routines to perform optimal control within the frame of orbital mechanics. + * + * @author Romain Serra + * @since 12.2 + * + */ +package org.orekit.control; diff --git a/src/main/java/org/orekit/errors/OrekitMessages.java b/src/main/java/org/orekit/errors/OrekitMessages.java index 3adf0318ea..4818b0d5b2 100644 --- a/src/main/java/org/orekit/errors/OrekitMessages.java +++ b/src/main/java/org/orekit/errors/OrekitMessages.java @@ -16,13 +16,13 @@ */ package org.orekit.errors; -import org.hipparchus.exception.Localizable; -import org.hipparchus.exception.UTF8Control; - import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; +import org.hipparchus.exception.Localizable; +import org.hipparchus.exception.UTF8Control; + /** * Enumeration for localized messages formats. *

@@ -282,6 +282,9 @@ public enum OrekitMessages implements Localizable { /** EVENT_DATE_TOO_CLOSE. */ EVENT_DATE_TOO_CLOSE("target event date must be before {1} by {3,number,0.0##############E0} seconds or after {2} by {3,number,0.0##############E0} seconds, but target event date {0} is {4,number,0.0##############E0} seconds before {1} and {5,number,0.0##############E0} seconds after {2} so it cannot be added"), + /** WRONG_COORDINATES_FOR_ADJOINT_EQUATION. */ + WRONG_COORDINATES_FOR_ADJOINT_EQUATION("trying to propagate Cartesian adjoint whilst integration equations of motion with non-Cartesian coordinates"), + /** UNABLE_TO_READ_JPL_HEADER. */ UNABLE_TO_READ_JPL_HEADER("unable to read header record from JPL ephemerides binary file {0}"), @@ -927,7 +930,13 @@ public enum OrekitMessages implements Localizable { WALKER_INCONSISTENT_PLANES("number of planes {0} is inconsistent with number of satellites {1} in Walker constellation"), /** INFINITE_NRMSISE00_DENSITY. */ - INFINITE_NRLMSISE00_DENSITY("Infinite value appears during computation of atmospheric density in NRLMSISE00 model"); + INFINITE_NRLMSISE00_DENSITY("Infinite value appears during computation of atmospheric density in NRLMSISE00 model"), + + /** FIELD_TOO_LONG. */ + FIELD_TOO_LONG("field \"{0}\" is too long, maximum length is {1} characters"), + + /** PROPAGATOR_BUILDER_NOT_CLONEABLE. */ + PROPAGATOR_BUILDER_NOT_CLONEABLE("Propagator builder cannot be cloned"); /** Base name of the resource bundle in classpath. */ private static final String RESOURCE_BASE_NAME = "assets/org/orekit/localization/OrekitMessages"; diff --git a/src/main/java/org/orekit/estimation/measurements/EstimatedMeasurement.java b/src/main/java/org/orekit/estimation/measurements/EstimatedMeasurement.java index 6749c0f719..5302b02644 100644 --- a/src/main/java/org/orekit/estimation/measurements/EstimatedMeasurement.java +++ b/src/main/java/org/orekit/estimation/measurements/EstimatedMeasurement.java @@ -60,6 +60,18 @@ public EstimatedMeasurement(final T observedMeasurement, this.parametersDerivatives = new IdentityHashMap>(); } + /** Constructor from measurement base. + * @param estimatedMeasurementBase estimated measurement base + * @since 12.1.3 + */ + public EstimatedMeasurement(final EstimatedMeasurementBase estimatedMeasurementBase) { + this(estimatedMeasurementBase.getObservedMeasurement(), estimatedMeasurementBase.getIteration(), + estimatedMeasurementBase.getCount(), estimatedMeasurementBase.getStates(), + estimatedMeasurementBase.getParticipants()); + this.setEstimatedValue(estimatedMeasurementBase.getEstimatedValue()); + this.setStatus(estimatedMeasurementBase.getStatus()); + } + /** Get state size. *

* Warning, the {@link #setStateDerivatives(int, double[][])} diff --git a/src/main/java/org/orekit/estimation/measurements/EstimatedMeasurementBase.java b/src/main/java/org/orekit/estimation/measurements/EstimatedMeasurementBase.java index 6ad97d7086..a39ee1cc81 100644 --- a/src/main/java/org/orekit/estimation/measurements/EstimatedMeasurementBase.java +++ b/src/main/java/org/orekit/estimation/measurements/EstimatedMeasurementBase.java @@ -66,7 +66,7 @@ public class EstimatedMeasurementBase> implemen * @param count evaluations counter * @param states states of the spacecrafts * @param participants coordinates of the participants in signal travel order - * in inertial frame + * in inertial frame of first state */ public EstimatedMeasurementBase(final T observedMeasurement, final int iteration, final int count, @@ -124,7 +124,7 @@ public SpacecraftState[] getStates() { * spacecraft for two-way range measurement). *

* @return coordinates of the measurements participants in signal travel order - * in inertial frame + * in inertial frame of first state */ public TimeStampedPVCoordinates[] getParticipants() { return participants.clone(); diff --git a/src/main/java/org/orekit/estimation/measurements/InterSatellitesRange.java b/src/main/java/org/orekit/estimation/measurements/InterSatellitesRange.java index a11c86792e..edefa2d013 100644 --- a/src/main/java/org/orekit/estimation/measurements/InterSatellitesRange.java +++ b/src/main/java/org/orekit/estimation/measurements/InterSatellitesRange.java @@ -21,6 +21,7 @@ import java.util.Map; import org.hipparchus.analysis.differentiation.Gradient; +import org.orekit.frames.Frame; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; @@ -119,10 +120,11 @@ protected EstimatedMeasurementBase theoreticalEvaluationWi final SpacecraftState[] states) { // coordinates of both satellites + final Frame frame = states[0].getFrame(); final SpacecraftState local = states[0]; - final TimeStampedPVCoordinates pvaL = local.getPVCoordinates(); + final TimeStampedPVCoordinates pvaL = local.getPVCoordinates(frame); final SpacecraftState remote = states[1]; - final TimeStampedPVCoordinates pvaR = remote.getPVCoordinates(); + final TimeStampedPVCoordinates pvaR = remote.getPVCoordinates(frame); // compute propagation times // (if state has already been set up to pre-compensate propagation delay, @@ -134,7 +136,7 @@ protected EstimatedMeasurementBase theoreticalEvaluationWi final TimeStampedPVCoordinates s1Downlink = pvaL.shiftedBy(arrivalDate.durationFrom(pvaL.getDate())); - final double tauD = signalTimeOfFlight(pvaR, s1Downlink.getPosition(), arrivalDate, local.getFrame()); + final double tauD = signalTimeOfFlight(pvaR, s1Downlink.getPosition(), arrivalDate, frame); // Transit state final double delta = getDate().durationFrom(remote.getDate()); @@ -152,15 +154,15 @@ protected EstimatedMeasurementBase theoreticalEvaluationWi final double tauU = signalTimeOfFlight(pvaL, transitState.getPosition(), transitState.getDate(), - local.getFrame()); + frame); estimated = new EstimatedMeasurementBase<>(this, iteration, evaluation, new SpacecraftState[] { local.shiftedBy(deltaMTauD), remote.shiftedBy(deltaMTauD) }, new TimeStampedPVCoordinates[] { - local.shiftedBy(delta - tauD - tauU).getPVCoordinates(), - remote.shiftedBy(delta - tauD).getPVCoordinates(), - local.shiftedBy(delta).getPVCoordinates() + local.shiftedBy(delta - tauD - tauU).getPVCoordinates(frame), + remote.shiftedBy(delta - tauD).getPVCoordinates(frame), + local.shiftedBy(delta).getPVCoordinates(frame) }); // Range value @@ -173,8 +175,8 @@ protected EstimatedMeasurementBase theoreticalEvaluationWi local.shiftedBy(deltaMTauD), remote.shiftedBy(deltaMTauD) }, new TimeStampedPVCoordinates[] { - remote.shiftedBy(delta - tauD).getPVCoordinates(), - local.shiftedBy(delta).getPVCoordinates() + remote.shiftedBy(delta - tauD).getPVCoordinates(frame), + local.shiftedBy(delta).getPVCoordinates(frame) }); // Clock offsets @@ -196,6 +198,8 @@ protected EstimatedMeasurement theoreticalEvaluation(final final int evaluation, final SpacecraftState[] states) { + final Frame frame = states[0].getFrame(); + // Range derivatives are computed with respect to spacecraft states in inertial frame // ---------------------- // @@ -222,7 +226,10 @@ protected EstimatedMeasurement theoreticalEvaluation(final final SpacecraftState local = states[0]; final TimeStampedFieldPVCoordinates pvaL = getCoordinates(local, 0, nbParams); final SpacecraftState remote = states[1]; - final TimeStampedFieldPVCoordinates pvaR = getCoordinates(remote, 6, nbParams); + final TimeStampedFieldPVCoordinates pvaR = states[1]. + getFrame(). + getTransformTo(frame, states[1].getDate()). + transformPVCoordinates(getCoordinates(remote, 6, nbParams)); // compute propagation times // (if state has already been set up to pre-compensate propagation delay, @@ -236,7 +243,7 @@ protected EstimatedMeasurement theoreticalEvaluation(final final TimeStampedFieldPVCoordinates s1Downlink = pvaL.shiftedBy(arrivalDate.durationFrom(pvaL.getDate())); final Gradient tauD = signalTimeOfFlight(pvaR, s1Downlink.getPosition(), - arrivalDate, local.getFrame()); + arrivalDate, frame); // Transit state final double delta = getDate().durationFrom(remote.getDate()); @@ -254,15 +261,15 @@ protected EstimatedMeasurement theoreticalEvaluation(final final Gradient tauU = signalTimeOfFlight(pvaL, transitStateDS.getPosition(), transitStateDS.getDate(), - local.getFrame()); + frame); estimated = new EstimatedMeasurement<>(this, iteration, evaluation, new SpacecraftState[] { local.shiftedBy(deltaMTauD.getValue()), remote.shiftedBy(deltaMTauD.getValue()) }, new TimeStampedPVCoordinates[] { - local.shiftedBy(delta - tauD.getValue() - tauU.getValue()).getPVCoordinates(), - remote.shiftedBy(delta - tauD.getValue()).getPVCoordinates(), - local.shiftedBy(delta).getPVCoordinates() + local.shiftedBy(delta - tauD.getValue() - tauU.getValue()).getPVCoordinates(frame), + remote.shiftedBy(delta - tauD.getValue()).getPVCoordinates(frame), + local.shiftedBy(delta).getPVCoordinates(frame) }); // Range value @@ -275,7 +282,7 @@ protected EstimatedMeasurement theoreticalEvaluation(final local.shiftedBy(deltaMTauD.getValue()), remote.shiftedBy(deltaMTauD.getValue()) }, new TimeStampedPVCoordinates[] { - remote.shiftedBy(delta - tauD.getValue()).getPVCoordinates(), + remote.shiftedBy(delta - tauD.getValue()).getPVCoordinates(frame), local.shiftedBy(delta).getPVCoordinates() }); diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/AbstractInterSatellitesMeasurement.java b/src/main/java/org/orekit/estimation/measurements/gnss/AbstractInterSatellitesMeasurement.java index b6f433a925..d7ed375dfa 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/AbstractInterSatellitesMeasurement.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/AbstractInterSatellitesMeasurement.java @@ -83,7 +83,10 @@ protected FieldPVCoordinatesProvider getRemotePV(final SpacecraftState final TimeStampedFieldPVCoordinates pv0 = getCoordinates(states[1], 6, freeParameters); // shift to desired date - return pv0.shiftedBy(date.durationFrom(states[1].getDate())); + final TimeStampedFieldPVCoordinates shifted = pv0.shiftedBy(date.durationFrom(states[1].getDate())); + + // transform to desired frame + return states[1].getFrame().getTransformTo(frame, states[1].getDate()).transformPVCoordinates(shifted); }; } diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/AbstractOnBoardMeasurement.java b/src/main/java/org/orekit/estimation/measurements/gnss/AbstractOnBoardMeasurement.java index 1e4237744d..db3e565198 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/AbstractOnBoardMeasurement.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/AbstractOnBoardMeasurement.java @@ -23,6 +23,7 @@ import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.estimation.measurements.QuadraticClockModel; import org.orekit.estimation.measurements.QuadraticFieldClockModel; +import org.orekit.frames.Frame; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; import org.orekit.time.ClockOffset; @@ -109,7 +110,8 @@ protected OnBoardCommonParametersWithoutDerivatives computeCommonParametersWitho final boolean clockOffsetAlreadyApplied) { // local and remote satellites - final TimeStampedPVCoordinates pvaLocal = states[0].getPVCoordinates(); + final Frame frame = states[0].getFrame(); + final TimeStampedPVCoordinates pvaLocal = states[0].getPVCoordinates(frame); final ClockOffset localClock = getSatellites(). get(0). getQuadraticClockModel(). @@ -125,7 +127,7 @@ protected OnBoardCommonParametersWithoutDerivatives computeCommonParametersWitho final double deltaT = arrivalDate.durationFrom(states[0]); final TimeStampedPVCoordinates pvaDownlink = pvaLocal.shiftedBy(deltaT); final double tauD = signalTimeOfFlight(remotePV, arrivalDate, pvaDownlink.getPosition(), - arrivalDate, states[0].getFrame()); + arrivalDate, frame); // Remote satellite at signal emission final AbsoluteDate emissionDate = arrivalDate.shiftedBy(-tauD); @@ -136,7 +138,7 @@ protected OnBoardCommonParametersWithoutDerivatives computeCommonParametersWitho localClockOffset, localClockRate, remoteClockOffset, remoteClockRate, tauD, pvaDownlink, - remotePV.getPVCoordinates(emissionDate, states[0].getFrame())); + remotePV.getPVCoordinates(emissionDate, frame)); } @@ -150,6 +152,8 @@ protected OnBoardCommonParametersWithoutDerivatives computeCommonParametersWitho protected OnBoardCommonParametersWithDerivatives computeCommonParametersWith(final SpacecraftState[] states, final boolean clockOffsetAlreadyApplied) { + final Frame frame = states[0].getFrame(); + // measurement derivatives are computed with respect to spacecraft state in inertial frame // Parameters: // - 6k..6k+2 - Position of spacecraft k (counting k from 0 to nbSat-1) in inertial frame @@ -183,7 +187,7 @@ protected OnBoardCommonParametersWithDerivatives computeCommonParametersWith(fin final TimeStampedFieldPVCoordinates pvaDownlink = pvaLocal.shiftedBy(deltaT); final Gradient tauD = signalTimeOfFlight(remotePV, arrivalDate, pvaDownlink.getPosition(), arrivalDate, - states[0].getFrame()); + frame); // Remote satellite at signal emission final FieldAbsoluteDate emissionDate = arrivalDate.shiftedBy(tauD.negate()); @@ -193,7 +197,7 @@ protected OnBoardCommonParametersWithDerivatives computeCommonParametersWith(fin localClockOffset.getOffset(), localClockOffset.getRate(), remoteClockOffset.getOffset(), remoteClockOffset.getRate(), tauD, pvaDownlink, - remotePV.getPVCoordinates(emissionDate, states[0].getFrame())); + remotePV.getPVCoordinates(emissionDate, frame)); } diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/AmbiguityCache.java b/src/main/java/org/orekit/estimation/measurements/gnss/AmbiguityCache.java index 575254b05b..fb401aa71c 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/AmbiguityCache.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/AmbiguityCache.java @@ -16,11 +16,11 @@ */ package org.orekit.estimation.measurements.gnss; -import org.hipparchus.util.Precision; - import java.util.HashMap; import java.util.Map; +import org.hipparchus.util.Precision; + /** Cache for {@link AmbiguityDriver}. * @author Luc Maisonobe * @since 12.1 @@ -55,8 +55,10 @@ public AmbiguityCache() { * @return parameter driver for the emitter/receiver/wavelength triplet */ public AmbiguityDriver getAmbiguity(final String emitter, final String receiver, final double wavelength) { - return cache.computeIfAbsent(new Key(emitter, receiver, wavelength), - k -> new AmbiguityDriver(emitter, receiver, wavelength)); + final Key key = new Key(emitter, receiver, wavelength); + synchronized (cache) { + return cache.computeIfAbsent(key, k -> new AmbiguityDriver(emitter, receiver, wavelength)); + } } /** Key for the map. */ diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/WindUpFactory.java b/src/main/java/org/orekit/estimation/measurements/gnss/WindUpFactory.java index 72a4dba4a7..536771fe87 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/WindUpFactory.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/WindUpFactory.java @@ -51,15 +51,18 @@ public WindUpFactory() { public WindUp getWindUp(final SatelliteSystem system, final int prnNumber, final Dipole emitterDipole, final String receiverName) { // select satellite system - final Map> systemModifiers = - modifiers.computeIfAbsent(system, s -> new HashMap<>()); + final Map> systemModifiers; + synchronized (modifiers) { + systemModifiers = modifiers.computeIfAbsent(system, s -> new HashMap<>()); - // select satellite - final Map satelliteModifiers = + // select satellite + final Map satelliteModifiers = systemModifiers.computeIfAbsent(prnNumber, n -> new HashMap<>()); - // select receiver - return satelliteModifiers.computeIfAbsent(receiverName, r -> new WindUp(emitterDipole)); + // select receiver + return satelliteModifiers.computeIfAbsent(receiverName, r -> new WindUp(emitterDipole)); + + } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/AngularRadioRefractionModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/AngularRadioRefractionModifier.java index f574ad8ee3..50aeb8b251 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/AngularRadioRefractionModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/AngularRadioRefractionModifier.java @@ -28,15 +28,14 @@ import org.orekit.propagation.SpacecraftState; import org.orekit.utils.ParameterDriver; -/** Class modifying theoretical angular measurement with ionospheric radio refractive index. +/** Class modifying theoretical angular measurement with tropospheric radio refractive index. * A radio ray passing through the lower (non-ionized) layer of the atmosphere undergoes bending * caused by the gradient of the relative index. Since the refractive index varies mainly with * altitude, only the vertical gradient of the refractive index is considered here. - * The effect of ionospheric correction on the angular measurement is computed directly + * The effect of tropospheric correction on the angular measurement is computed directly * through the computation of the apparent elevation angle. * Recommendation ITU-R P.453-11 (07/2015) and Recommendation ITU-R P.834-7 (10/2015) * - * * @author Thierry Ceolin * @since 8.0 */ @@ -56,7 +55,7 @@ public AngularRadioRefractionModifier(final AtmosphericRefractionModel model) { /** Compute the measurement error due to troposphere refraction. * @param station station * @param state spacecraft state - * @return the measurement error due to ionosphere + * @return the measurement error due to troposphere */ private double angularErrorRadioRefractionModel(final GroundStation station, final SpacecraftState state) { @@ -85,7 +84,7 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase final SpacecraftState state = estimated.getStates()[0]; final double correction = angularErrorRadioRefractionModel(station, state); - // update estimated value taking into account the tropospheric elevation corection. + // update estimated value taking into account the tropospheric elevation correction. // The tropospheric elevation correction is directly added to the elevation. final double[] oldValue = estimated.getEstimatedValue(); final double[] newValue = oldValue.clone(); diff --git a/src/main/java/org/orekit/estimation/sequential/KalmanEstimationCommon.java b/src/main/java/org/orekit/estimation/sequential/KalmanEstimationCommon.java index a0ee0909b0..6da3867cc2 100644 --- a/src/main/java/org/orekit/estimation/sequential/KalmanEstimationCommon.java +++ b/src/main/java/org/orekit/estimation/sequential/KalmanEstimationCommon.java @@ -16,6 +16,13 @@ */ package org.orekit.estimation.sequential; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.hipparchus.filtering.kalman.ProcessEstimate; import org.hipparchus.linear.ArrayRealVector; import org.hipparchus.linear.MatrixUtils; @@ -30,13 +37,6 @@ import org.orekit.utils.ParameterDriversList; import org.orekit.utils.ParameterDriversList.DelegatingDriver; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - /** Class defining the process model dynamics to use with a {@link KalmanEstimator}. * @author Romain Gerbaud * @author Maxime Journot @@ -473,6 +473,11 @@ public Propagator[] getEstimatedPropagators() { return propagators; } + /** Get the normalized process noise matrix. + * + * @param stateDimension state dimension + * @return the normalized process noise matrix + */ protected RealMatrix getNormalizedProcessNoise(final int stateDimension) { final RealMatrix physicalProcessNoise = MatrixUtils.createRealMatrix(stateDimension, stateDimension); for (int k = 0; k < covarianceMatricesProviders.size(); ++k) { @@ -519,67 +524,116 @@ protected RealMatrix getNormalizedProcessNoise(final int stateDimension) { return KalmanEstimatorUtil.normalizeCovarianceMatrix(physicalProcessNoise, scale); } - + /** Getter for the orbitsStartColumns. + * @return the orbitsStartColumns + */ protected int[] getOrbitsStartColumns() { return orbitsStartColumns; } + /** Getter for the propagationParameterColumns. + * @return the propagationParameterColumns + */ protected Map getPropagationParameterColumns() { return propagationParameterColumns; } + /** Getter for the measurementParameterColumns. + * @return the measurementParameterColumns + */ protected Map getMeasurementParameterColumns() { return measurementParameterColumns; } + /** Getter for the estimatedPropagationParameters. + * @return the estimatedPropagationParameters + */ protected ParameterDriversList[] getEstimatedPropagationParametersArray() { return estimatedPropagationParameters; } + /** Getter for the estimatedOrbitalParameters. + * @return the estimatedOrbitalParameters + */ protected ParameterDriversList[] getEstimatedOrbitalParametersArray() { return estimatedOrbitalParameters; } + /** Getter for the covarianceIndirection. + * @return the covarianceIndirection + */ protected int[][] getCovarianceIndirection() { return covarianceIndirection; } + /** Getter for the scale. + * @return the scale + */ protected double[] getScale() { return scale; } + /** Getter for the correctedEstimate. + * @return the correctedEstimate + */ protected ProcessEstimate getCorrectedEstimate() { return correctedEstimate; } + /** Setter for the correctedEstimate. + * @param correctedEstimate the correctedEstimate + */ protected void setCorrectedEstimate(final ProcessEstimate correctedEstimate) { this.correctedEstimate = correctedEstimate; } + /** Getter for the referenceDate. + * @return the referenceDate + */ protected AbsoluteDate getReferenceDate() { return referenceDate; } + /** Increment current measurement number. */ protected void incrementCurrentMeasurementNumber() { currentMeasurementNumber += 1; } + /** Setter for the currentDate. + * @param currentDate the currentDate + */ public void setCurrentDate(final AbsoluteDate currentDate) { this.currentDate = currentDate; } + /** Set correctedSpacecraftState at index. + * + * @param correctedSpacecraftState corrected S/C state o set + * @param index index where to set in the array + */ protected void setCorrectedSpacecraftState(final SpacecraftState correctedSpacecraftState, final int index) { this.correctedSpacecraftStates[index] = correctedSpacecraftState; } + /** Set predictedSpacecraftState at index. + * + * @param predictedSpacecraftState predicted S/C state o set + * @param index index where to set in the array + */ protected void setPredictedSpacecraftState(final SpacecraftState predictedSpacecraftState, final int index) { this.predictedSpacecraftStates[index] = predictedSpacecraftState; } + /** Setter for the predictedMeasurement. + * @param predictedMeasurement the predictedMeasurement + */ protected void setPredictedMeasurement(final EstimatedMeasurement predictedMeasurement) { this.predictedMeasurement = predictedMeasurement; } + /** Setter for the correctedMeasurement. + * @param correctedMeasurement the correctedMeasurement + */ protected void setCorrectedMeasurement(final EstimatedMeasurement correctedMeasurement) { this.correctedMeasurement = correctedMeasurement; } diff --git a/src/main/java/org/orekit/estimation/sequential/KalmanModel.java b/src/main/java/org/orekit/estimation/sequential/KalmanModel.java index b1702e0789..5243ea2095 100644 --- a/src/main/java/org/orekit/estimation/sequential/KalmanModel.java +++ b/src/main/java/org/orekit/estimation/sequential/KalmanModel.java @@ -148,7 +148,7 @@ private RealMatrix getErrorStateTransitionMatrix() { // Derivatives of the state vector with respect to propagation parameters final int nbParams = estimatedPropagationParameters[k].getNbParams(); - if (nbParams > 0) { + if (nbOrbParams > 0 && nbParams > 0) { final RealMatrix dYdPp = harvesters[k].getParametersJacobian(predictedSpacecraftStates[k]); // Fill 1st row, 2nd column (dY/dPp) diff --git a/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanModel.java b/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanModel.java index c446593b55..e30f01c453 100644 --- a/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanModel.java +++ b/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanModel.java @@ -336,11 +336,7 @@ private static > EstimatedMeasurement estima final EstimatedMeasurementBase estimatedMeasurementBase = observedMeasurement. estimateWithoutDerivatives(measurementNumber, measurementNumber, KalmanEstimatorUtil.filterRelevant(observedMeasurement, spacecraftStates)); - final EstimatedMeasurement estimatedMeasurement = new EstimatedMeasurement<>(estimatedMeasurementBase.getObservedMeasurement(), - estimatedMeasurementBase.getIteration(), estimatedMeasurementBase.getCount(), - estimatedMeasurementBase.getStates(), estimatedMeasurementBase.getParticipants()); - estimatedMeasurement.setEstimatedValue(estimatedMeasurementBase.getEstimatedValue()); - return estimatedMeasurement; + return new EstimatedMeasurement<>(estimatedMeasurementBase); } /** Update parameter drivers with a normalised state, adjusting state according to the driver limits. diff --git a/src/main/java/org/orekit/files/ccsds/definitions/CenterName.java b/src/main/java/org/orekit/files/ccsds/definitions/CenterName.java index c41b61f8d6..a26e6bf675 100644 --- a/src/main/java/org/orekit/files/ccsds/definitions/CenterName.java +++ b/src/main/java/org/orekit/files/ccsds/definitions/CenterName.java @@ -88,7 +88,7 @@ public enum CenterName { /** Celestial body getter. * @return getter for celestial body */ - private final Function celestialBodyGetter; + private final transient Function celestialBodyGetter; /** Simple constructor. * @param celestialBodyGetter getter for celestial body diff --git a/src/main/java/org/orekit/files/ilrs/CPF.java b/src/main/java/org/orekit/files/ilrs/CPF.java index aaf0199108..99581fe4df 100644 --- a/src/main/java/org/orekit/files/ilrs/CPF.java +++ b/src/main/java/org/orekit/files/ilrs/CPF.java @@ -56,13 +56,13 @@ public class CPF implements EphemerisFile { private CartesianDerivativesFilter filter; /** CPF file header. */ - private CPFHeader header; + private final CPFHeader header; /** Map containing satellite information. */ - private Map ephemeris; + private final Map ephemeris; /** List of comments contained in the file. */ - private List comments; + private final List comments; /** * Constructor. @@ -114,7 +114,11 @@ public List getComments() { * @since 11.0.1 */ public void addSatelliteCoordinates(final String id, final List coord) { - ephemeris.computeIfAbsent(id, i -> new CPFEphemeris(i)).coordinates.addAll(coord); + final CPFEphemeris e; + synchronized (this) { + e = ephemeris.computeIfAbsent(id, CPFEphemeris::new); + } + e.coordinates.addAll(coord); } /** @@ -124,7 +128,11 @@ public void addSatelliteCoordinates(final String id, final List c * @since 11.0.1 */ public void addSatelliteCoordinate(final String id, final CPFCoordinate coord) { - ephemeris.computeIfAbsent(id, i -> new CPFEphemeris(i)).coordinates.add(coord); + final CPFEphemeris e; + synchronized (this) { + e = ephemeris.computeIfAbsent(id, CPFEphemeris::new); + } + e.coordinates.add(coord); } /** diff --git a/src/main/java/org/orekit/files/rinex/clock/RinexClock.java b/src/main/java/org/orekit/files/rinex/clock/RinexClock.java index cb36aca64e..764494c5ab 100644 --- a/src/main/java/org/orekit/files/rinex/clock/RinexClock.java +++ b/src/main/java/org/orekit/files/rinex/clock/RinexClock.java @@ -403,9 +403,11 @@ public Map> getSystemObservationTypes() { */ public void addSystemObservationType(final SatelliteSystem satSystem, final ObservationType observationType) { - systemObservationTypes. - computeIfAbsent(satSystem, s -> new ArrayList<>()). - add(observationType); + final List list; + synchronized (systemObservationTypes) { + list = systemObservationTypes.computeIfAbsent(satSystem, s -> new ArrayList<>()); + } + list.add(observationType); } /** Getter for the file time system. @@ -663,7 +665,11 @@ public Map> getClockData() { */ public void addClockData(final String id, final ClockDataLine clockDataLine) { - clockData.computeIfAbsent(id, i -> new ArrayList<>()).add(clockDataLine); + final List list; + synchronized (clockData) { + list = clockData.computeIfAbsent(id, i -> new ArrayList<>()); + } + list.add(clockDataLine); final AbsoluteDate epoch = clockDataLine.getEpoch(); if (epoch.isBefore(earliestEpoch)) { earliestEpoch = epoch; diff --git a/src/main/java/org/orekit/files/rinex/observation/RinexObservationHeader.java b/src/main/java/org/orekit/files/rinex/observation/RinexObservationHeader.java index 869f43a397..0c848501fd 100644 --- a/src/main/java/org/orekit/files/rinex/observation/RinexObservationHeader.java +++ b/src/main/java/org/orekit/files/rinex/observation/RinexObservationHeader.java @@ -706,8 +706,10 @@ public List getPhaseShiftCorrections() { * @param scaleFactorCorrection scale factor correction */ public void addScaleFactorCorrection(final SatelliteSystem satelliteSystem, final ScaleFactorCorrection scaleFactorCorrection) { - final List sfc = scaleFactorCorrections.computeIfAbsent(satelliteSystem, - k -> new ArrayList<>()); + final List sfc; + synchronized (scaleFactorCorrections) { + sfc = scaleFactorCorrections.computeIfAbsent(satelliteSystem, k -> new ArrayList<>()); + } sfc.add(scaleFactorCorrection); } @@ -759,7 +761,10 @@ public int getNbSat() { * @since 12.0 */ public void setNbObsPerSatellite(final SatInSystem sat, final ObservationType type, final int nbObs) { - final Map satNbObs = nbObsPerSat.computeIfAbsent(sat, k -> new HashMap<>()); + final Map satNbObs; + synchronized (nbObsPerSat) { + satNbObs = nbObsPerSat.computeIfAbsent(sat, k -> new HashMap<>()); + } satNbObs.put(type, nbObs); } diff --git a/src/main/java/org/orekit/files/rinex/observation/RinexObservationWriter.java b/src/main/java/org/orekit/files/rinex/observation/RinexObservationWriter.java index 0cadd0e273..e62867cbdc 100644 --- a/src/main/java/org/orekit/files/rinex/observation/RinexObservationWriter.java +++ b/src/main/java/org/orekit/files/rinex/observation/RinexObservationWriter.java @@ -282,7 +282,7 @@ public void writeHeader(final RinexObservationHeader header) // OBSERVER / AGENCY outputField(header.getObserverName(), 20, true); - outputField(header.getAgencyName(), 40, true); + outputField(header.getAgencyName(), 60, true); finishHeaderLine(RinexLabels.OBSERVER_AGENCY); // REC # / TYPE / VERS @@ -883,6 +883,9 @@ private void outputField(final String format, final double value, final int next */ private void outputField(final String field, final int next, final boolean leftJustified) throws IOException { final int padding = next - (field == null ? 0 : field.length()) - column; + if (padding < 0) { + throw new OrekitException(OrekitMessages.FIELD_TOO_LONG, field, next - column); + } if (leftJustified && field != null) { output.append(field); } diff --git a/src/main/java/org/orekit/files/sp3/SP3.java b/src/main/java/org/orekit/files/sp3/SP3.java index fb73334178..01d00d3870 100644 --- a/src/main/java/org/orekit/files/sp3/SP3.java +++ b/src/main/java/org/orekit/files/sp3/SP3.java @@ -240,7 +240,7 @@ public static SP3 splice(final Collection sp3) { // in order to be conservative, we keep the worst accuracy from all SP3 files for this satellite for (int i = 0; i < commonSats.size(); ++i) { final String sat = commonSats.get(i); - double accuracy = Double.POSITIVE_INFINITY; + double accuracy = 0; for (final SP3 current : sorted) { accuracy = FastMath.max(accuracy, current.header.getAccuracy(sat)); } diff --git a/src/main/java/org/orekit/files/sp3/SP3CoordinateHermiteInterpolator.java b/src/main/java/org/orekit/files/sp3/SP3CoordinateHermiteInterpolator.java index 64357cc344..4399bc9ff9 100644 --- a/src/main/java/org/orekit/files/sp3/SP3CoordinateHermiteInterpolator.java +++ b/src/main/java/org/orekit/files/sp3/SP3CoordinateHermiteInterpolator.java @@ -28,6 +28,12 @@ * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation points * (about 10-20 points) in order to avoid Runge's phenomenon * and numerical problems (including NaN appearing). + *

+ *

+ * If some clock or clock rate are present in the SP3 files as default values (999999.999999), then they + * are replaced by {@code Double.NaN} during parsing, so the interpolation will exhibit NaNs, but the + * positions will be properly interpolated. + *

* * @author Luc Maisonobe * @see HermiteInterpolator diff --git a/src/main/java/org/orekit/files/sp3/SP3Parser.java b/src/main/java/org/orekit/files/sp3/SP3Parser.java index f321b0df65..745efdb968 100644 --- a/src/main/java/org/orekit/files/sp3/SP3Parser.java +++ b/src/main/java/org/orekit/files/sp3/SP3Parser.java @@ -719,9 +719,11 @@ public void parse(final String line, final ParseInfo pi) { SP3Utils.POSITION_UNIT.toSI(Double.parseDouble(line.substring(32, 46).trim()))); // clock (microsec) - pi.latestClock = SP3Utils.CLOCK_UNIT.toSI(line.trim().length() <= 46 ? - SP3Utils.DEFAULT_CLOCK_VALUE : - Double.parseDouble(line.substring(46, 60).trim())); + final double clockField = line.trim().length() <= 46 ? + SP3Utils.DEFAULT_CLOCK_VALUE : + Double.parseDouble(line.substring(46, 60).trim()); + pi.latestClock = FastMath.abs(clockField - SP3Utils.DEFAULT_CLOCK_VALUE) < 1.0e-6 ? + Double.NaN : SP3Utils.CLOCK_UNIT.toSI(clockField); if (pi.latestPosition.getNorm() > 0) { @@ -813,9 +815,11 @@ public void parse(final String line, final ParseInfo pi) { SP3Utils.VELOCITY_UNIT.toSI(Double.parseDouble(line.substring(32, 46).trim()))); // clock rate in file is 1e-4 us / s - final double clockRateChange = SP3Utils.CLOCK_RATE_UNIT.toSI(line.trim().length() <= 46 ? - SP3Utils.DEFAULT_CLOCK_RATE_VALUE : - Double.parseDouble(line.substring(46, 60).trim())); + final double clockRateField = line.trim().length() <= 46 ? + SP3Utils.DEFAULT_CLOCK_RATE_VALUE : + Double.parseDouble(line.substring(46, 60).trim()); + final double clockRateChange = FastMath.abs(clockRateField - SP3Utils.DEFAULT_CLOCK_RATE_VALUE) < 1.0e-6 ? + Double.NaN : SP3Utils.CLOCK_RATE_UNIT.toSI(clockRateField); final Vector3D velocityAccuracy; if (line.length() < 69 || diff --git a/src/main/java/org/orekit/files/sp3/SP3Segment.java b/src/main/java/org/orekit/files/sp3/SP3Segment.java index 2a724d13eb..3d9013da9c 100644 --- a/src/main/java/org/orekit/files/sp3/SP3Segment.java +++ b/src/main/java/org/orekit/files/sp3/SP3Segment.java @@ -75,6 +75,11 @@ public SP3Segment(final double mu, final Frame frame, } /** Extract the clock model. + *

+ * If some clock or clock rate are present in the SP3 files as default values (999999.999999), then they + * filtered out here when building the clock model, so interpolation will work if at least there are + * some remaining regular values. + *

* @return extracted clock model * @since 12.1 */ @@ -83,8 +88,10 @@ public ClockModel extractClockModel() { coordinates.forEach(c -> { final AbsoluteDate date = c.getDate(); final double offset = c.getClockCorrection(); - final double rate = filter.getMaxOrder() > 0 ? c.getClockRateChange() : Double.NaN; - sample.add(new ClockOffset(date, offset, rate, Double.NaN)); + if (!Double.isNaN(offset)) { + final double rate = filter.getMaxOrder() > 0 ? c.getClockRateChange() : Double.NaN; + sample.add(new ClockOffset(date, offset, rate, Double.NaN)); + } }); return new SampledClockModel(sample, interpolationSamples); } diff --git a/src/main/java/org/orekit/forces/gravity/AbstractBodyAttraction.java b/src/main/java/org/orekit/forces/gravity/AbstractBodyAttraction.java index 932d92315e..be14eb25ce 100644 --- a/src/main/java/org/orekit/forces/gravity/AbstractBodyAttraction.java +++ b/src/main/java/org/orekit/forces/gravity/AbstractBodyAttraction.java @@ -16,15 +16,23 @@ */ package org.orekit.forces.gravity; +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.orekit.bodies.CelestialBody; import org.orekit.forces.ForceModel; +import org.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeStampedPVCoordinates; +import org.orekit.utils.TimeStampedFieldPVCoordinates; import java.util.Collections; import java.util.List; -/** Abstract class for body attraction force model. +/** Abstract class for non-central body attraction force model. * * @author Romain Serra */ @@ -65,11 +73,61 @@ public String getBodyName() { /** Protected getter for the body. * @return the third body considered + * @deprecated in next major release this shall be removed as the class will not have to use a CelestialBody */ + @Deprecated protected CelestialBody getBody() { return body; } + /** + * Get the body's position vector. + * @param date date + * @param frame frame + * @return position + * @since 12.2 + */ + protected Vector3D getBodyPosition(final AbsoluteDate date, final Frame frame) { + return body.getPosition(date, frame); + } + + /** + * Get the body's position vector. + * @param date date + * @param frame frame + * @param field type + * @return position + * @since 12.2 + */ + protected > FieldVector3D getBodyPosition(final FieldAbsoluteDate date, + final Frame frame) { + return body.getPosition(date, frame); + } + + /** + * Get the body's position-velocity-acceleration vector. + * @param date date + * @param frame frame + * @return PV + * @since 12.2 + */ + protected TimeStampedPVCoordinates getBodyPVCoordinates(final AbsoluteDate date, final Frame frame) { + return body.getPVCoordinates(date, frame); + } + + /** + * Get the body's position-velocity-acceleration vector. + * @param date date + * @param frame frame + * @param field type + * @return PV + * @since 12.2 + */ + protected > TimeStampedFieldPVCoordinates getBodyPVCoordinates(final FieldAbsoluteDate date, + final Frame frame) { + return body.getPVCoordinates(date, frame); + } + /** {@inheritDoc} */ @Override public boolean dependsOnPositionOnly() { diff --git a/src/main/java/org/orekit/forces/gravity/J2OnlyPerturbation.java b/src/main/java/org/orekit/forces/gravity/J2OnlyPerturbation.java index 077b1765f7..28aeebbf2d 100644 --- a/src/main/java/org/orekit/forces/gravity/J2OnlyPerturbation.java +++ b/src/main/java/org/orekit/forces/gravity/J2OnlyPerturbation.java @@ -170,46 +170,73 @@ public boolean dependsOnPositionOnly() { /** {@inheritDoc} */ @Override public Vector3D acceleration(final SpacecraftState state, final double[] parameters) { - final Vector3D positionInJ2Frame = state.getPosition(frame); + final AbsoluteDate date = state.getDate(); + final StaticTransform fromPropagationToJ2Frame = state.getFrame().getStaticTransformTo(frame, date); + final Vector3D positionInJ2Frame = fromPropagationToJ2Frame.transformPosition(state.getPosition()); + final double j2 = j2OverTime.value(date); + final Vector3D accelerationInJ2Frame = computeAccelerationInJ2Frame(positionInJ2Frame, mu, rEq, j2); + final StaticTransform fromJ2FrameToPropagationOne = fromPropagationToJ2Frame.getStaticInverse(); + return fromJ2FrameToPropagationOne.transformVector(accelerationInJ2Frame); + } + + /** + * Compute acceleration in J2 frame. + * @param positionInJ2Frame position in J2 frame@ + * @param mu gravitational parameter + * @param rEq equatorial radius + * @param j2 J2 coefficient + * @return acceleration in J2 frame + */ + public static Vector3D computeAccelerationInJ2Frame(final Vector3D positionInJ2Frame, final double mu, + final double rEq, final double j2) { final double squaredRadius = positionInJ2Frame.getNormSq(); final double squaredZ = positionInJ2Frame.getZ() * positionInJ2Frame.getZ(); final double ratioTimesFive = 5. * squaredZ / squaredRadius; final double ratioTimesFiveMinusOne = ratioTimesFive - 1.; - final double accelerationX = positionInJ2Frame.getX() * ratioTimesFiveMinusOne; - final double accelerationY = positionInJ2Frame.getY() * ratioTimesFiveMinusOne; - final double accelerationZ = positionInJ2Frame.getZ() * (ratioTimesFive - 3); - final Vector3D accelerationInJ2Frame = new Vector3D(accelerationX, accelerationY, accelerationZ); - final AbsoluteDate date = state.getDate(); - final StaticTransform fromJ2FrameToPropagationOne = frame.getStaticTransformTo(state.getFrame(), date); - final Vector3D transformedAcceleration = fromJ2FrameToPropagationOne.transformVector(accelerationInJ2Frame); - final double j2 = j2OverTime.value(date); + final double componentX = positionInJ2Frame.getX() * ratioTimesFiveMinusOne; + final double componentY = positionInJ2Frame.getY() * ratioTimesFiveMinusOne; + final double componentZ = positionInJ2Frame.getZ() * (ratioTimesFive - 3); final double squaredRadiiRatio = rEq * rEq / squaredRadius; final double cubedRadius = squaredRadius * FastMath.sqrt(squaredRadius); final double factor = 3 * j2 * mu * squaredRadiiRatio / (2 * cubedRadius); - return transformedAcceleration.scalarMultiply(factor); + return new Vector3D(componentX, componentY, componentZ).scalarMultiply(factor); } /** {@inheritDoc} */ @Override public > FieldVector3D acceleration(final FieldSpacecraftState state, final T[] parameters) { - final FieldVector3D positionInJ2Frame = state.getPosition(frame); + final FieldAbsoluteDate date = state.getDate(); + final FieldStaticTransform fromPropagationToJ2Frame = state.getFrame().getStaticTransformTo(frame, date); + final FieldVector3D positionInJ2Frame = fromPropagationToJ2Frame.transformPosition(state.getPosition()); + final FieldVector3D accelerationInJ2Frame = computeAccelerationInJ2Frame(positionInJ2Frame, mu, rEq, + j2OverTime.value(date)); + final FieldStaticTransform fromJ2FrameToPropagation = fromPropagationToJ2Frame.getStaticInverse(); + return fromJ2FrameToPropagation.transformVector(accelerationInJ2Frame); + } + + /** + * Compute acceleration in J2 frame. Field version. + * @param positionInJ2Frame position in J2 frame@ + * @param mu gravitational parameter + * @param rEq equatorial radius + * @param j2 J2 coefficient + * @param field type + * @return acceleration in J2 frame + */ + public static > FieldVector3D computeAccelerationInJ2Frame(final FieldVector3D positionInJ2Frame, + final double mu, final double rEq, final T j2) { final T squaredRadius = positionInJ2Frame.getNormSq(); final T squaredZ = positionInJ2Frame.getZ().square(); final T ratioTimesFive = squaredZ.multiply(5.).divide(squaredRadius); final T ratioTimesFiveMinusOne = ratioTimesFive.subtract(1.); - final T accelerationX = positionInJ2Frame.getX().multiply(ratioTimesFiveMinusOne); - final T accelerationY = positionInJ2Frame.getY().multiply(ratioTimesFiveMinusOne); - final T accelerationZ = positionInJ2Frame.getZ().multiply(ratioTimesFive.subtract(3.)); - final FieldVector3D accelerationInJ2Frame = new FieldVector3D<>(accelerationX, accelerationY, accelerationZ); - final FieldAbsoluteDate date = state.getDate(); - final FieldStaticTransform fromJ2FrameToPropagationOne = frame.getStaticTransformTo(state.getFrame(), date); - final FieldVector3D transformedAcceleration = fromJ2FrameToPropagationOne.transformVector(accelerationInJ2Frame); - final T j2 = j2OverTime.value(date); + final T componentX = positionInJ2Frame.getX().multiply(ratioTimesFiveMinusOne); + final T componentY = positionInJ2Frame.getY().multiply(ratioTimesFiveMinusOne); + final T componentZ = positionInJ2Frame.getZ().multiply(ratioTimesFive.subtract(3.)); final T squaredRadiiRatio = squaredRadius.reciprocal().multiply(rEq * rEq); final T cubedRadius = squaredRadius.multiply(FastMath.sqrt(squaredRadius)); final T factor = j2.multiply(mu).multiply(3.).multiply(squaredRadiiRatio).divide(cubedRadius.multiply(2)); - return transformedAcceleration.scalarMultiply(factor); + return new FieldVector3D<>(componentX, componentY, componentZ).scalarMultiply(factor); } /** {@inheritDoc} */ diff --git a/src/main/java/org/orekit/forces/gravity/SingleBodyAbsoluteAttraction.java b/src/main/java/org/orekit/forces/gravity/SingleBodyAbsoluteAttraction.java index b2d5d9a770..6d2b25fb5b 100644 --- a/src/main/java/org/orekit/forces/gravity/SingleBodyAbsoluteAttraction.java +++ b/src/main/java/org/orekit/forces/gravity/SingleBodyAbsoluteAttraction.java @@ -72,7 +72,7 @@ public SingleBodyAbsoluteAttraction(final CelestialBody body) { public Vector3D acceleration(final SpacecraftState s, final double[] parameters) { // compute bodies separation vectors and squared norm - final Vector3D bodyPosition = getBody().getPosition(s.getDate(), s.getFrame()); + final Vector3D bodyPosition = getBodyPosition(s.getDate(), s.getFrame()); final Vector3D satToBody = bodyPosition.subtract(s.getPosition()); final double r2Sat = satToBody.getNormSq(); @@ -86,7 +86,7 @@ public Vector3D acceleration(final SpacecraftState s, final double[] parameters) public > FieldVector3D acceleration(final FieldSpacecraftState s, final T[] parameters) { // compute bodies separation vectors and squared norm - final FieldVector3D centralToBody = getBody().getPosition(s.getDate(), s.getFrame()); + final FieldVector3D centralToBody = getBodyPosition(s.getDate(), s.getFrame()); final FieldVector3D satToBody = centralToBody.subtract(s.getPosition()); final T r2Sat = satToBody.getNormSq(); diff --git a/src/main/java/org/orekit/forces/gravity/SingleBodyRelativeAttraction.java b/src/main/java/org/orekit/forces/gravity/SingleBodyRelativeAttraction.java index cd5f76f4c1..05542d4dc8 100644 --- a/src/main/java/org/orekit/forces/gravity/SingleBodyRelativeAttraction.java +++ b/src/main/java/org/orekit/forces/gravity/SingleBodyRelativeAttraction.java @@ -45,7 +45,7 @@ public SingleBodyRelativeAttraction(final CelestialBody body) { public Vector3D acceleration(final SpacecraftState s, final double[] parameters) { // compute bodies separation vectors and squared norm - final PVCoordinates bodyPV = getBody().getPVCoordinates(s.getDate(), s.getFrame()); + final PVCoordinates bodyPV = getBodyPVCoordinates(s.getDate(), s.getFrame()); final Vector3D satToBody = bodyPV.getPosition().subtract(s.getPosition()); final double r2Sat = satToBody.getNormSq(); @@ -61,7 +61,7 @@ public > FieldVector3D acceleration(final F final T[] parameters) { // compute bodies separation vectors and squared norm - final FieldPVCoordinates bodyPV = getBody().getPVCoordinates(s.getDate(), s.getFrame()); + final FieldPVCoordinates bodyPV = getBodyPVCoordinates(s.getDate(), s.getFrame()); final FieldVector3D satToBody = bodyPV.getPosition().subtract(s.getPosition()); final T r2Sat = satToBody.getNormSq(); diff --git a/src/main/java/org/orekit/forces/gravity/ThirdBodyAttraction.java b/src/main/java/org/orekit/forces/gravity/ThirdBodyAttraction.java index 3d0f95c59a..f80c0aeafa 100644 --- a/src/main/java/org/orekit/forces/gravity/ThirdBodyAttraction.java +++ b/src/main/java/org/orekit/forces/gravity/ThirdBodyAttraction.java @@ -48,7 +48,7 @@ public Vector3D acceleration(final SpacecraftState s, final double[] parameters) final double gm = parameters[0]; // compute bodies separation vectors and squared norm - final Vector3D centralToBody = getBody().getPosition(s.getDate(), s.getFrame()); + final Vector3D centralToBody = getBodyPosition(s.getDate(), s.getFrame()); final double r2Central = centralToBody.getNormSq(); final Vector3D satToBody = centralToBody.subtract(s.getPosition()); final double r2Sat = satToBody.getNormSq(); @@ -67,7 +67,7 @@ public > FieldVector3D acceleration(final F final T gm = parameters[0]; // compute bodies separation vectors and squared norm - final FieldVector3D centralToBody = getBody().getPosition(s.getDate(), s.getFrame()); + final FieldVector3D centralToBody = getBodyPosition(s.getDate(), s.getFrame()); final T r2Central = centralToBody.getNormSq(); final FieldVector3D satToBody = centralToBody.subtract(s.getPosition()); final T r2Sat = satToBody.getNormSq(); diff --git a/src/main/java/org/orekit/forces/gravity/ThirdBodyAttractionEpoch.java b/src/main/java/org/orekit/forces/gravity/ThirdBodyAttractionEpoch.java index 9dff46a4e3..57310ce1d2 100644 --- a/src/main/java/org/orekit/forces/gravity/ThirdBodyAttractionEpoch.java +++ b/src/main/java/org/orekit/forces/gravity/ThirdBodyAttractionEpoch.java @@ -52,7 +52,7 @@ private FieldVector3D accelerationToEpoch(final SpacecraftState s, fin final double gm = parameters[0]; // compute bodies separation vectors and squared norm - final Vector3D centralToBody = getBody().getPosition(s.getDate(), s.getFrame()); + final Vector3D centralToBody = getBodyPosition(s.getDate(), s.getFrame()); // Spacecraft Position final double rx = centralToBody.getX(); @@ -83,7 +83,7 @@ private FieldVector3D accelerationToEpoch(final SpacecraftState s, fin public double[] getDerivativesToEpoch(final SpacecraftState s, final double[] parameters) { final FieldVector3D acc = accelerationToEpoch(s, parameters); - final Vector3D centralToBodyVelocity = getBody().getPVCoordinates(s.getDate(), s.getFrame()).getVelocity(); + final Vector3D centralToBodyVelocity = getBodyPVCoordinates(s.getDate(), s.getFrame()).getVelocity(); final double[] dAccxdR1i = acc.getX().getGradient(); final double[] dAccydR1i = acc.getY().getGradient(); diff --git a/src/main/java/org/orekit/forces/gravity/potential/EGMFormatReader.java b/src/main/java/org/orekit/forces/gravity/potential/EGMFormatReader.java index d6e4d8368b..558d7b9dcb 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/EGMFormatReader.java +++ b/src/main/java/org/orekit/forces/gravity/potential/EGMFormatReader.java @@ -16,22 +16,21 @@ */ package org.orekit.forces.gravity.potential; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.Precision; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.utils.Constants; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.text.ParseException; -import java.util.Arrays; import java.util.Locale; import java.util.regex.Pattern; -import org.hipparchus.util.FastMath; -import org.hipparchus.util.Precision; -import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitMessages; -import org.orekit.utils.Constants; - /**This reader is adapted to the EGM Format. * *

The proper way to use this class is to call the {@link GravityFieldFactory} @@ -101,8 +100,8 @@ public void loadData(final InputStream input, final String name) setTideSystem(TideSystem.TIDE_FREE); } - Container container = new Container(START_DEGREE_ORDER, START_DEGREE_ORDER, - missingCoefficientsAllowed() ? 0.0 : Double.NaN); + TemporaryCoefficientsContainer container = new TemporaryCoefficientsContainer(START_DEGREE_ORDER, START_DEGREE_ORDER, + missingCoefficientsAllowed() ? 0.0 : Double.NaN); boolean okFields = true; int maxDegree = -1; int maxOrder = -1; @@ -128,14 +127,14 @@ public void loadData(final InputStream input, final String name) if (i <= getMaxParseDegree() && j <= getMaxParseOrder()) { - while (!container.flattener.withinRange(i, j)) { + while (!container.getFlattener().withinRange(i, j)) { // we need to resize the container - container = container.resize(container.flattener.getDegree() * 2, - container.flattener.getOrder() * 2); + container = container.resize(container.getFlattener().getDegree() * 2, + container.getFlattener().getOrder() * 2); } - parseCoefficient(tab[2], container.flattener, container.c, i, j, "C", name); - parseCoefficient(tab[3], container.flattener, container.s, i, j, "S", name); + parseCoefficient(tab[2], container.getFlattener(), container.getC(), i, j, "C", name); + parseCoefficient(tab[3], container.getFlattener(), container.getS(), i, j, "S", name); maxDegree = FastMath.max(maxDegree, i); maxOrder = FastMath.max(maxOrder, j); @@ -150,8 +149,8 @@ public void loadData(final InputStream input, final String name) if (missingCoefficientsAllowed() && getMaxParseDegree() > 0 && getMaxParseOrder() > 0) { // ensure at least the (0, 0) element is properly set - if (Precision.equals(container.c[container.flattener.index(0, 0)], 0.0, 0)) { - container.c[container.flattener.index(0, 0)] = 1.0; + if (Precision.equals(container.getC()[container.getFlattener().index(0, 0)], 0.0, 0)) { + container.getC()[container.getFlattener().index(0, 0)] = 1.0; } } @@ -163,7 +162,7 @@ public void loadData(final InputStream input, final String name) } container = container.resize(maxDegree, maxOrder); - setRawCoefficients(true, container.flattener, container.c, container.s, name); + setRawCoefficients(true, container.getFlattener(), container.getC(), container.getS(), name); setReadComplete(true); } @@ -184,58 +183,4 @@ public RawSphericalHarmonicsProvider getProvider(final boolean wantNormalized, final int degree, final int order) { return getBaseProvider(wantNormalized, degree, order); } - - /** Temporary container for reading coefficients. - * @since 11.1 - */ - private static class Container { - - /** Converter from triangular to flat form. */ - private final Flattener flattener; - - /** Cosine coefficients. */ - private final double[] c; - - /** Sine coefficients. */ - private final double[] s; - - /** Initial value for coefficients. */ - private final double initialValue; - - /** Build a container with given degree and order. - * @param degree degree of the container - * @param order order of the container - * @param initialValue initial value for coefficients - */ - Container(final int degree, final int order, final double initialValue) { - this.flattener = new Flattener(degree, order); - this.c = new double[flattener.arraySize()]; - this.s = new double[flattener.arraySize()]; - this.initialValue = initialValue; - Arrays.fill(c, initialValue); - Arrays.fill(s, initialValue); - } - - /** Build a resized container. - * @param degree degree of the resized container - * @param order order of the resized container - * @return resized container - */ - Container resize(final int degree, final int order) { - final Container resized = new Container(degree, order, initialValue); - for (int n = 0; n <= degree; ++n) { - for (int m = 0; m <= FastMath.min(n, order); ++m) { - if (flattener.withinRange(n, m)) { - final int rIndex = resized.flattener.index(n, m); - final int index = flattener.index(n, m); - resized.c[rIndex] = c[index]; - resized.s[rIndex] = s[index]; - } - } - } - return resized; - } - - } - } diff --git a/src/main/java/org/orekit/forces/gravity/potential/GRGSFormatReader.java b/src/main/java/org/orekit/forces/gravity/potential/GRGSFormatReader.java index af7e398c82..d840af6a74 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/GRGSFormatReader.java +++ b/src/main/java/org/orekit/forces/gravity/potential/GRGSFormatReader.java @@ -16,15 +16,6 @@ */ package org.orekit.forces.gravity.potential; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.text.ParseException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - import org.hipparchus.util.FastMath; import org.hipparchus.util.Precision; import org.orekit.annotation.DefaultDataContext; @@ -37,6 +28,15 @@ import org.orekit.time.TimeScale; import org.orekit.utils.Constants; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.text.ParseException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** Reader for the GRGS gravity field format. * *

This format was used to describe various gravity fields at GRGS (Toulouse). @@ -90,7 +90,7 @@ public class GRGSFormatReader extends PotentialCoefficientsReader { }; // regular expression for data lines - final String data = "^([ 0-9]{3})([ 0-9]{3})( |DOT)\\s*(" + + final String data = "^([ 0-9]{3})([ 0-9]{3})( {3}|DOT)\\s*(" + real + sep + real + sep + real + sep + real + ")(\\s+[0-9]+)?\\s*$"; diff --git a/src/main/java/org/orekit/forces/gravity/potential/GravityFieldFactory.java b/src/main/java/org/orekit/forces/gravity/potential/GravityFieldFactory.java index d91743cb14..7c0572a1f9 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/GravityFieldFactory.java +++ b/src/main/java/org/orekit/forces/gravity/potential/GravityFieldFactory.java @@ -16,8 +16,6 @@ */ package org.orekit.forces.gravity.potential; -import java.util.List; - import org.hipparchus.util.FastMath; import org.hipparchus.util.Precision; import org.orekit.annotation.DefaultDataContext; @@ -26,6 +24,8 @@ import org.orekit.errors.OrekitMessages; import org.orekit.time.AbsoluteDate; +import java.util.List; + /** Factory used to read gravity field files in several supported formats. * @author Fabien Maussion * @author Pascal Parraud @@ -49,6 +49,9 @@ public class GravityFieldFactory { /** Default regular expression for GRGS files. */ public static final String GRGS_FILENAME = "^grim\\d_.*$"; + /** Default regular expression for SHA files. */ + public static final String SHA_FILENAME = "^sha\\..*$"; + /** Default regular expression for FES Cnm, Snm tides files. */ public static final String FES_CNM_SNM_FILENAME = "^fes(\\d)+_Cnm-Snm.dat$"; @@ -92,9 +95,10 @@ public static void addPotentialCoefficientsReader(final PotentialCoefficientsRea /** Add the default readers for gravity fields. *

- * The default READERS supports ICGEM, SHM, EGM and GRGS formats with the + * The default READERS supports ICGEM, SHM, EGM, GRGS and SHA formats with the * default names {@link #ICGEM_FILENAME}, {@link #SHM_FILENAME}, {@link - * #EGM_FILENAME}, {@link #GRGS_FILENAME} and don't allow missing coefficients. + * #EGM_FILENAME}, {@link #GRGS_FILENAME}, {@link #SHA_FILENAME} + * and don't allow missing coefficients. *

* @see #addPotentialCoefficientsReader(PotentialCoefficientsReader) * @see #clearPotentialCoefficientsReaders() diff --git a/src/main/java/org/orekit/forces/gravity/potential/ICGEMFormatReader.java b/src/main/java/org/orekit/forces/gravity/potential/ICGEMFormatReader.java index 464c8ca5e1..5c92eed335 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/ICGEMFormatReader.java +++ b/src/main/java/org/orekit/forces/gravity/potential/ICGEMFormatReader.java @@ -16,18 +16,6 @@ */ package org.orekit.forces.gravity.potential; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - import org.hipparchus.util.FastMath; import org.hipparchus.util.MathUtils; import org.hipparchus.util.Precision; @@ -42,6 +30,18 @@ import org.orekit.utils.Constants; import org.orekit.utils.TimeSpanMap; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** Reader for the ICGEM gravity field format. * *

This format is used to describe the gravity field of EIGEN models @@ -232,7 +232,7 @@ public void loadData(final InputStream input, final String name) boolean parseError = false; ++lineNumber; line = line.trim(); - if (line.length() == 0) { + if (line.isEmpty()) { continue; } final String[] tab = SEPARATOR.split(line); diff --git a/src/main/java/org/orekit/forces/gravity/potential/LazyLoadedGravityFields.java b/src/main/java/org/orekit/forces/gravity/potential/LazyLoadedGravityFields.java index 4be5f71103..8da5579970 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/LazyLoadedGravityFields.java +++ b/src/main/java/org/orekit/forces/gravity/potential/LazyLoadedGravityFields.java @@ -85,10 +85,11 @@ public void addPotentialCoefficientsReader(final PotentialCoefficientsReader rea /** * Add the default readers for gravity fields. * - *

The default readers support ICGEM, SHM, EGM and GRGS formats with the + *

The default readers support ICGEM, SHM, EGM, GRGS and SHA formats with the * default names {@link GravityFieldFactory#ICGEM_FILENAME}, {@link * GravityFieldFactory#SHM_FILENAME}, {@link GravityFieldFactory#EGM_FILENAME}, {@link - * GravityFieldFactory#GRGS_FILENAME} and don't allow missing coefficients. + * GravityFieldFactory#GRGS_FILENAME}, {@link GravityFieldFactory#SHA_FILENAME} + * and don't allow missing coefficients. * * @see #addPotentialCoefficientsReader(PotentialCoefficientsReader) * @see #clearPotentialCoefficientsReaders() @@ -99,6 +100,7 @@ public void addDefaultPotentialCoefficientsReaders() { readers.add(new SHMFormatReader(GravityFieldFactory.SHM_FILENAME, false, timeScale)); readers.add(new EGMFormatReader(GravityFieldFactory.EGM_FILENAME, false)); readers.add(new GRGSFormatReader(GravityFieldFactory.GRGS_FILENAME, false, timeScale)); + readers.add(new SHAFormatReader(GravityFieldFactory.SHA_FILENAME, false)); } } diff --git a/src/main/java/org/orekit/forces/gravity/potential/SHAFormatReader.java b/src/main/java/org/orekit/forces/gravity/potential/SHAFormatReader.java new file mode 100644 index 0000000000..8181b99a7d --- /dev/null +++ b/src/main/java/org/orekit/forces/gravity/potential/SHAFormatReader.java @@ -0,0 +1,151 @@ +/* Contributed in the public domain. + * 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.forces.gravity.potential; + +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.text.ParseException; +import java.util.regex.Pattern; + +/** Reader for the SHA gravity field format. + * + *

This format is used by some lunar gravity models distributed by + * NASA's Planetary Geology, Geophysics and Geochemistry Laboratory such as + * GRGM1200B and GRGM1200L. It is a simple ASCII format, described in + * the GRGM1200B model product site. + * The first line contains 4 constants: model GM, mean radius, maximum degree and maximum order. + * All other lines contain 6 entries: degree, order, Clm, Slm, sigma Clm and sigma Slm + * (formal errors of Clm and Slm). + * + *

The proper way to use this class is to call the {@link GravityFieldFactory} + * which will determine which reader to use with the selected gravity field file.

+ * + * @see GravityFields + * @author Rafael Ayala + */ +public class SHAFormatReader extends PotentialCoefficientsReader { + + /** Default "0" text value. */ + private static final String ZERO = "0.0"; + + /** Default "1" text value. */ + private static final String ONE = "1.0"; + + /** Expression for multiple spaces. */ + private static final String SPACES = "\\s+"; + + /** Pattern for delimiting regular expressions. */ + private static final Pattern SEPARATOR = Pattern.compile(SPACES); + + /** Pattern for real numbers. */ + private static final Pattern REAL = Pattern.compile("[-+]?\\d?\\.\\d+[eEdD][-+]\\d\\d"); + + /** Pattern for header line. */ + private static final Pattern HEADER_LINE = Pattern.compile("^\\s*" + REAL + SPACES + REAL + "\\s+\\d+\\s+\\d+\\s*$"); + + /** Pattern for data lines. */ + private static final Pattern DATA_LINE = Pattern.compile("^\\s*\\d+\\s+\\d+\\s+" + REAL + SPACES + REAL + SPACES + REAL + SPACES + REAL + "\\s*$"); + + /** Start degree and order for coefficients container. */ + private static final int START_DEGREE_ORDER = 120; + + /** Simple constructor. + * @param supportedNames regular expression for supported files names + * @param missingCoefficientsAllowed if true, allows missing coefficients in the input data + * @since 12.2 + */ + public SHAFormatReader(final String supportedNames, final boolean missingCoefficientsAllowed) { + super(supportedNames, missingCoefficientsAllowed, null); + } + + /** {@inheritDoc} */ + public void loadData(final InputStream input, final String name) throws IOException, ParseException, OrekitException { + + // reset the indicator before loading any data + setReadComplete(false); + setTideSystem(TideSystem.UNKNOWN); + int lineNumber = 0; + int maxDegree; + int maxOrder; + String line = null; + TemporaryCoefficientsContainer container = new TemporaryCoefficientsContainer(START_DEGREE_ORDER, START_DEGREE_ORDER, + missingCoefficientsAllowed() ? 0.0 : Double.NaN); + try (BufferedReader r = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) { + for (line = r.readLine(); line != null; line = r.readLine()) { + ++lineNumber; + if (lineNumber == 1) { + // match the pattern of the header line + if (!HEADER_LINE.matcher(line).matches()) { + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + lineNumber, name, line); + } + final String[] headerFields = SEPARATOR.split(line.trim()); + setMu(Double.parseDouble(headerFields[0])); + setAe(Double.parseDouble(headerFields[1])); + maxDegree = Integer.parseInt(headerFields[2]); + maxOrder = Integer.parseInt(headerFields[3]); + container = container.resize(maxDegree, maxOrder); + parseCoefficient(ONE, container.getFlattener(), container.getC(), 0, 0, "C", name); + parseCoefficient(ZERO, container.getFlattener(), container.getS(), 0, 0, "S", name); + parseCoefficient(ZERO, container.getFlattener(), container.getS(), 1, 0, "C", name); + parseCoefficient(ZERO, container.getFlattener(), container.getS(), 1, 0, "S", name); + parseCoefficient(ZERO, container.getFlattener(), container.getS(), 1, 1, "C", name); + parseCoefficient(ZERO, container.getFlattener(), container.getS(), 1, 1, "S", name); + } else if (lineNumber > 1) { + // match the pattern of the data lines + if (!DATA_LINE.matcher(line).matches()) { + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + lineNumber, name, line); + } + final String[] dataFields = SEPARATOR.split(line.trim()); + // we want to assign the values of the data fields to the corresponding variables + final int n = Integer.parseInt(dataFields[0]); + final int m = Integer.parseInt(dataFields[1]); + parseCoefficient(dataFields[2], container.getFlattener(), container.getC(), n, m, "C", name); + parseCoefficient(dataFields[3], container.getFlattener(), container.getS(), n, m, "S", name); + } + } + } catch (NumberFormatException nfe) { + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + lineNumber, name, line); + } + setRawCoefficients(true, container.getFlattener(), container.getC(), container.getS(), name); + setReadComplete(true); + } + + /** Provider for read spherical harmonics coefficients. + * Like EGM fields, SHA fields don't include time-dependent parts, + * so this method returns directly a constant provider. + * @param wantNormalized if true, the provider will provide normalized coefficients, + * otherwise it will provide un-normalized coefficients + * @param degree maximal degree + * @param order maximal order + * @return a new provider + * @since 12.2 + */ + public RawSphericalHarmonicsProvider getProvider(final boolean wantNormalized, + final int degree, final int order) { + return getBaseProvider(wantNormalized, degree, order); + } + +} diff --git a/src/main/java/org/orekit/forces/gravity/potential/TemporaryCoefficientsContainer.java b/src/main/java/org/orekit/forces/gravity/potential/TemporaryCoefficientsContainer.java new file mode 100644 index 0000000000..1d37eedb6d --- /dev/null +++ b/src/main/java/org/orekit/forces/gravity/potential/TemporaryCoefficientsContainer.java @@ -0,0 +1,99 @@ +/* Copyright 2002-2024 CS GROUP + * 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.forces.gravity.potential; + +import org.hipparchus.util.FastMath; + +import java.util.Arrays; + +/** + * Temporary container for reading gravity field coefficients. + * @since 12.2 + * @author Fabien Maussion + */ +class TemporaryCoefficientsContainer { + + /** Converter from triangular to flat form. */ + private final Flattener flattener; + + /** Cosine coefficients. */ + private final double[] c; + + /** Sine coefficients. */ + private final double[] s; + + /** Initial value for coefficients. */ + private final double initialValue; + + /** Build a container with given degree and order. + * @param degree degree of the container + * @param order order of the container + * @param initialValue initial value for coefficients + */ + TemporaryCoefficientsContainer(final int degree, final int order, final double initialValue) { + this.flattener = new Flattener(degree, order); + this.c = new double[flattener.arraySize()]; + this.s = new double[flattener.arraySize()]; + this.initialValue = initialValue; + Arrays.fill(c, initialValue); + Arrays.fill(s, initialValue); + } + + /** Build a resized container. + * @param degree degree of the resized container + * @param order order of the resized container + * @return resized container + */ + TemporaryCoefficientsContainer resize(final int degree, final int order) { + final TemporaryCoefficientsContainer resized = new TemporaryCoefficientsContainer(degree, order, initialValue); + for (int n = 0; n <= degree; ++n) { + for (int m = 0; m <= FastMath.min(n, order); ++m) { + if (flattener.withinRange(n, m)) { + final int rIndex = resized.flattener.index(n, m); + final int index = flattener.index(n, m); + resized.c[rIndex] = c[index]; + resized.s[rIndex] = s[index]; + } + } + } + return resized; + } + + /** + * Get the converter from triangular to flat form. + * @return the converter from triangular to flat form + */ + Flattener getFlattener() { + return flattener; + } + + /** + * Get the cosine coefficients. + * @return the cosine coefficients + */ + public double[] getC() { + return c; + } + + /** + * Get the sine coefficients. + * @return the cosine coefficients + */ + public double[] getS() { + return s; + } +} diff --git a/src/main/java/org/orekit/forces/maneuvers/FieldImpulseManeuver.java b/src/main/java/org/orekit/forces/maneuvers/FieldImpulseManeuver.java index 43fa92f129..4619d2a2cb 100644 --- a/src/main/java/org/orekit/forces/maneuvers/FieldImpulseManeuver.java +++ b/src/main/java/org/orekit/forces/maneuvers/FieldImpulseManeuver.java @@ -26,6 +26,7 @@ import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.events.FieldAbstractDetector; import org.orekit.propagation.events.FieldAdaptableInterval; +import org.orekit.propagation.events.FieldEventDetectionSettings; import org.orekit.propagation.events.FieldEventDetector; import org.orekit.propagation.events.handlers.FieldEventHandler; import org.orekit.time.FieldAbsoluteDate; @@ -116,8 +117,7 @@ public FieldImpulseManeuver(final D trigger, final FieldVector3D deltaVSat, f */ public FieldImpulseManeuver(final D trigger, final AttitudeProvider attitudeOverride, final FieldVector3D deltaVSat, final T isp) { - this(trigger.getMaxCheckInterval(), trigger.getThreshold(), trigger.getMaxIterationCount(), - new Handler<>(), trigger, attitudeOverride, deltaVSat, isp, + this(trigger.getDetectionSettings(), new Handler<>(), trigger, attitudeOverride, deltaVSat, isp, Control3DVectorCostType.TWO_NORM); } @@ -131,8 +131,7 @@ public FieldImpulseManeuver(final D trigger, final AttitudeProvider attitudeOver public FieldImpulseManeuver(final D trigger, final AttitudeProvider attitudeOverride, final FieldVector3D deltaVSat, final T isp, final Control3DVectorCostType control3DVectorCostType) { - this(trigger.getMaxCheckInterval(), trigger.getThreshold(), trigger.getMaxIterationCount(), - new Handler<>(), trigger, attitudeOverride, deltaVSat, isp, control3DVectorCostType); + this(trigger.getDetectionSettings(), new Handler<>(), trigger, attitudeOverride, deltaVSat, isp, control3DVectorCostType); } /** Private constructor with full parameters. @@ -141,9 +140,7 @@ public FieldImpulseManeuver(final D trigger, final AttitudeProvider attitudeOver * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

- * @param maxCheck maximum checking interval - * @param threshold convergence threshold (s) - * @param maxIter maximum number of iterations in the event time search + * @param detectionSettings detection settings * @param eventHandler event handler to call at event occurrences * @param trigger triggering event * @param attitudeOverride the attitude provider to use for the maneuver @@ -151,11 +148,11 @@ public FieldImpulseManeuver(final D trigger, final AttitudeProvider attitudeOver * @param isp engine specific impulse (s) * @param control3DVectorCostType increment's norm for mass consumption */ - private FieldImpulseManeuver(final FieldAdaptableInterval maxCheck, final T threshold, final int maxIter, + private FieldImpulseManeuver(final FieldEventDetectionSettings detectionSettings, final FieldEventHandler eventHandler, final D trigger, final AttitudeProvider attitudeOverride, final FieldVector3D deltaVSat, final T isp, final Control3DVectorCostType control3DVectorCostType) { - super(maxCheck, threshold, maxIter, eventHandler); + super(detectionSettings, eventHandler); this.trigger = trigger; this.deltaVSat = deltaVSat; this.isp = isp; @@ -169,7 +166,7 @@ private FieldImpulseManeuver(final FieldAdaptableInterval maxCheck, final T t protected FieldImpulseManeuver create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, final int newMaxIter, final FieldEventHandler fieldEventHandler) { - return new FieldImpulseManeuver<>(newMaxCheck, newThreshold, newMaxIter, fieldEventHandler, + return new FieldImpulseManeuver<>(new FieldEventDetectionSettings<>(newMaxCheck, newThreshold, newMaxIter), fieldEventHandler, trigger, attitudeOverride, deltaVSat, isp, control3DVectorCostType); } diff --git a/src/main/java/org/orekit/forces/maneuvers/ImpulseManeuver.java b/src/main/java/org/orekit/forces/maneuvers/ImpulseManeuver.java index fd0b599cf5..6e5b912558 100644 --- a/src/main/java/org/orekit/forces/maneuvers/ImpulseManeuver.java +++ b/src/main/java/org/orekit/forces/maneuvers/ImpulseManeuver.java @@ -25,6 +25,7 @@ import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.events.AbstractDetector; import org.orekit.propagation.events.AdaptableInterval; +import org.orekit.propagation.events.EventDetectionSettings; import org.orekit.propagation.events.EventDetector; import org.orekit.propagation.events.handlers.EventHandler; import org.orekit.time.AbsoluteDate; @@ -150,7 +151,7 @@ protected ImpulseManeuver(final AdaptableInterval maxCheck, final double thresho final int maxIter, final EventHandler handler, final EventDetector trigger, final AttitudeProvider attitudeOverride, final Vector3D deltaVSat, final double isp, final Control3DVectorCostType control3DVectorCostType) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.attitudeOverride = attitudeOverride; this.trigger = trigger; this.deltaVSat = deltaVSat; diff --git a/src/main/java/org/orekit/forces/maneuvers/propulsion/ProfileThrustPropulsionModel.java b/src/main/java/org/orekit/forces/maneuvers/propulsion/ProfileThrustPropulsionModel.java index c438c89b5f..e8ed287e59 100644 --- a/src/main/java/org/orekit/forces/maneuvers/propulsion/ProfileThrustPropulsionModel.java +++ b/src/main/java/org/orekit/forces/maneuvers/propulsion/ProfileThrustPropulsionModel.java @@ -26,7 +26,6 @@ import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.hipparchus.ode.events.Action; import org.hipparchus.util.FastMath; import org.orekit.forces.maneuvers.Control3DVectorCostType; import org.orekit.propagation.FieldSpacecraftState; @@ -35,6 +34,8 @@ import org.orekit.propagation.events.EventDetector; import org.orekit.propagation.events.FieldDateDetector; import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.propagation.events.handlers.FieldResetDerivativesOnEvent; +import org.orekit.propagation.events.handlers.ResetDerivativesOnEvent; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.FieldTimeStamped; import org.orekit.utils.Constants; @@ -154,7 +155,7 @@ public Stream getEventDetectors() { withMaxCheck(0.5 * shortest). withMinGap(0.5 * shortest). withThreshold(DATATION_ACCURACY). - withHandler((state, det, increasing) -> Action.RESET_DERIVATIVES); + withHandler(new ResetDerivativesOnEvent()); for (TimeSpanMap.Transition transition = profile.getFirstTransition(); transition != null; transition = transition.next()) { @@ -178,7 +179,7 @@ public > Stream> getFiel withMaxCheck(0.5 * shortest). withMinGap(0.5 * shortest). withThreshold(field.getZero().newInstance(DATATION_ACCURACY)). - withHandler((state, det, increasing) -> Action.RESET_DERIVATIVES); + withHandler(new FieldResetDerivativesOnEvent<>()); for (TimeSpanMap.Transition transition = profile.getFirstTransition(); transition != null; transition = transition.next()) { diff --git a/src/main/java/org/orekit/forces/radiation/AbstractLightFluxModel.java b/src/main/java/org/orekit/forces/radiation/AbstractLightFluxModel.java index 6a79584f8b..4fcc05bf03 100644 --- a/src/main/java/org/orekit/forces/radiation/AbstractLightFluxModel.java +++ b/src/main/java/org/orekit/forces/radiation/AbstractLightFluxModel.java @@ -116,6 +116,16 @@ protected > FieldVector3D getOccultedBodyPo */ protected abstract > FieldVector3D getUnoccultedFluxVector(FieldVector3D relativePosition); + /** Get the lighting ratio ([0-1]). + * @param state state + * @return lighting ratio + */ + public double getLightingRatio(final SpacecraftState state) { + final Vector3D position = state.getPosition(); + final Vector3D lightSourcePosition = getOccultedBodyPosition(state.getDate(), state.getFrame()); + return getLightingRatio(position, lightSourcePosition); + } + /** Get the lighting ratio ([0-1]). * @param position object's position * @param occultedBodyPosition occulted body position in same frame @@ -123,6 +133,17 @@ protected > FieldVector3D getOccultedBodyPo */ protected abstract double getLightingRatio(Vector3D position, Vector3D occultedBodyPosition); + /** Get the lighting ratio ([0-1]). + * @param state state + * @param field type + * @return lighting ratio + */ + public > T getLightingRatio(final FieldSpacecraftState state) { + final FieldVector3D position = state.getPosition(); + final FieldVector3D lightSourcePosition = getOccultedBodyPosition(state.getDate(), state.getFrame()); + return getLightingRatio(position, lightSourcePosition); + } + /** Get the lighting ratio ([0-1]). Field version. * @param position object's position * @param occultedBodyPosition occulted body position in same frame diff --git a/src/main/java/org/orekit/forces/radiation/AbstractRadiationForceModel.java b/src/main/java/org/orekit/forces/radiation/AbstractRadiationForceModel.java index 53eb7e4f8b..cefad2799c 100644 --- a/src/main/java/org/orekit/forces/radiation/AbstractRadiationForceModel.java +++ b/src/main/java/org/orekit/forces/radiation/AbstractRadiationForceModel.java @@ -26,7 +26,6 @@ import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.hipparchus.ode.events.Action; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathArrays; import org.orekit.bodies.OneAxisEllipsoid; @@ -35,6 +34,8 @@ import org.orekit.propagation.events.EventDetector; import org.orekit.propagation.events.FieldEclipseDetector; import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.propagation.events.handlers.FieldResetDerivativesOnEvent; +import org.orekit.propagation.events.handlers.ResetDerivativesOnEvent; import org.orekit.utils.Constants; import org.orekit.utils.ExtendedPVCoordinatesProvider; import org.orekit.utils.ExtendedPVCoordinatesProviderAdapter; @@ -92,13 +93,13 @@ public Stream getEventDetectors() { withMargin(-ANGULAR_MARGIN). withMaxCheck(ECLIPSE_MAX_CHECK). withThreshold(ECLIPSE_THRESHOLD). - withHandler((state, detector, increasing) -> Action.RESET_DERIVATIVES); + withHandler(new ResetDerivativesOnEvent()); detectors[2 * i + 1] = new EclipseDetector(occulting). withPenumbra(). withMargin(ANGULAR_MARGIN). withMaxCheck(ECLIPSE_MAX_CHECK). withThreshold(ECLIPSE_THRESHOLD). - withHandler((state, detector, increasing) -> Action.RESET_DERIVATIVES); + withHandler(new ResetDerivativesOnEvent()); } // Fusion between Date detector for parameter driver span change and // Detector for umbra / penumbra events @@ -119,13 +120,13 @@ public > Stream> getFiel withMargin(zero.newInstance(-ANGULAR_MARGIN)). withMaxCheck(ECLIPSE_MAX_CHECK). withThreshold(zero.newInstance(ECLIPSE_THRESHOLD)). - withHandler((state, detector, increasing) -> Action.RESET_DERIVATIVES); + withHandler(new FieldResetDerivativesOnEvent<>()); detectors[2 * i + 1] = new FieldEclipseDetector<>(field, occulting). withPenumbra(). withMargin(zero.newInstance(ANGULAR_MARGIN)). withMaxCheck(ECLIPSE_MAX_CHECK). withThreshold(zero.newInstance(ECLIPSE_THRESHOLD)). - withHandler((state, detector, increasing) -> Action.RESET_DERIVATIVES); + withHandler(new FieldResetDerivativesOnEvent<>()); } return Stream.concat(Stream.of(detectors), RadiationForceModel.super.getFieldEventDetectors(field)); } diff --git a/src/main/java/org/orekit/forces/radiation/AbstractSolarLightFluxModel.java b/src/main/java/org/orekit/forces/radiation/AbstractSolarLightFluxModel.java new file mode 100644 index 0000000000..1e2ef416ca --- /dev/null +++ b/src/main/java/org/orekit/forces/radiation/AbstractSolarLightFluxModel.java @@ -0,0 +1,104 @@ +/* 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.forces.radiation; + +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.orekit.propagation.events.EventDetectionSettings; +import org.orekit.utils.ExtendedPositionProvider; + +/** + * Abstract class for the definition of the solar flux model with a single occulting body of spherical shape. + * + * @author Romain Serra + * @see LightFluxModel + * @since 12.2 + */ +public abstract class AbstractSolarLightFluxModel extends AbstractLightFluxModel { + + /** Radius of central, occulting body (approximated as spherical). + * Its center is assumed to be at the origin of the frame linked to the state. */ + private final double occultingBodyRadius; + + /** Reference flux normalized for a 1m distance (N). */ + private final double kRef; + + /** Eclipse detection settings. */ + private final EventDetectionSettings eventDetectionSettings; + + /** + * Constructor. + * @param kRef reference flux + * @param occultedBody position provider for light source + * @param occultingBodyRadius radius of central, occulting body + * @param eventDetectionSettings user-defined detection settings for eclipses (if ill-tuned, events might be missed or performance might drop) + */ + protected AbstractSolarLightFluxModel(final double kRef, final ExtendedPositionProvider occultedBody, + final double occultingBodyRadius, final EventDetectionSettings eventDetectionSettings) { + super(occultedBody); + this.kRef = kRef; + this.occultingBodyRadius = occultingBodyRadius; + this.eventDetectionSettings = eventDetectionSettings; + } + + /** + * Constructor with default value for reference flux. + * @param occultedBody position provider for light source + * @param occultingBodyRadius radius of central, occulting body + * @param eventDetectionSettings user-defined detection settings for eclipses (if ill-tuned, events might be missed or performance might drop) + */ + protected AbstractSolarLightFluxModel(final ExtendedPositionProvider occultedBody, final double occultingBodyRadius, + final EventDetectionSettings eventDetectionSettings) { + this(4.56e-6 * FastMath.pow(149597870000.0, 2), occultedBody, occultingBodyRadius, + eventDetectionSettings); + } + + /** + * Getter for occulting body radius. + * @return radius + */ + public double getOccultingBodyRadius() { + return occultingBodyRadius; + } + + /** + * Getter for eclipse event detection settings used for eclipses. + * @return event detection settings + */ + public EventDetectionSettings getEventDetectionSettings() { + return eventDetectionSettings; + } + + /** {@inheritDoc} */ + @Override + protected Vector3D getUnoccultedFluxVector(final Vector3D relativePosition) { + final double squaredRadius = relativePosition.getNormSq(); + final double factor = kRef / (squaredRadius * FastMath.sqrt(squaredRadius)); + return relativePosition.scalarMultiply(factor); + } + + /** {@inheritDoc} */ + @Override + protected > FieldVector3D getUnoccultedFluxVector(final FieldVector3D relativePosition) { + final T squaredRadius = relativePosition.getNormSq(); + final T factor = (squaredRadius.multiply(squaredRadius.sqrt())).reciprocal().multiply(kRef); + return relativePosition.scalarMultiply(factor); + } + +} diff --git a/src/main/java/org/orekit/forces/radiation/ConicallyShadowedLightFluxModel.java b/src/main/java/org/orekit/forces/radiation/ConicallyShadowedLightFluxModel.java new file mode 100644 index 0000000000..83a808e742 --- /dev/null +++ b/src/main/java/org/orekit/forces/radiation/ConicallyShadowedLightFluxModel.java @@ -0,0 +1,450 @@ +/* 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.forces.radiation; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; +import org.orekit.frames.Frame; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.AdaptableInterval; +import org.orekit.propagation.events.EventDetector; +import org.orekit.propagation.events.EventDetectionSettings; +import org.orekit.propagation.events.FieldAdaptableInterval; +import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.propagation.events.FieldEventDetectionSettings; +import org.orekit.propagation.events.handlers.EventHandler; +import org.orekit.propagation.events.handlers.FieldEventHandler; +import org.orekit.propagation.events.handlers.FieldResetDerivativesOnEvent; +import org.orekit.propagation.events.handlers.ResetDerivativesOnEvent; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.ExtendedPositionProvider; + +import java.util.ArrayList; +import java.util.List; + +/** + * Class defining a flux model from a single occulted body, casting a shadow on a spherical occulting body. + * It cannot model oblate bodies or multiple occulting objects (for this, see {@link SolarRadiationPressure}). + * + * @author Romain Serra + * @see AbstractSolarLightFluxModel + * @see LightFluxModel + * @see "Montenbruck, Oliver, and Gill, Eberhard. Satellite orbits : models, methods, and + * * applications. Berlin New York: Springer, 2000." + * @since 12.2 + */ +public class ConicallyShadowedLightFluxModel extends AbstractSolarLightFluxModel { + + /** + * Default max. check interval for eclipse detection. + */ + private static final double CONICAL_ECLIPSE_MAX_CHECK = 60; + + /** + * Default threshold for eclipse detection. + */ + private static final double CONICAL_ECLIPSE_THRESHOLD = 1e-7; + + /** Occulted body radius. */ + private final double occultedBodyRadius; + + /** Cached date. */ + private AbsoluteDate lastDate; + + /** Cached frame. */ + private Frame propagationFrame; + + /** Cached position. */ + private Vector3D lastPosition; + + /** + * Constructor. + * @param kRef reference flux + * @param occultedBodyRadius radius of occulted body (light source) + * @param occultedBody position provider for light source + * @param occultingBodyRadius radius of central, occulting body + * @param eventDetectionSettings user-defined detection settings for eclipses (if ill-tuned, events might be missed or performance might drop) + */ + public ConicallyShadowedLightFluxModel(final double kRef, final double occultedBodyRadius, + final ExtendedPositionProvider occultedBody, + final double occultingBodyRadius, final EventDetectionSettings eventDetectionSettings) { + super(kRef, occultedBody, occultingBodyRadius, eventDetectionSettings); + this.occultedBodyRadius = occultedBodyRadius; + } + + /** + * Constructor with default event detection settings. + * @param kRef reference flux + * @param occultedBodyRadius radius of occulted body (light source) + * @param occultedBody position provider for light source + * @param occultingBodyRadius radius of central, occulting body + */ + public ConicallyShadowedLightFluxModel(final double kRef, final double occultedBodyRadius, + final ExtendedPositionProvider occultedBody, final double occultingBodyRadius) { + this(kRef, occultedBodyRadius, occultedBody, occultingBodyRadius, getDefaultEclipseDetectionSettings()); + } + + /** + * Constructor with default value for reference flux. + * @param occultedBodyRadius radius of occulted body (light source) + * @param occultedBody position provider for light source + * @param occultingBodyRadius radius of central, occulting body + */ + public ConicallyShadowedLightFluxModel(final double occultedBodyRadius, final ExtendedPositionProvider occultedBody, + final double occultingBodyRadius) { + super(occultedBody, occultingBodyRadius, getDefaultEclipseDetectionSettings()); + this.occultedBodyRadius = occultedBodyRadius; + } + + /** + * Define default detection settings for eclipses. + * @return default settings + */ + public static EventDetectionSettings getDefaultEclipseDetectionSettings() { + return new EventDetectionSettings(CONICAL_ECLIPSE_MAX_CHECK, CONICAL_ECLIPSE_THRESHOLD, + EventDetectionSettings.DEFAULT_MAX_ITER); + } + + /** {@inheritDoc} */ + @Override + public void init(final SpacecraftState initialState, final AbsoluteDate targetDate) { + super.init(initialState, targetDate); + lastDate = initialState.getDate(); + propagationFrame = initialState.getFrame(); + lastPosition = getOccultedBodyPosition(lastDate, propagationFrame); + } + + /** {@inheritDoc} */ + @Override + public > void init(final FieldSpacecraftState initialState, + final FieldAbsoluteDate targetDate) { + super.init(initialState, targetDate); + lastDate = initialState.getDate().toAbsoluteDate(); + propagationFrame = initialState.getFrame(); + lastPosition = getOccultedBodyPosition(initialState.getDate(), propagationFrame).toVector3D(); + } + + /** + * Get occulted body position using cache. + * @param date date + * @return occulted body position + */ + private Vector3D getOccultedBodyPosition(final AbsoluteDate date) { + if (!lastDate.isEqualTo(date)) { + lastPosition = getOccultedBodyPosition(date, propagationFrame); + lastDate = date; + } + return lastPosition; + } + + /** + * Get occulted body position using cache (non-Field and no derivatives case). + * @param fieldDate date + * @param field type + * @return occulted body position + */ + private > FieldVector3D getOccultedBodyPosition(final FieldAbsoluteDate fieldDate) { + if (fieldDate.hasZeroField()) { + final AbsoluteDate date = fieldDate.toAbsoluteDate(); + if (!lastDate.isEqualTo(date)) { + lastPosition = getOccultedBodyPosition(date, propagationFrame); + lastDate = date; + } + return new FieldVector3D<>(fieldDate.getField(), lastPosition); + } else { + return getOccultedBodyPosition(fieldDate, propagationFrame); + } + } + + /** {@inheritDoc} */ + @Override + protected double getLightingRatio(final Vector3D position, final Vector3D occultedBodyPosition) { + final double distanceSun = occultedBodyPosition.getNorm(); + final double squaredDistance = position.getNormSq(); + final Vector3D occultedBodyDirection = occultedBodyPosition.normalize(); + final double s0 = -position.dotProduct(occultedBodyDirection); + if (s0 > 0.0) { + final double l = FastMath.sqrt(squaredDistance - s0 * s0); + final double sinf2 = (occultedBodyRadius - getOccultingBodyRadius()) / distanceSun; + final double l2 = (s0 * sinf2 - getOccultingBodyRadius()) / FastMath.sqrt(1.0 - sinf2 * sinf2); + if (FastMath.abs(l2) - l >= 0.0) { // umbra + return 0.; + } + final double sinf1 = (occultedBodyRadius + getOccultingBodyRadius()) / distanceSun; + final double l1 = (s0 * sinf1 + getOccultingBodyRadius()) / FastMath.sqrt(1.0 - sinf1 * sinf1); + if (l1 - l > 0.0) { // penumbra + final Vector3D relativePosition = occultedBodyPosition.subtract(position); + final double relativeDistance = relativePosition.getNorm(); + final double a = FastMath.asin(occultedBodyRadius / relativeDistance); + final double a2 = a * a; + final double r = FastMath.sqrt(squaredDistance); + final double b = FastMath.asin(getOccultingBodyRadius() / r); + final double c = FastMath.acos(-(relativePosition.dotProduct(position)) / (r * relativeDistance)); + final double x = (c * c + a2 - b * b) / (2 * c); + final double y = FastMath.sqrt(FastMath.max(0., a2 - x * x)); + final double arcCosXOverA = FastMath.acos(FastMath.max(-1, x / a)); + final double intermediate = (arcCosXOverA + (b * b * FastMath.acos((c - x) / b) - c * y) / a2) / FastMath.PI; + return 1. - intermediate; + } + } + return 1.; + } + + /** {@inheritDoc} */ + @Override + protected > T getLightingRatio(final FieldVector3D position, + final FieldVector3D occultedBodyPosition) { + final Field field = position.getX().getField(); + final T distanceSun = occultedBodyPosition.getNorm(); + final T squaredDistance = position.getNormSq(); + final FieldVector3D occultedBodyDirection = occultedBodyPosition.normalize(); + final T s0 = position.dotProduct(occultedBodyDirection).negate(); + if (s0.getReal() > 0.0) { + final T reciprocalDistanceSun = distanceSun.reciprocal(); + final T sinf2 = reciprocalDistanceSun.multiply(occultedBodyRadius - getOccultingBodyRadius()); + final T l2 = (s0.multiply(sinf2).subtract(getOccultingBodyRadius())).divide(FastMath.sqrt(sinf2.square().negate().add(1))); + final T l = FastMath.sqrt(squaredDistance.subtract(s0.square())); + if (FastMath.abs(l2).subtract(l).getReal() >= 0.0) { // umbra + return field.getZero(); + } + final T sinf1 = reciprocalDistanceSun.multiply(occultedBodyRadius + getOccultingBodyRadius()); + final T l1 = (s0.multiply(sinf1).add(getOccultingBodyRadius())).divide(FastMath.sqrt(sinf1.square().negate().add(1))); + if (l1.subtract(l).getReal() > 0.0) { // penumbra + final FieldVector3D relativePosition = occultedBodyPosition.subtract(position); + final T relativeDistance = relativePosition.getNorm(); + final T a = FastMath.asin(relativeDistance.reciprocal().multiply(occultedBodyRadius)); + final T a2 = a.square(); + final T r = FastMath.sqrt(squaredDistance); + final T b = FastMath.asin(r.reciprocal().multiply(getOccultingBodyRadius())); + final T b2 = b.square(); + final T c = FastMath.acos((relativePosition.dotProduct(position).negate()).divide(r.multiply(relativeDistance))); + final T x = (c.square().add(a2).subtract(b2)).divide(c.multiply(2)); + final T x2 = x.square(); + final T y = (a2.getReal() - x2.getReal() <= 0) ? s0.getField().getZero() : FastMath.sqrt(a2.subtract(x2)); + final T arcCosXOverA = (x.getReal() / a.getReal() < -1) ? s0.getPi().negate() : FastMath.acos(x.divide(a)); + final T intermediate = arcCosXOverA.add(((b2.multiply(FastMath.acos((c.subtract(x)).divide(b)))) + .subtract(c.multiply(y))).divide(a2)); + return intermediate.divide(-FastMath.PI).add(1); + } + } + return field.getOne(); + } + + /** {@inheritDoc} */ + @Override + public List getEclipseConditionsDetector() { + final List detectors = new ArrayList<>(); + detectors.add(createUmbraEclipseDetector()); + detectors.add(createPenumbraEclipseDetector()); + return detectors; + } + + /** + * Method to create a new umbra detector. + * @return detector + */ + private InternalEclipseDetector createUmbraEclipseDetector() { + return new InternalEclipseDetector() { + @Override + public double g(final SpacecraftState s) { + final Vector3D position = s.getPosition(); + final Vector3D occultedBodyPosition = getOccultedBodyPosition(s.getDate()); + final Vector3D occultedBodyDirection = occultedBodyPosition.normalize(); + final double s0 = -position.dotProduct(occultedBodyDirection); + final double distanceSun = occultedBodyPosition.getNorm(); + final double squaredDistance = position.getNormSq(); + final double sinf2 = (occultedBodyRadius - getOccultingBodyRadius()) / distanceSun; + final double l = FastMath.sqrt(squaredDistance - s0 * s0); + final double l2 = (s0 * sinf2 - getOccultingBodyRadius()) / FastMath.sqrt(1.0 - sinf2 * sinf2); + return FastMath.abs(l2) / l - 1.; + } + }; + } + + /** + * Method to create a new penumbra detector. + * @return detector + */ + private InternalEclipseDetector createPenumbraEclipseDetector() { + return new InternalEclipseDetector() { + @Override + public double g(final SpacecraftState s) { + final Vector3D position = s.getPosition(); + final Vector3D occultedBodyPosition = getOccultedBodyPosition(s.getDate()); + final Vector3D occultedBodyDirection = occultedBodyPosition.normalize(); + final double s0 = -position.dotProduct(occultedBodyDirection); + final double distanceSun = occultedBodyPosition.getNorm(); + final double squaredDistance = position.getNormSq(); + final double l = FastMath.sqrt(squaredDistance - s0 * s0); + final double sinf1 = (occultedBodyRadius + getOccultingBodyRadius()) / distanceSun; + final double l1 = (s0 * sinf1 + getOccultingBodyRadius()) / FastMath.sqrt(1.0 - sinf1 * sinf1); + return l1 / l - 1.; + } + }; + } + + /** + * Internal class for event detector. + */ + private abstract class InternalEclipseDetector implements EventDetector { + /** Event handler. */ + private final ResetDerivativesOnEvent handler; + + /** + * Constructor. + */ + InternalEclipseDetector() { + this.handler = new ResetDerivativesOnEvent(); + } + + @Override + public EventDetectionSettings getDetectionSettings() { + return getEventDetectionSettings(); + } + + @Override + public double getThreshold() { + return getDetectionSettings().getThreshold(); + } + + @Override + public AdaptableInterval getMaxCheckInterval() { + return getDetectionSettings().getMaxCheckInterval(); + } + + @Override + public int getMaxIterationCount() { + return getDetectionSettings().getMaxIterationCount(); + } + + @Override + public EventHandler getHandler() { + return handler; + } + } + + /** {@inheritDoc} */ + @Override + public > List> getFieldEclipseConditionsDetector(final Field field) { + final List> detectors = new ArrayList<>(); + final FieldEventDetectionSettings detectionSettings = new FieldEventDetectionSettings<>(field, + getEventDetectionSettings()); + detectors.add(createFieldUmbraEclipseDetector(detectionSettings)); + detectors.add(createFieldPenumbraEclipseDetector(detectionSettings)); + return detectors; + } + + /** + * Method to create a new umbra detector. Field version. + * @param detectionSettings non-Field detection settings + * @param field type + * @return detector + */ + private > FieldInternalEclipseDetector createFieldUmbraEclipseDetector(final FieldEventDetectionSettings detectionSettings) { + return new FieldInternalEclipseDetector(detectionSettings) { + @Override + public T g(final FieldSpacecraftState s) { + final FieldVector3D position = s.getPosition(); + final FieldVector3D occultedBodyPosition = getOccultedBodyPosition(s.getDate()); + final FieldVector3D occultedBodyDirection = occultedBodyPosition.normalize(); + final T s0 = position.dotProduct(occultedBodyDirection).negate(); + final T distanceSun = occultedBodyPosition.getNorm(); + final T squaredDistance = position.getNormSq(); + final T reciprocalDistanceSun = distanceSun.reciprocal(); + final T sinf1 = reciprocalDistanceSun.multiply(occultedBodyRadius + getOccultingBodyRadius()); + final T l1 = (s0.multiply(sinf1).add(getOccultingBodyRadius())).divide(FastMath.sqrt(sinf1.square().negate().add(1))); + final T l = FastMath.sqrt(squaredDistance.subtract(s0.square())); + return l1.divide(l).subtract(1); + } + }; + } + + /** + * Method to create a new penumbra detector. Field version. + * @param detectionSettings non-Field detection settings + * @param field type + * @return detector + */ + private > FieldInternalEclipseDetector createFieldPenumbraEclipseDetector(final FieldEventDetectionSettings detectionSettings) { + return new FieldInternalEclipseDetector(detectionSettings) { + @Override + public T g(final FieldSpacecraftState s) { + final FieldVector3D position = s.getPosition(); + final FieldVector3D occultedBodyPosition = getOccultedBodyPosition(s.getDate()); + final FieldVector3D occultedBodyDirection = occultedBodyPosition.normalize(); + final T s0 = position.dotProduct(occultedBodyDirection).negate(); + final T distanceSun = occultedBodyPosition.getNorm(); + final T squaredDistance = position.getNormSq(); + final T reciprocalDistanceSun = distanceSun.reciprocal(); + final T sinf2 = reciprocalDistanceSun.multiply(occultedBodyRadius - getOccultingBodyRadius()); + final T l2 = (s0.multiply(sinf2).subtract(getOccultingBodyRadius())).divide(FastMath.sqrt(sinf2.square().negate().add(1))); + final T l = FastMath.sqrt(squaredDistance.subtract(s0.square())); + return FastMath.abs(l2).divide(l).subtract(1); + } + }; + } + + /** + * Internal class for event detector. + */ + private abstract static class FieldInternalEclipseDetector> implements FieldEventDetector { + /** Event handler. */ + private final FieldResetDerivativesOnEvent handler; + + /** Detection settings. */ + private final FieldEventDetectionSettings fieldEventDetectionSettings; + + /** + * Constructor. + * @param fieldEventDetectionSettings detection settings + */ + FieldInternalEclipseDetector(final FieldEventDetectionSettings fieldEventDetectionSettings) { + this.handler = new FieldResetDerivativesOnEvent<>(); + this.fieldEventDetectionSettings = fieldEventDetectionSettings; + } + + @Override + public FieldEventDetectionSettings getDetectionSettings() { + return fieldEventDetectionSettings; + } + + @Override + public T getThreshold() { + return getDetectionSettings().getThreshold(); + } + + @Override + public FieldAdaptableInterval getMaxCheckInterval() { + return getDetectionSettings().getMaxCheckInterval(); + } + + @Override + public int getMaxIterationCount() { + return getDetectionSettings().getMaxIterationCount(); + } + + @Override + public FieldEventHandler getHandler() { + return handler; + } + } +} diff --git a/src/main/java/org/orekit/forces/radiation/CylindricallyShadowedLightFluxModel.java b/src/main/java/org/orekit/forces/radiation/CylindricallyShadowedLightFluxModel.java index a4a6a93f54..54683004cb 100644 --- a/src/main/java/org/orekit/forces/radiation/CylindricallyShadowedLightFluxModel.java +++ b/src/main/java/org/orekit/forces/radiation/CylindricallyShadowedLightFluxModel.java @@ -20,12 +20,14 @@ import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.hipparchus.ode.events.Action; -import org.hipparchus.util.FastMath; import org.orekit.propagation.events.CylindricalShadowEclipseDetector; import org.orekit.propagation.events.EventDetector; +import org.orekit.propagation.events.EventDetectionSettings; import org.orekit.propagation.events.FieldCylindricalShadowEclipseDetector; import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.propagation.events.FieldEventDetectionSettings; +import org.orekit.propagation.events.handlers.FieldResetDerivativesOnEvent; +import org.orekit.propagation.events.handlers.ResetDerivativesOnEvent; import org.orekit.utils.ExtendedPositionProvider; import java.util.ArrayList; @@ -37,40 +39,44 @@ * model. * * @author Romain Serra - * @see AbstractLightFluxModel + * @see AbstractSolarLightFluxModel * @see LightFluxModel * @since 12.1 */ -public class CylindricallyShadowedLightFluxModel extends AbstractLightFluxModel { +public class CylindricallyShadowedLightFluxModel extends AbstractSolarLightFluxModel { /** - * Max. check interval for eclipse detection. + * Default max. check interval for eclipse detection. */ private static final double CYLINDRICAL_ECLIPSE_MAX_CHECK = 100; /** - * Threshold for eclipse detection. + * Default threshold for eclipse detection. */ private static final double CYLINDRICAL_ECLIPSE_THRESHOLD = 1e-7; - /** Radius of central, occulting body (approximated as spherical). - * Its center is assumed to be at the origin of the frame linked to the state. */ - private final double occultingBodyRadius; - - /** Reference flux normalized for a 1m distance (N). */ - private final double kRef; - /** * Constructor. * @param kRef reference flux * @param occultedBody position provider for light source * @param occultingBodyRadius radius of central, occulting body + * @param eventDetectionSettings user-defined detection settings for eclipses (if ill-tuned, events might be missed or performance might drop) + * @since 12.2 + */ + public CylindricallyShadowedLightFluxModel(final double kRef, final ExtendedPositionProvider occultedBody, + final double occultingBodyRadius, final EventDetectionSettings eventDetectionSettings) { + super(kRef, occultedBody, occultingBodyRadius, eventDetectionSettings); + } + + /** + * Constructor with default event detection settings. + * @param kRef reference flux + * @param occultedBody position provider for light source + * @param occultingBodyRadius radius of central, occulting body */ public CylindricallyShadowedLightFluxModel(final double kRef, final ExtendedPositionProvider occultedBody, final double occultingBodyRadius) { - super(occultedBody); - this.kRef = kRef; - this.occultingBodyRadius = occultingBodyRadius; + this(kRef, occultedBody, occultingBodyRadius, getDefaultEclipseDetectionSettings()); } /** @@ -80,31 +86,17 @@ public CylindricallyShadowedLightFluxModel(final double kRef, final ExtendedPosi */ public CylindricallyShadowedLightFluxModel(final ExtendedPositionProvider occultedBody, final double occultingBodyRadius) { - this(4.56e-6 * FastMath.pow(149597870000.0, 2), occultedBody, occultingBodyRadius); + super(occultedBody, occultingBodyRadius, getDefaultEclipseDetectionSettings()); } /** - * Getter for occulting body radius. - * @return radius + * Define default detection settings for eclipses. + * @return default settings + * @since 12.2 */ - public double getOccultingBodyRadius() { - return occultingBodyRadius; - } - - /** {@inheritDoc} */ - @Override - protected Vector3D getUnoccultedFluxVector(final Vector3D relativePosition) { - final double squaredRadius = relativePosition.getNormSq(); - final double factor = kRef / (squaredRadius * FastMath.sqrt(squaredRadius)); - return relativePosition.scalarMultiply(factor); - } - - /** {@inheritDoc} */ - @Override - protected > FieldVector3D getUnoccultedFluxVector(final FieldVector3D relativePosition) { - final T squaredRadius = relativePosition.getNormSq(); - final T factor = (squaredRadius.multiply(squaredRadius.sqrt())).reciprocal().multiply(kRef); - return relativePosition.scalarMultiply(factor); + public static EventDetectionSettings getDefaultEclipseDetectionSettings() { + return new EventDetectionSettings(CYLINDRICAL_ECLIPSE_MAX_CHECK, CYLINDRICAL_ECLIPSE_THRESHOLD, + EventDetectionSettings.DEFAULT_MAX_ITER); } /** {@inheritDoc} */ @@ -114,7 +106,7 @@ protected double getLightingRatio(final Vector3D position, final Vector3D occult final double dotProduct = position.dotProduct(occultedBodyDirection); if (dotProduct < 0.) { final double distanceToCylinderAxis = (position.subtract(occultedBodyDirection.scalarMultiply(dotProduct))).getNorm(); - if (distanceToCylinderAxis <= occultingBodyRadius) { + if (distanceToCylinderAxis <= getOccultingBodyRadius()) { return 0.; } } @@ -130,7 +122,7 @@ protected > T getLightingRatio(final FieldVect final T dotProduct = position.dotProduct(occultedBodyDirection); if (dotProduct.getReal() < 0.) { final T distanceToCylinderAxis = (position.subtract(occultedBodyDirection.scalarMultiply(dotProduct))).getNorm(); - if (distanceToCylinderAxis.getReal() <= occultingBodyRadius) { + if (distanceToCylinderAxis.getReal() <= getOccultingBodyRadius()) { return field.getZero(); } } @@ -142,8 +134,7 @@ protected > T getLightingRatio(final FieldVect @Override public List getEclipseConditionsDetector() { final List detectors = new ArrayList<>(); - detectors.add(createCylindricalShadowEclipseDetector() - .withThreshold(CYLINDRICAL_ECLIPSE_THRESHOLD).withMaxCheck(CYLINDRICAL_ECLIPSE_MAX_CHECK)); + detectors.add(createCylindricalShadowEclipseDetector().withDetectionSettings(getEventDetectionSettings())); return detectors; } @@ -153,16 +144,15 @@ public List getEclipseConditionsDetector() { */ private CylindricalShadowEclipseDetector createCylindricalShadowEclipseDetector() { return new CylindricalShadowEclipseDetector(getOccultedBody(), getOccultingBodyRadius(), - (state, detector, increasing) -> Action.RESET_DERIVATIVES); + new ResetDerivativesOnEvent()); } /** {@inheritDoc} */ @Override public > List> getFieldEclipseConditionsDetector(final Field field) { final List> detectors = new ArrayList<>(); - final T threshold = field.getZero().newInstance(CYLINDRICAL_ECLIPSE_THRESHOLD); - detectors.add(createFieldCylindricalShadowEclipseDetector(field) - .withThreshold(threshold).withMaxCheck(CYLINDRICAL_ECLIPSE_MAX_CHECK)); + final FieldEventDetectionSettings detectionSettings = new FieldEventDetectionSettings<>(field, getEventDetectionSettings()); + detectors.add(createFieldCylindricalShadowEclipseDetector(field).withDetectionSettings(detectionSettings)); return detectors; } @@ -175,6 +165,6 @@ public > List> getFieldE private > FieldCylindricalShadowEclipseDetector createFieldCylindricalShadowEclipseDetector(final Field field) { final T occultingBodyRadiusAsField = field.getZero().newInstance(getOccultingBodyRadius()); return new FieldCylindricalShadowEclipseDetector<>(getOccultedBody(), occultingBodyRadiusAsField, - (state, detector, increasing) -> Action.RESET_DERIVATIVES); + new FieldResetDerivativesOnEvent<>()); } } diff --git a/src/main/java/org/orekit/forces/radiation/LightFluxModel.java b/src/main/java/org/orekit/forces/radiation/LightFluxModel.java index 2e3b30e257..c64029e8c5 100644 --- a/src/main/java/org/orekit/forces/radiation/LightFluxModel.java +++ b/src/main/java/org/orekit/forces/radiation/LightFluxModel.java @@ -24,6 +24,8 @@ import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.events.EventDetector; import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; import java.util.List; @@ -36,6 +38,28 @@ */ public interface LightFluxModel { + /** + * Perform initialization steps before starting propagation. + * @param initialState initial state + * @param targetDate target date for propagation + * @since 12.2 + */ + default void init(SpacecraftState initialState, final AbsoluteDate targetDate) { + // nothing by default + } + + /** + * Perform initialization steps before starting propagation. + * @param initialState initial state + * @param targetDate target date for propagation + * @param field type + * @since 12.2 + */ + default > void init(FieldSpacecraftState initialState, + final FieldAbsoluteDate targetDate) { + // nothing by default + } + /** * Get the light flux vector in the state's frame. * @param state state diff --git a/src/main/java/org/orekit/forces/radiation/RadiationPressureModel.java b/src/main/java/org/orekit/forces/radiation/RadiationPressureModel.java index f07f704fc3..176fccb93e 100644 --- a/src/main/java/org/orekit/forces/radiation/RadiationPressureModel.java +++ b/src/main/java/org/orekit/forces/radiation/RadiationPressureModel.java @@ -24,6 +24,8 @@ import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.events.EventDetector; import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.ParameterDriver; import java.util.List; @@ -87,6 +89,21 @@ public boolean dependsOnPositionOnly() { return radiationSensitive instanceof IsotropicRadiationClassicalConvention || radiationSensitive instanceof IsotropicRadiationCNES95Convention || radiationSensitive instanceof IsotropicRadiationSingleCoefficient; } + /** {@inheritDoc} */ + @Override + public void init(final SpacecraftState initialState, final AbsoluteDate target) { + RadiationForceModel.super.init(initialState, target); + lightFluxModel.init(initialState, target); + } + + /** {@inheritDoc} */ + @Override + public > void init(final FieldSpacecraftState initialState, + final FieldAbsoluteDate target) { + RadiationForceModel.super.init(initialState, target); + lightFluxModel.init(initialState, target); + } + /** {@inheritDoc} */ @Override public Vector3D acceleration(final SpacecraftState s, final double[] parameters) { diff --git a/src/main/java/org/orekit/forces/radiation/SolarRadiationPressure.java b/src/main/java/org/orekit/forces/radiation/SolarRadiationPressure.java index 58b55a68cf..255b8c6d30 100644 --- a/src/main/java/org/orekit/forces/radiation/SolarRadiationPressure.java +++ b/src/main/java/org/orekit/forces/radiation/SolarRadiationPressure.java @@ -16,6 +16,9 @@ */ package org.orekit.forces.radiation; +import java.lang.reflect.Array; +import java.util.List; + import org.hipparchus.CalculusFieldElement; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; @@ -33,9 +36,6 @@ import org.orekit.utils.OccultationEngine; import org.orekit.utils.ParameterDriver; -import java.lang.reflect.Array; -import java.util.List; - /** Solar radiation pressure force model. *

* Since Orekit 11.0, it is possible to take into account @@ -333,7 +333,7 @@ private > T getLightingRatio(final FieldSpacec @SuppressWarnings("unchecked") final OccultationEngine.FieldOccultationAngles[] angles = - (OccultationEngine.FieldOccultationAngles[]) Array.newInstance(OccultationEngine.FieldOccultationAngles.class, n); + (OccultationEngine.FieldOccultationAngles[]) Array.newInstance(OccultationEngine.FieldOccultationAngles.class, n); for (int i = 0; i < n; ++i) { angles[i] = occultingBodies.get(i).angles(state); } diff --git a/src/main/java/org/orekit/frames/FieldKinematicTransform.java b/src/main/java/org/orekit/frames/FieldKinematicTransform.java index c733fccb8e..50048fa4fa 100644 --- a/src/main/java/org/orekit/frames/FieldKinematicTransform.java +++ b/src/main/java/org/orekit/frames/FieldKinematicTransform.java @@ -20,6 +20,7 @@ import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.util.MathArrays; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.FieldPVCoordinates; @@ -27,6 +28,8 @@ import org.orekit.utils.TimeStampedFieldPVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; +import java.util.Arrays; + /** * A transform that only includes translation and rotation as well as their respective rates. * It is kinematic in the sense that it cannot transform an acceleration vector. @@ -116,6 +119,63 @@ default TimeStampedFieldPVCoordinates transformOnlyPV(final TimeStampedFieldP FieldVector3D.getZero(pv.getDate().getField())); } + /** Compute the Jacobian of the {@link #transformOnlyPV(FieldPVCoordinates)} (FieldPVCoordinates)} + * method of the transform. + *

+ * Element {@code jacobian[i][j]} is the derivative of Cartesian coordinate i + * of the transformed {@link FieldPVCoordinates} with respect to Cartesian coordinate j + * of the input {@link FieldPVCoordinates} in method {@link #transformOnlyPV(FieldPVCoordinates)}. + *

+ *

+ * This definition implies that if we define position-velocity coordinates + *

+     * PV₁ = transform.transformPVCoordinates(PV₀), then
+     * 
+ *

their differentials dPV₁ and dPV₀ will obey the following relation + * where J is the matrix computed by this method: + *

+     * dPV₁ = J × dPV₀
+     * 
+ * + * @return Jacobian matrix + */ + default T[][] getPVJacobian() { + final Field field = getFieldDate().getField(); + final T zero = field.getZero(); + final T[][] jacobian = MathArrays.buildArray(field, 6, 6); + + // elementary matrix for rotation + final T[][] mData = getRotation().getMatrix(); + + // dP1/dP0 + System.arraycopy(mData[0], 0, jacobian[0], 0, 3); + System.arraycopy(mData[1], 0, jacobian[1], 0, 3); + System.arraycopy(mData[2], 0, jacobian[2], 0, 3); + + // dP1/dV0 + Arrays.fill(jacobian[0], 3, 6, zero); + Arrays.fill(jacobian[1], 3, 6, zero); + Arrays.fill(jacobian[2], 3, 6, zero); + + // dV1/dP0 + final FieldVector3D o = getRotationRate(); + final T ox = o.getX(); + final T oy = o.getY(); + final T oz = o.getZ(); + for (int i = 0; i < 3; ++i) { + jacobian[3][i] = oz.multiply(mData[1][i]).subtract(oy.multiply(mData[2][i])); + jacobian[4][i] = ox.multiply(mData[2][i]).subtract(oz.multiply(mData[0][i])); + jacobian[5][i] = oy.multiply(mData[0][i]).subtract(ox.multiply(mData[1][i])); + } + + // dV1/dV0 + System.arraycopy(mData[0], 0, jacobian[3], 3, 3); + System.arraycopy(mData[1], 0, jacobian[4], 3, 3); + System.arraycopy(mData[2], 0, jacobian[5], 3, 3); + + return jacobian; + } + /** Get the first time derivative of the translation. * @return first time derivative of the translation * @see #getTranslation() diff --git a/src/main/java/org/orekit/frames/FieldStaticTransform.java b/src/main/java/org/orekit/frames/FieldStaticTransform.java index a2d7cb8cb8..e21ee09481 100644 --- a/src/main/java/org/orekit/frames/FieldStaticTransform.java +++ b/src/main/java/org/orekit/frames/FieldStaticTransform.java @@ -41,13 +41,59 @@ public interface FieldStaticTransform> extends /** * Get the identity static transform. + * Override methods for speed. * * @param type of the elements * @param field field used by default * @return identity transform. */ static > FieldStaticTransform getIdentity(final Field field) { - return FieldTransform.getIdentity(field); + return new FieldStaticTransform() { + @Override + public FieldVector3D getTranslation() { + return FieldVector3D.getZero(field); + } + + @Override + public FieldRotation getRotation() { + return FieldRotation.getIdentity(field); + } + + @Override + public FieldStaticTransform getStaticInverse() { + return getInverse(); + } + + @Override + public FieldStaticTransform getInverse() { + return this; + } + + @Override + public AbsoluteDate getDate() { + return AbsoluteDate.ARBITRARY_EPOCH; + } + + @Override + public FieldVector3D transformVector(final FieldVector3D vector) { + return new FieldVector3D<>(vector.getX(), vector.getY(), vector.getZ()); + } + + @Override + public FieldVector3D transformVector(final Vector3D vector) { + return new FieldVector3D<>(field, vector); + } + + @Override + public FieldVector3D transformPosition(final FieldVector3D position) { + return transformVector(position); + } + + @Override + public FieldVector3D transformPosition(final Vector3D position) { + return transformVector(position); + } + }; } /** diff --git a/src/main/java/org/orekit/frames/FieldTransform.java b/src/main/java/org/orekit/frames/FieldTransform.java index 35719499d8..ddcfefba10 100644 --- a/src/main/java/org/orekit/frames/FieldTransform.java +++ b/src/main/java/org/orekit/frames/FieldTransform.java @@ -27,6 +27,7 @@ import org.hipparchus.geometry.euclidean.threed.FieldLine; import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.FieldTimeInterpolator; @@ -717,74 +718,64 @@ public void getJacobian(final CartesianDerivativesFilter selector, final T[][] j final T zero = date.getField().getZero(); - // elementary matrix for rotation - final T[][] mData = angular.getRotation().getMatrix(); + if (selector.getMaxOrder() == 0) { + // elementary matrix for rotation + final T[][] mData = angular.getRotation().getMatrix(); - // dP1/dP0 - System.arraycopy(mData[0], 0, jacobian[0], 0, 3); - System.arraycopy(mData[1], 0, jacobian[1], 0, 3); - System.arraycopy(mData[2], 0, jacobian[2], 0, 3); + // dP1/dP0 + System.arraycopy(mData[0], 0, jacobian[0], 0, 3); + System.arraycopy(mData[1], 0, jacobian[1], 0, 3); + System.arraycopy(mData[2], 0, jacobian[2], 0, 3); + } + + else if (selector.getMaxOrder() == 1) { + // use KinematicTransform Jacobian + final T[][] mData = getPVJacobian(); + for (int i = 0; i < mData.length; i++) { + System.arraycopy(mData[i], 0, jacobian[i], 0, mData[i].length); + } + } + + else if (selector.getMaxOrder() >= 2) { + getJacobian(CartesianDerivativesFilter.USE_PV, jacobian); - if (selector.getMaxOrder() >= 1) { + // dP1/dA0 + Arrays.fill(jacobian[0], 6, 9, zero); + Arrays.fill(jacobian[1], 6, 9, zero); + Arrays.fill(jacobian[2], 6, 9, zero); - // dP1/dV0 - Arrays.fill(jacobian[0], 3, 6, zero); - Arrays.fill(jacobian[1], 3, 6, zero); - Arrays.fill(jacobian[2], 3, 6, zero); + // dV1/dA0 + Arrays.fill(jacobian[3], 6, 9, zero); + Arrays.fill(jacobian[4], 6, 9, zero); + Arrays.fill(jacobian[5], 6, 9, zero); - // dV1/dP0 + // dA1/dP0 final FieldVector3D o = angular.getRotationRate(); final T ox = o.getX(); final T oy = o.getY(); final T oz = o.getZ(); + final FieldVector3D oDot = angular.getRotationAcceleration(); + final T oDotx = oDot.getX(); + final T oDoty = oDot.getY(); + final T oDotz = oDot.getZ(); for (int i = 0; i < 3; ++i) { - jacobian[3][i] = oz.multiply(mData[1][i]).subtract(oy.multiply(mData[2][i])); - jacobian[4][i] = ox.multiply(mData[2][i]).subtract(oz.multiply(mData[0][i])); - jacobian[5][i] = oy.multiply(mData[0][i]).subtract(ox.multiply(mData[1][i])); + jacobian[6][i] = oDotz.multiply(jacobian[1][i]).subtract(oDoty.multiply(jacobian[2][i])).add(oz.multiply(jacobian[4][i]).subtract(oy.multiply(jacobian[5][i]))); + jacobian[7][i] = oDotx.multiply(jacobian[2][i]).subtract(oDotz.multiply(jacobian[0][i])).add(ox.multiply(jacobian[5][i]).subtract(oz.multiply(jacobian[3][i]))); + jacobian[8][i] = oDoty.multiply(jacobian[0][i]).subtract(oDotx.multiply(jacobian[1][i])).add(oy.multiply(jacobian[3][i]).subtract(ox.multiply(jacobian[4][i]))); } - // dV1/dV0 - System.arraycopy(mData[0], 0, jacobian[3], 3, 3); - System.arraycopy(mData[1], 0, jacobian[4], 3, 3); - System.arraycopy(mData[2], 0, jacobian[5], 3, 3); - - if (selector.getMaxOrder() >= 2) { - - // dP1/dA0 - Arrays.fill(jacobian[0], 6, 9, zero); - Arrays.fill(jacobian[1], 6, 9, zero); - Arrays.fill(jacobian[2], 6, 9, zero); - - // dV1/dA0 - Arrays.fill(jacobian[3], 6, 9, zero); - Arrays.fill(jacobian[4], 6, 9, zero); - Arrays.fill(jacobian[5], 6, 9, zero); - - // dA1/dP0 - final FieldVector3D oDot = angular.getRotationAcceleration(); - final T oDotx = oDot.getX(); - final T oDoty = oDot.getY(); - final T oDotz = oDot.getZ(); - for (int i = 0; i < 3; ++i) { - jacobian[6][i] = oDotz.multiply(mData[1][i]).subtract(oDoty.multiply(mData[2][i])).add(oz.multiply(jacobian[4][i]).subtract(oy.multiply(jacobian[5][i]))); - jacobian[7][i] = oDotx.multiply(mData[2][i]).subtract(oDotz.multiply(mData[0][i])).add(ox.multiply(jacobian[5][i]).subtract(oz.multiply(jacobian[3][i]))); - jacobian[8][i] = oDoty.multiply(mData[0][i]).subtract(oDotx.multiply(mData[1][i])).add(oy.multiply(jacobian[3][i]).subtract(ox.multiply(jacobian[4][i]))); - } - - // dA1/dV0 - for (int i = 0; i < 3; ++i) { - jacobian[6][i + 3] = oz.multiply(mData[1][i]).subtract(oy.multiply(mData[2][i])).multiply(2); - jacobian[7][i + 3] = ox.multiply(mData[2][i]).subtract(oz.multiply(mData[0][i])).multiply(2); - jacobian[8][i + 3] = oy.multiply(mData[0][i]).subtract(ox.multiply(mData[1][i])).multiply(2); - } - - // dA1/dA0 - System.arraycopy(mData[0], 0, jacobian[6], 6, 3); - System.arraycopy(mData[1], 0, jacobian[7], 6, 3); - System.arraycopy(mData[2], 0, jacobian[8], 6, 3); - + // dA1/dV0 + for (int i = 0; i < 3; ++i) { + jacobian[6][i + 3] = oz.multiply(jacobian[1][i]).subtract(oy.multiply(jacobian[2][i])).multiply(2); + jacobian[7][i + 3] = ox.multiply(jacobian[2][i]).subtract(oz.multiply(jacobian[0][i])).multiply(2); + jacobian[8][i + 3] = oy.multiply(jacobian[0][i]).subtract(ox.multiply(jacobian[1][i])).multiply(2); } + // dA1/dA0 + System.arraycopy(jacobian[0], 0, jacobian[6], 6, 3); + System.arraycopy(jacobian[1], 0, jacobian[7], 6, 3); + System.arraycopy(jacobian[2], 0, jacobian[8], 6, 3); + } } @@ -884,6 +875,9 @@ public FieldVector3D getRotationAcceleration() { /** Specialized class for identity transform. */ private static class FieldIdentityTransform> extends FieldTransform { + /** Field. */ + private final Field field; + /** Simple constructor. * @param field field for the components */ @@ -892,6 +886,17 @@ private static class FieldIdentityTransform> e FieldAbsoluteDate.getArbitraryEpoch(field).toAbsoluteDate(), FieldPVCoordinates.getZero(field), FieldAngularCoordinates.getIdentity(field)); + this.field = field; + } + + @Override + public FieldStaticTransform staticShiftedBy(final T dt) { + return toStaticTransform(); + } + + @Override + public FieldTransform shiftedBy(final T dt) { + return this; } /** {@inheritDoc} */ @@ -900,24 +905,44 @@ public FieldTransform shiftedBy(final double dt) { return this; } + @Override + public FieldStaticTransform getStaticInverse() { + return toStaticTransform(); + } + /** {@inheritDoc} */ @Override public FieldTransform getInverse() { return this; } + @Override + public FieldStaticTransform toStaticTransform() { + return FieldStaticTransform.getIdentity(field); + } + /** {@inheritDoc} */ @Override public FieldVector3D transformPosition(final FieldVector3D position) { return position; } + @Override + public FieldVector3D transformPosition(final Vector3D position) { + return transformVector(position); + } + /** {@inheritDoc} */ @Override public FieldVector3D transformVector(final FieldVector3D vector) { return vector; } + @Override + public FieldVector3D transformVector(final Vector3D vector) { + return new FieldVector3D<>(field, vector); + } + /** {@inheritDoc} */ @Override public FieldLine transformLine(final FieldLine line) { @@ -930,6 +955,31 @@ public FieldPVCoordinates transformPVCoordinates(final FieldPVCoordinates return pv; } + @Override + public FieldPVCoordinates transformPVCoordinates(final PVCoordinates pv) { + return new FieldPVCoordinates<>(field, pv); + } + + @Override + public TimeStampedFieldPVCoordinates transformPVCoordinates(final TimeStampedPVCoordinates pv) { + return new TimeStampedFieldPVCoordinates<>(field, pv); + } + + @Override + public TimeStampedFieldPVCoordinates transformPVCoordinates(final TimeStampedFieldPVCoordinates pv) { + return pv; + } + + @Override + public FieldPVCoordinates transformOnlyPV(final FieldPVCoordinates pv) { + return new FieldPVCoordinates<>(pv.getPosition(), pv.getVelocity()); + } + + @Override + public TimeStampedFieldPVCoordinates transformOnlyPV(final TimeStampedFieldPVCoordinates pv) { + return new TimeStampedFieldPVCoordinates<>(pv.getDate(), pv.getPosition(), pv.getVelocity(), FieldVector3D.getZero(field)); + } + /** {@inheritDoc} */ @Override public FieldTransform freeze() { diff --git a/src/main/java/org/orekit/frames/FixedTransformProvider.java b/src/main/java/org/orekit/frames/FixedTransformProvider.java index 7b21012657..65fd8a7de5 100644 --- a/src/main/java/org/orekit/frames/FixedTransformProvider.java +++ b/src/main/java/org/orekit/frames/FixedTransformProvider.java @@ -21,8 +21,8 @@ import java.util.HashMap; import java.util.Map; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; @@ -55,16 +55,13 @@ public Transform getTransform(final AbsoluteDate date) { } /** {@inheritDoc} */ + @SuppressWarnings("unchecked") @Override public > FieldTransform getTransform(final FieldAbsoluteDate date) { - - @SuppressWarnings("unchecked") - final FieldTransform ft = - (FieldTransform) cached.computeIfAbsent(date.getField(), - f -> new FieldTransform<>((Field) f, transform)); - - return ft; - + synchronized (cached) { + return (FieldTransform) cached.computeIfAbsent(date.getField(), + f -> new FieldTransform<>((Field) f, transform)); + } } /** Replace the instance with a data transfer object for serialization. diff --git a/src/main/java/org/orekit/frames/KinematicTransform.java b/src/main/java/org/orekit/frames/KinematicTransform.java index c23a1fc4ef..3e415d6d8c 100644 --- a/src/main/java/org/orekit/frames/KinematicTransform.java +++ b/src/main/java/org/orekit/frames/KinematicTransform.java @@ -22,6 +22,8 @@ import org.orekit.utils.PVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; +import java.util.Arrays; + /** * A transform that only includes translation and rotation as well as their respective rates. * It is kinematic in the sense that it cannot transform an acceleration vector. @@ -103,6 +105,61 @@ default TimeStampedPVCoordinates transformOnlyPV(final TimeStampedPVCoordinates return new TimeStampedPVCoordinates(pv.getDate(), transformedP, transformedV); } + /** Compute the Jacobian of the {@link #transformOnlyPV(PVCoordinates)} (PVCoordinates)} + * method of the transform. + *

+ * Element {@code jacobian[i][j]} is the derivative of Cartesian coordinate i + * of the transformed {@link PVCoordinates} with respect to Cartesian coordinate j + * of the input {@link PVCoordinates} in method {@link #transformOnlyPV(PVCoordinates)}. + *

+ *

+ * This definition implies that if we define position-velocity coordinates + *

+     * PV₁ = transform.transformPVCoordinates(PV₀), then
+     * 
+ *

their differentials dPV₁ and dPV₀ will obey the following relation + * where J is the matrix computed by this method: + *

+     * dPV₁ = J × dPV₀
+     * 
+ * + * @return Jacobian matrix + */ + default double[][] getPVJacobian() { + final double[][] jacobian = new double[6][6]; + + // elementary matrix for rotation + final double[][] mData = getRotation().getMatrix(); + + // dP1/dP0 + System.arraycopy(mData[0], 0, jacobian[0], 0, 3); + System.arraycopy(mData[1], 0, jacobian[1], 0, 3); + System.arraycopy(mData[2], 0, jacobian[2], 0, 3); + + // dP1/dV0 + Arrays.fill(jacobian[0], 3, 6, 0.0); + Arrays.fill(jacobian[1], 3, 6, 0.0); + Arrays.fill(jacobian[2], 3, 6, 0.0); + + // dV1/dP0 + final Vector3D o = getRotationRate(); + final double ox = o.getX(); + final double oy = o.getY(); + final double oz = o.getZ(); + for (int i = 0; i < 3; ++i) { + jacobian[3][i] = -(oy * mData[2][i] - oz * mData[1][i]); + jacobian[4][i] = -(oz * mData[0][i] - ox * mData[2][i]); + jacobian[5][i] = -(ox * mData[1][i] - oy * mData[0][i]); + } + + // dV1/dV0 + System.arraycopy(mData[0], 0, jacobian[3], 3, 3); + System.arraycopy(mData[1], 0, jacobian[4], 3, 3); + System.arraycopy(mData[2], 0, jacobian[5], 3, 3); + + return jacobian; + } + /** Get the first time derivative of the translation. * @return first time derivative of the translation * @see #getTranslation() diff --git a/src/main/java/org/orekit/frames/MODProvider.java b/src/main/java/org/orekit/frames/MODProvider.java index 1bb9655169..ef3201bcba 100644 --- a/src/main/java/org/orekit/frames/MODProvider.java +++ b/src/main/java/org/orekit/frames/MODProvider.java @@ -20,8 +20,8 @@ import java.util.HashMap; import java.util.Map; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.RotationConvention; @@ -94,16 +94,18 @@ public Transform getTransform(final AbsoluteDate date) { } /** {@inheritDoc} */ + @SuppressWarnings("unchecked") @Override public > FieldTransform getTransform(final FieldAbsoluteDate date) { // compute the precession angles phiA, omegaA, chiA final T[] angles = precessionFunction.value(date); - @SuppressWarnings("unchecked") - final FieldRotation fR4 = - (FieldRotation) fieldR4.computeIfAbsent(date.getField(), - f -> new FieldRotation<>((Field) f, r4)); + final FieldRotation fR4; + synchronized (fieldR4) { + fR4 = (FieldRotation) fieldR4.computeIfAbsent(date.getField(), + f -> new FieldRotation<>((Field) f, r4)); + } // complete precession final FieldRotation precession = fR4.compose(new FieldRotation<>(RotationOrder.ZXZ, RotationConvention.FRAME_TRANSFORM, diff --git a/src/main/java/org/orekit/frames/StaticTransform.java b/src/main/java/org/orekit/frames/StaticTransform.java index eb00eb5112..932d1e497e 100644 --- a/src/main/java/org/orekit/frames/StaticTransform.java +++ b/src/main/java/org/orekit/frames/StaticTransform.java @@ -38,11 +38,57 @@ public interface StaticTransform extends TimeStamped { /** * Get the identity static transform. + * It overrides most methods for speed. * * @return identity transform. */ static StaticTransform getIdentity() { - return Transform.IDENTITY; + return new StaticTransform() { + @Override + public Vector3D getTranslation() { + return Vector3D.ZERO; + } + + @Override + public Rotation getRotation() { + return Rotation.IDENTITY; + } + + @Override + public StaticTransform getStaticInverse() { + return getInverse(); + } + + @Override + public StaticTransform getInverse() { + return this; + } + + @Override + public AbsoluteDate getDate() { + return AbsoluteDate.ARBITRARY_EPOCH; + } + + @Override + public Vector3D transformPosition(final Vector3D position) { + return transformVector(position); + } + + @Override + public Vector3D transformVector(final Vector3D vector) { + return new Vector3D(vector.getX(), vector.getY(), vector.getZ()); + } + + @Override + public > FieldVector3D transformVector(final FieldVector3D vector) { + return new FieldVector3D<>(vector.getX(), vector.getY(), vector.getZ()); + } + + @Override + public > FieldVector3D transformPosition(final FieldVector3D position) { + return transformVector(position); + } + }; } /** diff --git a/src/main/java/org/orekit/frames/Transform.java b/src/main/java/org/orekit/frames/Transform.java index 5d7ca31253..2d4c6839bb 100644 --- a/src/main/java/org/orekit/frames/Transform.java +++ b/src/main/java/org/orekit/frames/Transform.java @@ -25,6 +25,7 @@ import java.util.stream.Stream; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Line; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; @@ -543,76 +544,65 @@ public > TimeStampedFieldPVCoordinates tran */ public void getJacobian(final CartesianDerivativesFilter selector, final double[][] jacobian) { - // elementary matrix for rotation - final double[][] mData = angular.getRotation().getMatrix(); + if (selector.getMaxOrder() == 0) { + // elementary matrix for rotation + final double[][] mData = angular.getRotation().getMatrix(); - // dP1/dP0 - System.arraycopy(mData[0], 0, jacobian[0], 0, 3); - System.arraycopy(mData[1], 0, jacobian[1], 0, 3); - System.arraycopy(mData[2], 0, jacobian[2], 0, 3); + // dP1/dP0 + System.arraycopy(mData[0], 0, jacobian[0], 0, 3); + System.arraycopy(mData[1], 0, jacobian[1], 0, 3); + System.arraycopy(mData[2], 0, jacobian[2], 0, 3); + } + + else if (selector.getMaxOrder() == 1) { + // use KinematicTransform Jacobian + final double[][] mData = getPVJacobian(); + for (int i = 0; i < mData.length; i++) { + System.arraycopy(mData[i], 0, jacobian[i], 0, mData[i].length); + } + } - if (selector.getMaxOrder() >= 1) { + else if (selector.getMaxOrder() >= 2) { + getJacobian(CartesianDerivativesFilter.USE_PV, jacobian); - // dP1/dV0 - Arrays.fill(jacobian[0], 3, 6, 0.0); - Arrays.fill(jacobian[1], 3, 6, 0.0); - Arrays.fill(jacobian[2], 3, 6, 0.0); + // dP1/dA0 + Arrays.fill(jacobian[0], 6, 9, 0.0); + Arrays.fill(jacobian[1], 6, 9, 0.0); + Arrays.fill(jacobian[2], 6, 9, 0.0); - // dV1/dP0 + // dV1/dA0 + Arrays.fill(jacobian[3], 6, 9, 0.0); + Arrays.fill(jacobian[4], 6, 9, 0.0); + Arrays.fill(jacobian[5], 6, 9, 0.0); + + // dA1/dP0 final Vector3D o = angular.getRotationRate(); final double ox = o.getX(); final double oy = o.getY(); final double oz = o.getZ(); + final Vector3D oDot = angular.getRotationAcceleration(); + final double oDotx = oDot.getX(); + final double oDoty = oDot.getY(); + final double oDotz = oDot.getZ(); for (int i = 0; i < 3; ++i) { - jacobian[3][i] = -(oy * mData[2][i] - oz * mData[1][i]); - jacobian[4][i] = -(oz * mData[0][i] - ox * mData[2][i]); - jacobian[5][i] = -(ox * mData[1][i] - oy * mData[0][i]); + jacobian[6][i] = -(oDoty * jacobian[2][i] - oDotz * jacobian[1][i]) - (oy * jacobian[5][i] - oz * jacobian[4][i]); + jacobian[7][i] = -(oDotz * jacobian[0][i] - oDotx * jacobian[2][i]) - (oz * jacobian[3][i] - ox * jacobian[5][i]); + jacobian[8][i] = -(oDotx * jacobian[1][i] - oDoty * jacobian[0][i]) - (ox * jacobian[4][i] - oy * jacobian[3][i]); } - // dV1/dV0 - System.arraycopy(mData[0], 0, jacobian[3], 3, 3); - System.arraycopy(mData[1], 0, jacobian[4], 3, 3); - System.arraycopy(mData[2], 0, jacobian[5], 3, 3); - - if (selector.getMaxOrder() >= 2) { - - // dP1/dA0 - Arrays.fill(jacobian[0], 6, 9, 0.0); - Arrays.fill(jacobian[1], 6, 9, 0.0); - Arrays.fill(jacobian[2], 6, 9, 0.0); - - // dV1/dA0 - Arrays.fill(jacobian[3], 6, 9, 0.0); - Arrays.fill(jacobian[4], 6, 9, 0.0); - Arrays.fill(jacobian[5], 6, 9, 0.0); - - // dA1/dP0 - final Vector3D oDot = angular.getRotationAcceleration(); - final double oDotx = oDot.getX(); - final double oDoty = oDot.getY(); - final double oDotz = oDot.getZ(); - for (int i = 0; i < 3; ++i) { - jacobian[6][i] = -(oDoty * mData[2][i] - oDotz * mData[1][i]) - (oy * jacobian[5][i] - oz * jacobian[4][i]); - jacobian[7][i] = -(oDotz * mData[0][i] - oDotx * mData[2][i]) - (oz * jacobian[3][i] - ox * jacobian[5][i]); - jacobian[8][i] = -(oDotx * mData[1][i] - oDoty * mData[0][i]) - (ox * jacobian[4][i] - oy * jacobian[3][i]); - } - - // dA1/dV0 - for (int i = 0; i < 3; ++i) { - jacobian[6][i + 3] = -2 * (oy * mData[2][i] - oz * mData[1][i]); - jacobian[7][i + 3] = -2 * (oz * mData[0][i] - ox * mData[2][i]); - jacobian[8][i + 3] = -2 * (ox * mData[1][i] - oy * mData[0][i]); - } - - // dA1/dA0 - System.arraycopy(mData[0], 0, jacobian[6], 6, 3); - System.arraycopy(mData[1], 0, jacobian[7], 6, 3); - System.arraycopy(mData[2], 0, jacobian[8], 6, 3); - + // dA1/dV0 + for (int i = 0; i < 3; ++i) { + jacobian[6][i + 3] = -2 * (oy * jacobian[2][i] - oz * jacobian[1][i]); + jacobian[7][i + 3] = -2 * (oz * jacobian[0][i] - ox * jacobian[2][i]); + jacobian[8][i + 3] = -2 * (ox * jacobian[1][i] - oy * jacobian[0][i]); } - } + // dA1/dA0 + System.arraycopy(jacobian[0], 0, jacobian[6], 6, 3); + System.arraycopy(jacobian[1], 0, jacobian[7], 6, 3); + System.arraycopy(jacobian[2], 0, jacobian[8], 6, 3); + } } /** Get the underlying elementary Cartesian part. @@ -718,18 +708,33 @@ private static class IdentityTransform extends Transform { super(AbsoluteDate.ARBITRARY_EPOCH, PVCoordinates.ZERO, AngularCoordinates.IDENTITY); } + @Override + public StaticTransform staticShiftedBy(final double dt) { + return toStaticTransform(); + } + /** {@inheritDoc} */ @Override public Transform shiftedBy(final double dt) { return this; } + @Override + public StaticTransform getStaticInverse() { + return toStaticTransform(); + } + /** {@inheritDoc} */ @Override public Transform getInverse() { return this; } + @Override + public StaticTransform toStaticTransform() { + return StaticTransform.getIdentity(); + } + /** {@inheritDoc} */ @Override public Vector3D transformPosition(final Vector3D position) { @@ -742,6 +747,16 @@ public Vector3D transformVector(final Vector3D vector) { return vector; } + @Override + public > FieldVector3D transformPosition(final FieldVector3D position) { + return transformVector(position); + } + + @Override + public > FieldVector3D transformVector(final FieldVector3D vector) { + return new FieldVector3D<>(vector.getX(), vector.getY(), vector.getZ()); + } + /** {@inheritDoc} */ @Override public Line transformLine(final Line line) { @@ -754,6 +769,16 @@ public PVCoordinates transformPVCoordinates(final PVCoordinates pv) { return pv; } + @Override + public PVCoordinates transformOnlyPV(final PVCoordinates pv) { + return new PVCoordinates(pv.getPosition(), pv.getVelocity()); + } + + @Override + public TimeStampedPVCoordinates transformOnlyPV(final TimeStampedPVCoordinates pv) { + return new TimeStampedPVCoordinates(pv.getDate(), pv.getPosition(), pv.getVelocity()); + } + @Override public Transform freeze() { return this; diff --git a/src/main/java/org/orekit/frames/TransformProviderUtils.java b/src/main/java/org/orekit/frames/TransformProviderUtils.java index e3477abc20..047fd5c761 100644 --- a/src/main/java/org/orekit/frames/TransformProviderUtils.java +++ b/src/main/java/org/orekit/frames/TransformProviderUtils.java @@ -47,6 +47,12 @@ public Transform getTransform(final AbsoluteDate date) { return Transform.IDENTITY; } + /** {@inheritDoc} */ + @Override + public StaticTransform getStaticTransform(final AbsoluteDate date) { + return StaticTransform.getIdentity(); + } + /** {@inheritDoc} *

* Always returns {@link FieldTransform#getIdentity(org.hipparchus.Field)} @@ -57,6 +63,11 @@ public > FieldTransform getTransform(final return FieldTransform.getIdentity(date.getField()); } + /** {@inheritDoc} */ + @Override + public > FieldStaticTransform getStaticTransform(final FieldAbsoluteDate date) { + return FieldStaticTransform.getIdentity(date.getField()); + } }; /** Private constructor. diff --git a/src/main/java/org/orekit/gnss/TimeSystem.java b/src/main/java/org/orekit/gnss/TimeSystem.java index 3ede2749ac..89690ae14a 100644 --- a/src/main/java/org/orekit/gnss/TimeSystem.java +++ b/src/main/java/org/orekit/gnss/TimeSystem.java @@ -63,12 +63,12 @@ public enum TimeSystem { */ SBAS(null, "SB", "S", TimeScales::getUTC), - /** GMT (should only by used in RUN BY / DATE entries). + /** GMT (should only be used in RUN BY / DATE entries). * @since 12.0 */ GMT("GMT", null, null, TimeScales::getUTC), - /** Unknown (should only by used in RUN BY / DATE entries). */ + /** Unknown (should only be used in RUN BY / DATE entries). */ UNKNOWN("LCL", null, null, TimeScales::getGPS); /** Parsing key map. */ @@ -138,6 +138,22 @@ public String getKey() { return key; } + /** Get the two letters code. + * @return two letters code (may be null for non-GNSS time systems) + * @since 12.2 + */ + public String getTwoLettersCode() { + return twoLettersCode; + } + + /** Get the one letter code. + * @return one letter code (may be null for non-GNSS time systems) + * @since 12.2 + */ + public String getOneLetterCode() { + return oneLetterCode; + } + /** Parse a string to get the time system. *

* The string must be the time system. diff --git a/src/main/java/org/orekit/models/earth/atmosphere/JB2008.java b/src/main/java/org/orekit/models/earth/atmosphere/JB2008.java index c894024360..b5e3d94f8e 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/JB2008.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/JB2008.java @@ -43,7 +43,7 @@ /** This is the realization of the Jacchia-Bowman 2008 atmospheric model. *

* It is described in the paper:
- * A + * A * New Empirical Thermospheric Density Model JB2008 Using New Solar Indices
* Bruce R. Bowman & al.
* AIAA 2008-6438
diff --git a/src/main/java/org/orekit/models/earth/ionosphere/FieldNeQuickParameters.java b/src/main/java/org/orekit/models/earth/ionosphere/FieldNeQuickParameters.java index 061270c780..0908387e4f 100644 --- a/src/main/java/org/orekit/models/earth/ionosphere/FieldNeQuickParameters.java +++ b/src/main/java/org/orekit/models/earth/ionosphere/FieldNeQuickParameters.java @@ -21,7 +21,6 @@ import org.hipparchus.util.FastMath; import org.hipparchus.util.FieldSinCos; import org.hipparchus.util.MathArrays; -import org.hipparchus.util.SinCos; import org.orekit.time.DateComponents; import org.orekit.time.DateTimeComponents; import org.orekit.time.TimeComponents; @@ -76,21 +75,21 @@ class FieldNeQuickParameters > { /** * Build a new instance. - * @param field field of the elements * @param dateTime current date time components - * @param f2 F2 coefficients used by the F2 layer - * @param fm3 Fm3 coefficients used by the F2 layer + * @param flattenF2 F2 coefficients used by the F2 layer (flatten array) + * @param flattenFm3 Fm3 coefficients used by the F2 layer (flatten array) * @param latitude latitude of a point along the integration path, in radians * @param longitude longitude of a point along the integration path, in radians * @param alpha effective ionisation level coefficients * @param modipGrip modip grid */ - FieldNeQuickParameters(final Field field, final DateTimeComponents dateTime, final double[][][] f2, - final double[][][] fm3, final T latitude, final T longitude, + FieldNeQuickParameters(final DateTimeComponents dateTime, + final double[] flattenF2, final double[] flattenFm3, + final T latitude, final T longitude, final double[] alpha, final double[][] modipGrip) { // Zero - final T zero = field.getZero(); + final T zero = latitude.getField().getZero(); // MODIP in degrees final T modip = computeMODIP(latitude, longitude, modipGrip); @@ -106,25 +105,8 @@ class FieldNeQuickParameters > { // Effective solar zenith angle in radians final T xeff = computeEffectiveSolarAngle(date.getMonth(), hours, latitude, longitude); - // Coefficients for F2 layer parameters - // Compute the array of interpolated coefficients for foF2 (Eq. 44) - final T[][] af2 = MathArrays.buildArray(field, 76, 13); - for (int j = 0; j < 76; j++) { - for (int k = 0; k < 13; k++ ) { - af2[j][k] = azr.multiply(0.01).negate().add(1.0).multiply(f2[0][j][k]).add(azr.multiply(0.01).multiply(f2[1][j][k])); - } - } - - // Compute the array of interpolated coefficients for M(3000)F2 (Eq. 46) - final T[][] am3 = MathArrays.buildArray(field, 49, 9); - for (int j = 0; j < 49; j++) { - for (int k = 0; k < 9; k++ ) { - am3[j][k] = azr.multiply(0.01).negate().add(1.0).multiply(fm3[0][j][k]).add(azr.multiply(0.01).multiply(fm3[1][j][k])); - } - } - // E layer maximum density height in km (Eq. 78) - this.hmE = field.getZero().newInstance(120.0); + this.hmE = zero.newInstance(120.0); // E layer critical frequency in MHz final T foE = computefoE(date.getMonth(), az, xeff, latitude); // E layer maximum density in 10^11 m-3 (Eq. 36) @@ -133,19 +115,21 @@ class FieldNeQuickParameters > { // Time argument (Eq. 49) final double t = FastMath.toRadians(15 * hours) - FastMath.PI; // Compute Fourier time series for foF2 and M(3000)F2 - final T[] cf2 = computeCF2(field, af2, t); - final T[] cm3 = computeCm3(field, am3, t); + final T[] scT = sinCos(zero.newInstance(t), 6); + final T[] cf2 = computeCF2(flattenF2, azr, scT); + final T[] cm3 = computeCm3(flattenFm3, azr, scT); // F2 layer critical frequency in MHz - final T foF2 = computefoF2(field, modip, cf2, latitude, longitude); + final T[] scL = sinCos(longitude, 8); + final T foF2 = computefoF2(modip, cf2, latitude, scL); // Maximum Usable Frequency factor - final T mF2 = computeMF2(field, modip, cm3, latitude, longitude); + final T mF2 = computeMF2(modip, cm3, latitude, scL); // F2 layer maximum density in 10^11 m-3 this.nmF2 = foF2.multiply(foF2).multiply(0.124); // F2 layer maximum density height in km - this.hmF2 = computehmF2(field, foE, foF2, mF2); + this.hmF2 = computehmF2(foE, foF2, mF2); // F1 layer critical frequency in MHz - final T foF1 = computefoF1(field, foE, foF2); + final T foF1 = computefoF1(foE, foF2); // F1 layer maximum density in 10^11 m-3 final T nmF1; if (foF1.getReal() <= 0.0 && foE.getReal() > 2.0) { @@ -166,10 +150,10 @@ class FieldNeQuickParameters > { this.beBot = zero.newInstance(5.0); // Layer amplitude coefficients - this.amplitudes = computeLayerAmplitudes(field, nmE, nmF1, foF1); + this.amplitudes = computeLayerAmplitudes(nmE, nmF1, foF1); // Topside thickness parameter - this.h0 = computeH0(field, date.getMonth(), azr); + this.h0 = computeH0(date.getMonth(), azr); } /** @@ -323,9 +307,8 @@ private T computeMODIP(final T lat, final T lon, final double[][] stModip) { final T y = b.subtract(bF); // MODIP (Ref Eq. 16) - final T modip = interpolate(z1, z2, z3, z4, y); + return interpolate(z1, z2, z3, z4, y); - return modip; } /** @@ -425,21 +408,20 @@ private T computefoE(final int month, final T az, final T seasp = ee.subtract(1.0).divide(ee.add(1.0)).multiply(seas); // Critical frequency (Eq. 35) final T coef = seasp.multiply(0.019).negate().add(1.112); - final T foE = FastMath.sqrt(coef .multiply(coef).multiply(sqAz).multiply(FastMath.cos(xeff).pow(0.6)).add(0.49)); - return foE; + return FastMath.sqrt(coef .multiply(coef).multiply(sqAz).multiply(FastMath.cos(xeff).pow(0.6)).add(0.49)); + } /** * Computes the F2 layer height of maximum electron density. - * @param field field of the elements * @param foE E layer layer critical frequency in MHz * @param foF2 F2 layer layer critical frequency in MHz * @param mF2 maximum usable frequency factor * @return hmF2 in km */ - private T computehmF2(final Field field, final T foE, final T foF2, final T mF2) { + private T computehmF2(final T foE, final T foF2, final T mF2) { // Zero - final T zero = field.getZero(); + final T zero = foE.getField().getZero(); // Ratio final T fo = foF2.divide(foE); final T ratio = join(fo, zero.newInstance(1.75), zero.newInstance(20.0), fo.subtract(1.75)); @@ -453,163 +435,192 @@ private T computehmF2(final Field field, final T foE, final T foF2, final T m // hmF2 Eq. 80 final T mF2Sq = mF2.square(); final T temp = FastMath.sqrt(mF2Sq.multiply(0.0196).add(1.0).divide(mF2Sq.multiply(1.2967).subtract(1.0))); - final T height = mF2.multiply(1490.0).multiply(temp).divide(mF2.add(deltaM)).subtract(176.0); - return height; + return mF2.multiply(1490.0).multiply(temp).divide(mF2.add(deltaM)).subtract(176.0); + + } + + /** Compute sines and cosines. + * @param a argument + * @param n number of terms + * @return sin(a), cos(a), sin(2a), cos(2a) … sin(n a), cos(n a) array + * @since 12.1.3 + */ + private T[] sinCos(final T a, final int n) { + + final FieldSinCos sc0 = FastMath.sinCos(a); + FieldSinCos sci = sc0; + final T[] sc = MathArrays.buildArray(a.getField(), 2 * n); + int isc = 0; + sc[isc++] = sci.sin(); + sc[isc++] = sci.cos(); + for (int i = 1; i < n; i++) { + sci = FieldSinCos.sum(sc0, sci); + sc[isc++] = sci.sin(); + sc[isc++] = sci.cos(); + } + + return sc; + } /** * Computes cf2 coefficients. - * @param field field of the elements - * @param af2 interpolated coefficients for foF2 - * @param t time argument + * @param flattenF2 F2 coefficients used by the F2 layer (flatten array) + * @param azr effective sunspot number (Eq. 19) + * @param scT sines/cosines array of time argument * @return the cf2 coefficients array */ - private T[] computeCF2(final Field field, final T[][] af2, final double t) { - // Eq. 50 - final T[] cf2 = MathArrays.buildArray(field, 76); + private T[] computeCF2(final double[] flattenF2, final T azr, final T[] scT) { + + // interpolation coefficients for effective spot number + final T azr01 = azr.multiply(0.01); + final T omazr01 = azr01.negate().add(1); + + // Eq. 44 and Eq. 50 merged into one loop + final T[] cf2 = MathArrays.buildArray(azr.getField(), 76); + int index = 0; for (int i = 0; i < cf2.length; i++) { - T sum = field.getZero(); - for (int k = 0; k < 6; k++) { - final SinCos sc = FastMath.sinCos((k + 1) * t); - sum = sum.add(af2[i][2 * k + 1].multiply(sc.sin()).add(af2[i][2 * (k + 1)].multiply(sc.cos()))); - } - cf2[i] = af2[i][0].add(sum); + // CHECKSTYLE: stop Indentation check + cf2[i] = omazr01.multiply(flattenF2[index ]).add(azr01.multiply(flattenF2[index + 1])). + add(omazr01.multiply(flattenF2[index + 2]).add(azr01.multiply(flattenF2[index + 3])).multiply(scT[ 0])). + add(omazr01.multiply(flattenF2[index + 4]).add(azr01.multiply(flattenF2[index + 5])).multiply(scT[ 1])). + add(omazr01.multiply(flattenF2[index + 6]).add(azr01.multiply(flattenF2[index + 7])).multiply(scT[ 2])). + add(omazr01.multiply(flattenF2[index + 8]).add(azr01.multiply(flattenF2[index + 9])).multiply(scT[ 3])). + add(omazr01.multiply(flattenF2[index + 10]).add(azr01.multiply(flattenF2[index + 11])).multiply(scT[ 4])). + add(omazr01.multiply(flattenF2[index + 12]).add(azr01.multiply(flattenF2[index + 13])).multiply(scT[ 5])). + add(omazr01.multiply(flattenF2[index + 14]).add(azr01.multiply(flattenF2[index + 15])).multiply(scT[ 6])). + add(omazr01.multiply(flattenF2[index + 16]).add(azr01.multiply(flattenF2[index + 17])).multiply(scT[ 7])). + add(omazr01.multiply(flattenF2[index + 18]).add(azr01.multiply(flattenF2[index + 19])).multiply(scT[ 8])). + add(omazr01.multiply(flattenF2[index + 20]).add(azr01.multiply(flattenF2[index + 21])).multiply(scT[ 9])). + add(omazr01.multiply(flattenF2[index + 22]).add(azr01.multiply(flattenF2[index + 23])).multiply(scT[10])). + add(omazr01.multiply(flattenF2[index + 24]).add(azr01.multiply(flattenF2[index + 25])).multiply(scT[11])); + index += 26; + // CHECKSTYLE: resume Indentation check } return cf2; } /** * Computes Cm3 coefficients. - * @param field field of the elements - * @param am3 interpolated coefficients for foF2 - * @param t time argument + * @param flattenFm3 Fm3 coefficients used by the F2 layer (flatten array) + * @param azr effective sunspot number (Eq. 19) + * @param scT sines/cosines array of time argument * @return the Cm3 coefficients array */ - private T[] computeCm3(final Field field, final T[][] am3, final double t) { - // Eq. 51 - final T[] cm3 = MathArrays.buildArray(field, 49); + private T[] computeCm3(final double[] flattenFm3, final T azr, final T[] scT) { + + // interpolation coefficients for effective spot number + final T azr01 = azr.multiply(0.01); + final T omazr01 = azr01.negate().add(1); + + // Eq. 44 and Eq. 51 merged into one loop + final T[] cm3 = MathArrays.buildArray(azr.getField(), 49); + int index = 0; for (int i = 0; i < cm3.length; i++) { - T sum = field.getZero(); - for (int k = 0; k < 4; k++) { - final SinCos sc = FastMath.sinCos((k + 1) * t); - sum = sum.add(am3[i][2 * k + 1].multiply(sc.sin()).add(am3[i][2 * (k + 1)].multiply(sc.cos()))); - } - cm3[i] = am3[i][0].add(sum); + cm3[i] = omazr01.multiply(flattenFm3[index ]).add(azr01.multiply(flattenFm3[index + 1])). + add(omazr01.multiply(flattenFm3[index + 2]).add(azr01.multiply(flattenFm3[index + 3])).multiply(scT[ 0])). + add(omazr01.multiply(flattenFm3[index + 4]).add(azr01.multiply(flattenFm3[index + 5])).multiply(scT[ 1])). + add(omazr01.multiply(flattenFm3[index + 6]).add(azr01.multiply(flattenFm3[index + 7])).multiply(scT[ 2])). + add(omazr01.multiply(flattenFm3[index + 8]).add(azr01.multiply(flattenFm3[index + 9])).multiply(scT[ 3])). + add(omazr01.multiply(flattenFm3[index + 10]).add(azr01.multiply(flattenFm3[index + 11])).multiply(scT[ 4])). + add(omazr01.multiply(flattenFm3[index + 12]).add(azr01.multiply(flattenFm3[index + 13])).multiply(scT[ 5])). + add(omazr01.multiply(flattenFm3[index + 14]).add(azr01.multiply(flattenFm3[index + 15])).multiply(scT[ 6])). + add(omazr01.multiply(flattenFm3[index + 16]).add(azr01.multiply(flattenFm3[index + 17])).multiply(scT[ 7])); + index += 18; } return cm3; } /** * This method computes the F2 layer critical frequency. - * @param field field of the elements * @param modip modified DIP latitude, in degrees * @param cf2 Fourier time series for foF2 * @param latitude latitude in radians - * @param longitude longitude in radians + * @param scL sines/cosines array of longitude argument * @return the F2 layer critical frequency, MHz */ - private T computefoF2(final Field field, final T modip, final T[] cf2, - final T latitude, final T longitude) { - - // One - final T one = field.getOne(); + private T computefoF2(final T modip, final T[] cf2, + final T latitude, final T[] scL) { // Legendre grades (Eq. 63) final int[] q = new int[] { 12, 12, 9, 5, 2, 1, 1, 1, 1 }; - // Array for geographic terms - final T[] g = MathArrays.buildArray(field, cf2.length); - g[0] = one; + T frequency = cf2[0]; // MODIP coefficients Eq. 57 final T sinMODIP = FastMath.sin(FastMath.toRadians(modip)); - final T[] m = MathArrays.buildArray(field, 12); - m[0] = one; + final T[] m = MathArrays.buildArray(latitude.getField(), 12); + m[0] = latitude.getField().getOne(); for (int i = 1; i < q[0]; i++) { m[i] = sinMODIP.multiply(m[i - 1]); - g[i] = m[i]; - } - - // Latitude coefficients (Eq. 58) - final T cosLat = FastMath.cos(latitude); - final T[] p = MathArrays.buildArray(field, 8); - p[0] = cosLat; - for (int n = 2; n < 9; n++) { - p[n - 1] = cosLat.multiply(p[n - 2]); + frequency = frequency.add(m[i].multiply(cf2[i])); } // latitude and longitude terms int index = 12; + final T cosLat1 = FastMath.cos(latitude); + T cosLatI = cosLat1; for (int i = 1; i < q.length; i++) { - final FieldSinCos sc = FastMath.sinCos(longitude.multiply(i)); + final T c = cosLatI.multiply(scL[2 * i - 1]); + final T s = cosLatI.multiply(scL[2 * i - 2]); for (int j = 0; j < q[i]; j++) { - g[index++] = m[j].multiply(p[i - 1]).multiply(sc.cos()); - g[index++] = m[j].multiply(p[i - 1]).multiply(sc.sin()); + frequency = frequency.add(m[j].multiply(c).multiply(cf2[index++])); + frequency = frequency.add(m[j].multiply(s).multiply(cf2[index++])); } + cosLatI = cosLatI.multiply(cosLat1); } - // Compute foF2 by linear combination - final T frequency = one.linearCombination(g, cf2); return frequency; + } /** * This method computes the Maximum Usable Frequency factor. - * @param field field of the elements * @param modip modified DIP latitude, in degrees * @param cm3 Fourier time series for M(3000)F2 * @param latitude latitude in radians - * @param longitude longitude in radians + * @param scL sines/cosines array of longitude argument * @return the Maximum Usable Frequency factor */ - private T computeMF2(final Field field, final T modip, final T[] cm3, - final T latitude, final T longitude) { + private T computeMF2(final T modip, final T[] cm3, + final T latitude, final T[] scL) { - // One - final T one = field.getOne(); // Legendre grades (Eq. 71) final int[] r = new int[] { 7, 8, 6, 3, 2, 1, 1 }; - // Array for geographic terms - final T[] g = MathArrays.buildArray(field, cm3.length); - g[0] = one; + T m3000 = cm3[0]; // MODIP coefficients Eq. 57 final T sinMODIP = FastMath.sin(FastMath.toRadians(modip)); - final T[] m = MathArrays.buildArray(field, 12); - m[0] = one; + final T[] m = MathArrays.buildArray(latitude.getField(), 12); + m[0] = latitude.getField().getOne(); for (int i = 1; i < 12; i++) { m[i] = sinMODIP.multiply(m[i - 1]); if (i < 7) { - g[i] = m[i]; + m3000 = m3000.add(m[i].multiply(cm3[i])); } } - // Latitude coefficients (Eq. 58) - final T cosLat = FastMath.cos(latitude); - final T[] p = MathArrays.buildArray(field, 8); - p[0] = cosLat; - for (int n = 2; n < 9; n++) { - p[n - 1] = cosLat.multiply(p[n - 2]); - } - // latitude and longitude terms int index = 7; + final T cosLat1 = FastMath.cos(latitude); + T cosLatI = cosLat1; for (int i = 1; i < r.length; i++) { - final FieldSinCos sc = FastMath.sinCos(longitude.multiply(i)); + final T c = cosLatI.multiply(scL[2 * i - 1]); + final T s = cosLatI.multiply(scL[2 * i - 2]); for (int j = 0; j < r[i]; j++) { - g[index++] = m[j].multiply(p[i - 1]).multiply(sc.cos()); - g[index++] = m[j].multiply(p[i - 1]).multiply(sc.sin()); + m3000 = m3000.add(m[j].multiply(c).multiply(cm3[index++])); + m3000 = m3000.add(m[j].multiply(s).multiply(cm3[index++])); } + cosLatI = cosLatI.multiply(cosLat1); // Eq. 58 } - // Compute m3000 by linear combination - final T m3000 = one.linearCombination(g, cm3); return m3000; + } /** @@ -618,13 +629,12 @@ private T computeMF2(final Field field, final T modip, final T[] cm3, * This computation performs the algorithm exposed in Annex F * of the reference document. *

- * @param field field of the elements * @param foE the E layer critical frequency, MHz * @return the F1 layer critical frequency, MHz * @param foF2 the F2 layer critical frequency, MHz */ - private T computefoF1(final Field field, final T foE, final T foF2) { - final T zero = field.getZero(); + private T computefoF1(final T foE, final T foF2) { + final T zero = foE.getField().getZero(); final T temp = join(foE.multiply(1.4), zero, zero.newInstance(1000.0), foE.subtract(2.0)); final T temp2 = join(zero, temp, zero.newInstance(1000.0), foE.subtract(temp)); final T value = join(temp2, temp2.multiply(0.85), zero.newInstance(60.0), foF2.multiply(0.85).subtract(temp2)); @@ -645,18 +655,17 @@ private T computefoF1(final Field field, final T foE, final T foF2) { *
  • double[2] = A3 → E layer amplitude * *

    - * @param field field of the elements * @param nmE E layer maximum density in 10^11 m-3 * @param nmF1 F1 layer maximum density in 10^11 m-3 * @param foF1 F1 layer critical frequency in MHz * @return a three components array containing the layer amplitudes */ - private T[] computeLayerAmplitudes(final Field field, final T nmE, final T nmF1, final T foF1) { + private T[] computeLayerAmplitudes(final T nmE, final T nmF1, final T foF1) { // Zero - final T zero = field.getZero(); + final T zero = nmE.getField().getZero(); // Initialize array - final T[] amplitude = MathArrays.buildArray(field, 3); + final T[] amplitude = MathArrays.buildArray(nmE.getField(), 3); // F2 layer amplitude (Eq. 90) final T a1 = nmF2.multiply(4.0); @@ -671,7 +680,7 @@ private T[] computeLayerAmplitudes(final Field field, final T nmE, final T nm T a3a = nmE.multiply(4.0); for (int i = 0; i < 5; i++) { a2a = nmF1.subtract(epst(a1, hmF2, b2Bot, hmF1)).subtract(epst(a3a, hmE, beTop, hmF1)).multiply(4.0); - a2a = join(a2a, nmF1.multiply(0.8), field.getOne(), a2a.subtract(nmF1.multiply(0.8))); + a2a = join(a2a, nmF1.multiply(0.8), nmE.getField().getOne(), a2a.subtract(nmF1.multiply(0.8))); a3a = nmE.subtract(epst(a2a, hmF1, b1Bot, hmE)).subtract(epst(a1, hmF2, b2Bot, hmE)).multiply(4.0); } amplitude[1] = a2a; @@ -684,15 +693,14 @@ private T[] computeLayerAmplitudes(final Field field, final T nmE, final T nm /** * This method computes the topside thickness parameter H0. * - * @param field field of the elements * @param month current month * @param azr effective sunspot number * @return H0 in km */ - private T computeH0(final Field field, final int month, final T azr) { + private T computeH0(final int month, final T azr) { // One - final T one = field.getOne(); + final T one = azr.getField().getOne(); // Auxiliary parameter ka (Eq. 99 and 100) final T ka; @@ -717,8 +725,8 @@ private T computeH0(final Field field, final int month, final T azr) { final T v = x.multiply(0.041163).subtract(0.183981).multiply(x).add(1.424472); // Topside thickness parameter (Eq. 106) - final T h = hA.divide(v); - return h; + return hA.divide(v); + } /** @@ -769,9 +777,8 @@ private T interpolate(final T z1, final T z2, final T a1 = g2.multiply(9.0).subtract(g4); final T a2 = g3.subtract(g1); final T a3 = g4.subtract(g2); - final T zx = delta.multiply(a3).add(a2).multiply(delta).add(a1).multiply(delta).add(a0).multiply(0.0625); + return delta.multiply(a3).add(a2).multiply(delta).add(a1).multiply(delta).add(a0).multiply(0.0625); - return zx; } /** @@ -809,8 +816,8 @@ private T epst(final T x, final T y, final T z, final T w) { final T ex = clipExp(w.subtract(y).divide(z)); final T opex = ex.add(1.0); - final T epst = x.multiply(ex).divide(opex.multiply(opex)); - return epst; + return x.multiply(ex).divide(opex.multiply(opex)); + } } diff --git a/src/main/java/org/orekit/models/earth/ionosphere/NeQuickModel.java b/src/main/java/org/orekit/models/earth/ionosphere/NeQuickModel.java index 1af899a1c9..1814823ddd 100644 --- a/src/main/java/org/orekit/models/earth/ionosphere/NeQuickModel.java +++ b/src/main/java/org/orekit/models/earth/ionosphere/NeQuickModel.java @@ -87,11 +87,11 @@ public class NeQuickModel implements IonosphericModel { /** Month used for loading CCIR coefficients. */ private int month; - /** F2 coefficients used by the F2 layer. */ - private double[][][] f2; + /** F2 coefficients used by the F2 layer (flatten array for cache efficiency). */ + private double[] flattenF2; - /** Fm3 coefficients used by the F2 layer. */ - private double[][][] fm3; + /** Fm3 coefficients used by the F2 layer(flatten array for cache efficiency). */ + private double[] flattenFm3; /** UTC time scale. */ private final TimeScale utc; @@ -118,9 +118,9 @@ public NeQuickModel(final double[] alpha) { public NeQuickModel(final double[] alpha, final TimeScale utc) { // F2 layer values - this.month = 0; - this.f2 = null; - this.fm3 = null; + this.month = 0; + this.flattenF2 = null; + this.flattenFm3 = null; // Read modip grid final MODIPLoader parser = new MODIPLoader(); parser.loadMODIPGrid(); @@ -309,7 +309,7 @@ private double stecIntegration(final Segment seg, final DateTimeComponents dateT // Compute electron density double density = 0.0; for (int i = 0; i < heightS.length; i++) { - final NeQuickParameters parameters = new NeQuickParameters(dateTime, f2, fm3, + final NeQuickParameters parameters = new NeQuickParameters(dateTime, flattenF2, flattenFm3, latitudeS[i], longitudeS[i], alpha, stModip); density += electronDensity(heightS[i], parameters); @@ -327,8 +327,8 @@ private double stecIntegration(final Segment seg, final DateTimeComponents dateT * @return result of the integration */ private > T stecIntegration(final Field field, - final FieldSegment seg, - final DateTimeComponents dateTime) { + final FieldSegment seg, + final DateTimeComponents dateTime) { // Integration points final T[] heightS = seg.getHeights(); final T[] latitudeS = seg.getLatitudes(); @@ -337,7 +337,7 @@ private > T stecIntegration(final Field fie // Compute electron density T density = field.getZero(); for (int i = 0; i < heightS.length; i++) { - final FieldNeQuickParameters parameters = new FieldNeQuickParameters<>(field, dateTime, f2, fm3, + final FieldNeQuickParameters parameters = new FieldNeQuickParameters<>(dateTime, flattenF2, flattenFm3, latitudeS[i], longitudeS[i], alpha, stModip); density = density.add(electronDensity(field, heightS[i], parameters)); @@ -609,7 +609,7 @@ private void loadsIfNeeded(final DateComponents date) { final int currentMonth = date.getMonth(); // Check if date have changed or if f2 and fm3 arrays are null - if (currentMonth != month || f2 == null || fm3 == null) { + if (currentMonth != month || flattenF2 == null || flattenFm3 == null) { this.month = currentMonth; // Read file @@ -617,11 +617,32 @@ private void loadsIfNeeded(final DateComponents date) { loader.loadCCIRCoefficients(date); // Update arrays - this.f2 = loader.getF2(); - this.fm3 = loader.getFm3(); + this.flattenF2 = flatten(loader.getF2()); + this.flattenFm3 = flatten(loader.getFm3()); } } + /** Flatten a 3-dimensions array. + *

    + * This method convert 3-dimensions arrays into 1-dimension arrays + * optimized to avoid cache misses when looping over all elements. + *

    + * @param original original array a[i][j][k] + * @return flatten array, for embedded loops on j (outer), k (intermediate), i (inner) + */ + private double[] flatten(final double[][][] original) { + final double[] flatten = new double[original.length * original[0].length * original[0][0].length]; + int index = 0; + for (int j = 0; j < original[0].length; j++) { + for (int k = 0; k < original[0][0].length; k++) { + for (final double[][] doubles : original) { + flatten[index++] = doubles[j][k]; + } + } + } + return flatten; + } + /** * A clipped exponential function. *

    @@ -746,7 +767,7 @@ public void loadData(final InputStream input, final String name) line = line.trim(); // Read grid data - if (line.length() > 0) { + if (!line.isEmpty()) { final String[] modip_line = SEPARATOR.split(line); for (int column = 0; column < modip_line.length; column++) { array[lineNumber - 1][column] = Double.parseDouble(modip_line[column]); @@ -891,9 +912,9 @@ public void loadData(final InputStream input, final String name) line = line.trim(); // Read grid data - if (line.length() > 0) { + if (!line.isEmpty()) { final String[] ccir_line = SEPARATOR.split(line); - for (int i = 0; i < ccir_line.length; i++) { + for (final String field : ccir_line) { if (index < NUMBER_F2_COEFFICIENTS) { // Parse F2 coefficients @@ -905,7 +926,7 @@ public void loadData(final InputStream input, final String name) currentColumnF2 = 0; currentRowF2++; } - f2Temp[currentRowF2][currentColumnF2][currentDepthF2++] = Double.parseDouble(ccir_line[i]); + f2Temp[currentRowF2][currentColumnF2][currentDepthF2++] = Double.parseDouble(field); index++; } else { // Parse Fm3 coefficients @@ -917,7 +938,7 @@ public void loadData(final InputStream input, final String name) currentColumnFm3 = 0; currentRowFm3++; } - fm3Temp[currentRowFm3][currentColumnFm3][currentDepthFm3++] = Double.parseDouble(ccir_line[i]); + fm3Temp[currentRowFm3][currentColumnFm3][currentDepthFm3++] = Double.parseDouble(field); index++; } @@ -992,23 +1013,23 @@ private static class Ray { final SinCos scLon21 = FastMath.sinCos(lon2 - lon1); // Zenith angle computation (Eq. 153 to 155) - final double cosD = scLatRec.sin() * scLatSat.sin() + - scLatRec.cos() * scLatSat.cos() * scLon21.cos(); + // with added protection against numerical noise near zenith observation + final double cosD = FastMath.min(1.0, + scLatRec.sin() * scLatSat.sin() + + scLatRec.cos() * scLatSat.cos() * scLon21.cos()); final double sinD = FastMath.sqrt(1.0 - cosD * cosD); - final double z = FastMath.atan2(sinD, cosD - (r1 / r2)); + final double z = FastMath.atan2(sinD, cosD - (r1 / r2)); + final SinCos scZ = FastMath.sinCos(z); // Ray-perigee computation in meters (Eq. 156) - this.rp = r1 * FastMath.sin(z); + this.rp = r1 * scZ.sin(); // Ray-perigee latitude and longitude if (FastMath.abs(FastMath.abs(lat1) - 0.5 * FastMath.PI) < THRESHOLD) { + // receiver is almost at North or South pole // Ray-perigee latitude (Eq. 157) - if (lat1 < 0) { - this.latP = -z; - } else { - this.latP = z; - } + this.latP = FastMath.copySign(z, lat1); // Ray-perigee longitude (Eq. 164) if (z < 0) { @@ -1017,20 +1038,24 @@ private static class Ray { this.lonP = lon2 + FastMath.PI; } + } else if (FastMath.abs(scZ.sin()) < THRESHOLD) { + // satellite is almost on receiver zenith + + this.latP = recP.getLatitude(); + this.lonP = recP.getLongitude(); + } else { // Ray-perigee latitude (Eq. 158 to 163) - final double deltaP = 0.5 * FastMath.PI - z; - final SinCos scDeltaP = FastMath.sinCos(deltaP); final double sinAz = scLon21.sin() * scLatSat.cos() / sinD; final double cosAz = (scLatSat.sin() - cosD * scLatRec.sin()) / (sinD * scLatRec.cos()); - final double sinLatP = scLatRec.sin() * scDeltaP.cos() - scLatRec.cos() * scDeltaP.sin() * cosAz; + final double sinLatP = scLatRec.sin() * scZ.sin() - scLatRec.cos() * scZ.cos() * cosAz; final double cosLatP = FastMath.sqrt(1.0 - sinLatP * sinLatP); this.latP = FastMath.atan2(sinLatP, cosLatP); // Ray-perigee longitude (Eq. 165 to 167) - final double sinLonP = -sinAz * scDeltaP.sin() / cosLatP; - final double cosLonP = (scDeltaP.cos() - scLatRec.sin() * sinLatP) / (scLatRec.cos() * cosLatP); + final double sinLonP = -sinAz * scZ.cos() / cosLatP; + final double cosLonP = (scZ.sin() - scLatRec.sin() * sinLatP) / (scLatRec.cos() * cosLatP); this.lonP = FastMath.atan2(sinLonP, cosLonP) + lon1; } @@ -1038,19 +1063,15 @@ private static class Ray { // Sine and cosine of ray-perigee latitude this.scLatP = FastMath.sinCos(latP); - final SinCos scLon = FastMath.sinCos(lon2 - lonP); - // Sine and cosine of azimuth of satellite as seen from ray-perigee - final double psi = greatCircleAngle(scLatSat, scLon); - final SinCos scPsi = FastMath.sinCos(psi); - if (FastMath.abs(FastMath.abs(latP) - 0.5 * FastMath.PI) < THRESHOLD) { + if (FastMath.abs(FastMath.abs(latP) - 0.5 * FastMath.PI) < THRESHOLD || + FastMath.abs(scZ.sin()) < THRESHOLD) { // Eq. 172 and 173 this.sinAzP = 0.0; - if (latP < 0.0) { - this.cosAzP = 1; - } else { - this.cosAzP = -1; - } + this.cosAzP = -FastMath.copySign(1, latP); } else { + final SinCos scLon = FastMath.sinCos(lon2 - lonP); + // Sine and cosine of azimuth of satellite as seen from ray-perigee + final SinCos scPsi = FastMath.sinCos(greatCircleAngle(scLatSat, scLon)); // Eq. 174 and 175 this.sinAzP = scLatSat.cos() * scLon.sin() / scPsi.sin(); this.cosAzP = (scLatSat.sin() - scLatP.sin() * scPsi.cos()) / (scLatP.cos() * scPsi.sin()); @@ -1191,25 +1212,23 @@ private static class FieldRay > { final T lon2 = satP.getLongitude(); final FieldSinCos scLatSat = FastMath.sinCos(lat2); final FieldSinCos scLatRec = FastMath.sinCos(lat1); + final FieldSinCos scLon21 = FastMath.sinCos(lon2.subtract(lon1)); // Zenith angle computation (Eq. 153 to 155) final T cosD = scLatRec.sin().multiply(scLatSat.sin()). - add(scLatRec.cos().multiply(scLatSat.cos()).multiply(FastMath.cos(lon2.subtract(lon1)))); + add(scLatRec.cos().multiply(scLatSat.cos()).multiply(scLon21.cos())); final T sinD = FastMath.sqrt(cosD.multiply(cosD).negate().add(1.0)); final T z = FastMath.atan2(sinD, cosD.subtract(r1.divide(r2))); + final FieldSinCos scZ = FastMath.sinCos(z); // Ray-perigee computation in meters (Eq. 156) - this.rp = r1.multiply(FastMath.sin(z)); + this.rp = r1.multiply(scZ.sin()); // Ray-perigee latitude and longitude if (FastMath.abs(FastMath.abs(lat1).subtract(pi.multiply(0.5)).getReal()) < THRESHOLD) { // Ray-perigee latitude (Eq. 157) - if (lat1.getReal() < 0) { - this.latP = z.negate(); - } else { - this.latP = z; - } + this.latP = FastMath.copySign(z, lat1); // Ray-perigee longitude (Eq. 164) if (z.getReal() < 0) { @@ -1218,20 +1237,24 @@ private static class FieldRay > { this.lonP = lon2.add(pi); } + } else if (FastMath.abs(scZ.sin().getReal()) < THRESHOLD) { + // satellite is almost on receiver zenith + + this.latP = recP.getLatitude(); + this.lonP = recP.getLongitude(); + } else { // Ray-perigee latitude (Eq. 158 to 163) - final T deltaP = z.negate().add(pi.multiply(0.5)); - final FieldSinCos scDeltaP = FastMath.sinCos(deltaP); final T sinAz = FastMath.sin(lon2.subtract(lon1)).multiply(scLatSat.cos()).divide(sinD); final T cosAz = scLatSat.sin().subtract(cosD.multiply(scLatRec.sin())).divide(sinD.multiply(scLatRec.cos())); - final T sinLatP = scLatRec.sin().multiply(scDeltaP.cos()).subtract(scLatRec.cos().multiply(scDeltaP.sin()).multiply(cosAz)); + final T sinLatP = scLatRec.sin().multiply(scZ.sin()).subtract(scLatRec.cos().multiply(scZ.cos()).multiply(cosAz)); final T cosLatP = FastMath.sqrt(sinLatP.multiply(sinLatP).negate().add(1.0)); this.latP = FastMath.atan2(sinLatP, cosLatP); // Ray-perigee longitude (Eq. 165 to 167) - final T sinLonP = sinAz.negate().multiply(scDeltaP.sin()).divide(cosLatP); - final T cosLonP = scDeltaP.cos().subtract(scLatRec.sin().multiply(sinLatP)).divide(scLatRec.cos().multiply(cosLatP)); + final T sinLonP = sinAz.negate().multiply(scZ.cos()).divide(cosLatP); + final T cosLonP = scZ.sin().subtract(scLatRec.sin().multiply(sinLatP)).divide(scLatRec.cos().multiply(cosLatP)); this.lonP = FastMath.atan2(sinLonP, cosLonP).add(lon1); } @@ -1239,19 +1262,15 @@ private static class FieldRay > { // Sine and cosine of ray-perigee latitude this.scLatP = FastMath.sinCos(latP); - final FieldSinCos scLon = FastMath.sinCos(lon2.subtract(lonP)); - // Sine and cosie of azimuth of satellite as seen from ray-perigee - final T psi = greatCircleAngle(scLatSat, scLon); - final FieldSinCos scPsi = FastMath.sinCos(psi); - if (FastMath.abs(FastMath.abs(latP).subtract(pi.multiply(0.5)).getReal()) < THRESHOLD) { + if (FastMath.abs(FastMath.abs(latP).subtract(pi.multiply(0.5)).getReal()) < THRESHOLD || + FastMath.abs(scZ.sin().getReal()) < THRESHOLD) { // Eq. 172 and 173 this.sinAzP = field.getZero(); - if (latP.getReal() < 0.0) { - this.cosAzP = field.getOne(); - } else { - this.cosAzP = field.getOne().negate(); - } + this.cosAzP = FastMath.copySign(field.getOne(), latP).negate(); } else { + final FieldSinCos scLon = FastMath.sinCos(lon2.subtract(lonP)); + // Sine and cosine of azimuth of satellite as seen from ray-perigee + final FieldSinCos scPsi = FastMath.sinCos(greatCircleAngle(scLatSat, scLon)); // Eq. 174 and 175 this.sinAzP = scLatSat.cos().multiply(scLon.sin()).divide(scPsi.sin()); this.cosAzP = scLatSat.sin().subtract(scLatP.sin().multiply(scPsi.cos())).divide(scLatP.cos().multiply(scPsi.sin())); @@ -1342,6 +1361,9 @@ private T greatCircleAngle(final FieldSinCos scLat, final FieldSinCos scLo /** Performs the computation of the coordinates along the integration path. */ private static class Segment { + /** Threshold for zenith segment. */ + private static final double THRESHOLD = 1.0e-3; + /** Latitudes [rad]. */ private final double[] latitudes; @@ -1368,7 +1390,7 @@ private static class Segment { this.deltaN = (s2 - s1) / n; // Segments - final double[] s = getSegments(n, s1, s2); + final double[] s = getSegments(n, s1); // Useful parameters final double rp = ray.getRadius(); @@ -1383,20 +1405,26 @@ private static class Segment { // Heights (Eq. 178) heights[i] = FastMath.sqrt(s[i] * s[i] + rp * rp) - RE; - // Great circle parameters (Eq. 179 to 181) - final double tanDs = s[i] / rp; - final double cosDs = 1.0 / FastMath.sqrt(1.0 + tanDs * tanDs); - final double sinDs = tanDs * cosDs; - - // Latitude (Eq. 182 to 183) - final double sinLatS = scLatP.sin() * cosDs + scLatP.cos() * sinDs * ray.getCosineAz(); - final double cosLatS = FastMath.sqrt(1.0 - sinLatS * sinLatS); - latitudes[i] = FastMath.atan2(sinLatS, cosLatS); - - // Longitude (Eq. 184 to 187) - final double sinLonS = sinDs * ray.getSineAz() * scLatP.cos(); - final double cosLonS = cosDs - scLatP.sin() * sinLatS; - longitudes[i] = FastMath.atan2(sinLonS, cosLonS) + ray.getLongitude(); + if (rp < THRESHOLD) { + // zenith segment + latitudes[i] = ray.getLatitude(); + longitudes[i] = ray.getLongitude(); + } else { + // Great circle parameters (Eq. 179 to 181) + final double tanDs = s[i] / rp; + final double cosDs = 1.0 / FastMath.sqrt(1.0 + tanDs * tanDs); + final double sinDs = tanDs * cosDs; + + // Latitude (Eq. 182 to 183) + final double sinLatS = scLatP.sin() * cosDs + scLatP.cos() * sinDs * ray.getCosineAz(); + final double cosLatS = FastMath.sqrt(1.0 - sinLatS * sinLatS); + latitudes[i] = FastMath.atan2(sinLatS, cosLatS); + + // Longitude (Eq. 184 to 187) + final double sinLonS = sinDs * ray.getSineAz() * scLatP.cos(); + final double cosLonS = cosDs - scLatP.sin() * sinLatS; + longitudes[i] = FastMath.atan2(sinLonS, cosLonS) + ray.getLongitude(); + } } } @@ -1404,10 +1432,9 @@ private static class Segment { * Computes the distance of a point from the ray-perigee. * @param n number of points used for the integration * @param s1 lower boundary - * @param s2 upper boundary * @return the distance of a point from the ray-perigee in km */ - private double[] getSegments(final int n, final double s1, final double s2) { + private double[] getSegments(final int n, final double s1) { // Eq. 196 final double g = 0.5773502691896 * deltaN; // Eq. 197 @@ -1460,6 +1487,9 @@ public double getInterval() { /** Performs the computation of the coordinates along the integration path. */ private static class FieldSegment > { + /** Threshold for zenith segment. */ + private static final double THRESHOLD = 1.0e-3; + /** Latitudes [rad]. */ private final T[] latitudes; @@ -1487,7 +1517,7 @@ private static class FieldSegment > { this.deltaN = s2.subtract(s1).divide(n); // Segments - final T[] s = getSegments(field, n, s1, s2); + final T[] s = getSegments(field, n, s1); // Useful parameters final T rp = ray.getRadius(); @@ -1502,20 +1532,26 @@ private static class FieldSegment > { // Heights (Eq. 178) heights[i] = FastMath.sqrt(s[i].multiply(s[i]).add(rp.multiply(rp))).subtract(RE); - // Great circle parameters (Eq. 179 to 181) - final T tanDs = s[i].divide(rp); - final T cosDs = FastMath.sqrt(tanDs.multiply(tanDs).add(1.0)).reciprocal(); - final T sinDs = tanDs.multiply(cosDs); - - // Latitude (Eq. 182 to 183) - final T sinLatS = scLatP.sin().multiply(cosDs).add(scLatP.cos().multiply(sinDs).multiply(ray.getCosineAz())); - final T cosLatS = FastMath.sqrt(sinLatS.multiply(sinLatS).negate().add(1.0)); - latitudes[i] = FastMath.atan2(sinLatS, cosLatS); - - // Longitude (Eq. 184 to 187) - final T sinLonS = sinDs.multiply(ray.getSineAz()).multiply(scLatP.cos()); - final T cosLonS = cosDs.subtract(scLatP.sin().multiply(sinLatS)); - longitudes[i] = FastMath.atan2(sinLonS, cosLonS).add(ray.getLongitude()); + if (rp.getReal() < THRESHOLD) { + // zenith segment + latitudes[i] = ray.getLatitude(); + longitudes[i] = ray.getLongitude(); + } else { + // Great circle parameters (Eq. 179 to 181) + final T tanDs = s[i].divide(rp); + final T cosDs = FastMath.sqrt(tanDs.multiply(tanDs).add(1.0)).reciprocal(); + final T sinDs = tanDs.multiply(cosDs); + + // Latitude (Eq. 182 to 183) + final T sinLatS = scLatP.sin().multiply(cosDs).add(scLatP.cos().multiply(sinDs).multiply(ray.getCosineAz())); + final T cosLatS = FastMath.sqrt(sinLatS.multiply(sinLatS).negate().add(1.0)); + latitudes[i] = FastMath.atan2(sinLatS, cosLatS); + + // Longitude (Eq. 184 to 187) + final T sinLonS = sinDs.multiply(ray.getSineAz()).multiply(scLatP.cos()); + final T cosLonS = cosDs.subtract(scLatP.sin().multiply(sinLatS)); + longitudes[i] = FastMath.atan2(sinLonS, cosLonS).add(ray.getLongitude()); + } } } @@ -1524,10 +1560,9 @@ private static class FieldSegment > { * @param field field of the elements * @param n number of points used for the integration * @param s1 lower boundary - * @param s2 upper boundary * @return the distance of a point from the ray-perigee in km */ - private T[] getSegments(final Field field, final int n, final T s1, final T s2) { + private T[] getSegments(final Field field, final int n, final T s1) { // Eq. 196 final T g = deltaN.multiply(0.5773502691896); // Eq. 197 diff --git a/src/main/java/org/orekit/models/earth/ionosphere/NeQuickParameters.java b/src/main/java/org/orekit/models/earth/ionosphere/NeQuickParameters.java index 1ad4b57e68..97042983e8 100644 --- a/src/main/java/org/orekit/models/earth/ionosphere/NeQuickParameters.java +++ b/src/main/java/org/orekit/models/earth/ionosphere/NeQuickParameters.java @@ -17,7 +17,6 @@ package org.orekit.models.earth.ionosphere; import org.hipparchus.util.FastMath; -import org.hipparchus.util.MathArrays; import org.hipparchus.util.SinCos; import org.orekit.time.DateComponents; import org.orekit.time.DateTimeComponents; @@ -74,15 +73,16 @@ class NeQuickParameters { /** * Build a new instance. * @param dateTime current date time components - * @param f2 F2 coefficients used by the F2 layer - * @param fm3 Fm3 coefficients used by the F2 layer + * @param flattenF2 F2 coefficients used by the F2 layer (flatten array) + * @param flattenFm3 Fm3 coefficients used by the F2 layer (flatten array) * @param latitude latitude of a point along the integration path, in radians * @param longitude longitude of a point along the integration path, in radians * @param alpha effective ionisation level coefficients * @param modipGrip modip grid */ - NeQuickParameters(final DateTimeComponents dateTime, final double[][][] f2, - final double[][][] fm3, final double latitude, final double longitude, + NeQuickParameters(final DateTimeComponents dateTime, + final double[] flattenF2, final double[] flattenFm3, + final double latitude, final double longitude, final double[] alpha, final double[][] modipGrip) { // MODIP in degrees @@ -99,23 +99,6 @@ class NeQuickParameters { // Effective solar zenith angle in radians final double xeff = computeEffectiveSolarAngle(date.getMonth(), hours, latitude, longitude); - // Coefficients for F2 layer parameters - // Compute the array of interpolated coefficients for foF2 (Eq. 44) - final double[][] af2 = new double[76][13]; - for (int j = 0; j < 76; j++) { - for (int k = 0; k < 13; k++ ) { - af2[j][k] = f2[0][j][k] * (1.0 - (azr * 0.01)) + f2[1][j][k] * (azr * 0.01); - } - } - - // Compute the array of interpolated coefficients for M(3000)F2 (Eq. 46) - final double[][] am3 = new double[49][9]; - for (int j = 0; j < 49; j++) { - for (int k = 0; k < 9; k++ ) { - am3[j][k] = fm3[0][j][k] * (1.0 - (azr * 0.01)) + fm3[1][j][k] * (azr * 0.01); - } - } - // E layer maximum density height in km (Eq. 78) this.hmE = 120.0; // E layer critical frequency in MHz @@ -126,12 +109,14 @@ class NeQuickParameters { // Time argument (Eq. 49) final double t = FastMath.toRadians(15 * hours) - FastMath.PI; // Compute Fourier time series for foF2 and M(3000)F2 - final double[] cf2 = computeCF2(af2, t); - final double[] cm3 = computeCm3(am3, t); + final double[] scT = sinCos(t, 6); + final double[] cf2 = computeCF2(flattenF2, azr, scT); + final double[] cm3 = computeCm3(flattenFm3, azr, scT); // F2 layer critical frequency in MHz - final double foF2 = computefoF2(modip, cf2, latitude, longitude); + final double[] scL = sinCos(longitude, 8); + final double foF2 = computefoF2(modip, cf2, latitude, scL); // Maximum Usable Frequency factor - final double mF2 = computeMF2(modip, cm3, latitude, longitude); + final double mF2 = computeMF2(modip, cm3, latitude, scL); // F2 layer maximum density in 10^11 m-3 this.nmF2 = 0.124 * foF2 * foF2; // F2 layer maximum density height in km @@ -309,9 +294,8 @@ private double computeMODIP(final double lat, final double lon, final double[][] final double y = b - bF; // MODIP (Ref Eq. 16) - final double modip = interpolate(z1, z2, z3, z4, y); + return interpolate(z1, z2, z3, z4, y); - return modip; } /** @@ -404,8 +388,8 @@ private double computefoE(final int month, final double az, final double seasp = seas * ((ee - 1.0) / (ee + 1.0)); // Critical frequency (Eq. 35) final double coef = 1.112 - 0.019 * seasp; - final double foE = FastMath.sqrt(coef * coef * sqAz * FastMath.pow(FastMath.cos(xeff), 0.6) + 0.49); - return foE; + return FastMath.sqrt(coef * coef * sqAz * FastMath.pow(FastMath.cos(xeff), 0.6) + 0.49); + } /** @@ -429,46 +413,96 @@ private double computehmF2(final double foE, final double foF2, final double mF2 // hmF2 Eq. 80 final double mF2Sq = mF2 * mF2; final double temp = FastMath.sqrt((0.0196 * mF2Sq + 1) / (1.2967 * mF2Sq - 1.0)); - final double height = ((1490.0 * mF2 * temp) / (mF2 + deltaM)) - 176.0; - return height; + return ((1490.0 * mF2 * temp) / (mF2 + deltaM)) - 176.0; + + } + + /** Compute sines and cosines. + * @param a argument + * @param n number of terms + * @return sin(a), cos(a), sin(2a), cos(2a) … sin(n a), cos(n a) array + * @since 12.1.3 + */ + private double[] sinCos(final double a, final int n) { + + final SinCos sc0 = FastMath.sinCos(a); + SinCos sci = sc0; + final double[] sc = new double[2 * n]; + int isc = 0; + sc[isc++] = sci.sin(); + sc[isc++] = sci.cos(); + for (int i = 1; i < n; i++) { + sci = SinCos.sum(sc0, sci); + sc[isc++] = sci.sin(); + sc[isc++] = sci.cos(); + } + + return sc; + } /** * Computes cf2 coefficients. - * @param af2 interpolated coefficients for foF2 - * @param t time argument + * @param flattenF2 F2 coefficients used by the F2 layer (flatten array) + * @param azr effective sunspot number (Eq. 19) + * @param scT sines/cosines array of time argument * @return the cf2 coefficients array */ - private double[] computeCF2(final double[][] af2, final double t) { - // Eq. 50 + private double[] computeCF2(final double[] flattenF2, final double azr, final double[] scT) { + + // interpolation coefficients for effective spot number + final double azr01 = azr * 0.01; + final double omazr01 = 1 - azr01; + + // Eq. 44 and Eq. 50 merged into one loop final double[] cf2 = new double[76]; + int index = 0; for (int i = 0; i < cf2.length; i++) { - double sum = 0.0; - for (int k = 0; k < 6; k++) { - final SinCos sc = FastMath.sinCos((k + 1) * t); - sum += af2[i][2 * k + 1] * sc.sin() + af2[i][2 * (k + 1)] * sc.cos(); - } - cf2[i] = af2[i][0] + sum; + cf2[i] = omazr01 * flattenF2[index ] + azr01 * flattenF2[index + 1] + + (omazr01 * flattenF2[index + 2] + azr01 * flattenF2[index + 3]) * scT[ 0] + + (omazr01 * flattenF2[index + 4] + azr01 * flattenF2[index + 5]) * scT[ 1] + + (omazr01 * flattenF2[index + 6] + azr01 * flattenF2[index + 7]) * scT[ 2] + + (omazr01 * flattenF2[index + 8] + azr01 * flattenF2[index + 9]) * scT[ 3] + + (omazr01 * flattenF2[index + 10] + azr01 * flattenF2[index + 11]) * scT[ 4] + + (omazr01 * flattenF2[index + 12] + azr01 * flattenF2[index + 13]) * scT[ 5] + + (omazr01 * flattenF2[index + 14] + azr01 * flattenF2[index + 15]) * scT[ 6] + + (omazr01 * flattenF2[index + 16] + azr01 * flattenF2[index + 17]) * scT[ 7] + + (omazr01 * flattenF2[index + 18] + azr01 * flattenF2[index + 19]) * scT[ 8] + + (omazr01 * flattenF2[index + 20] + azr01 * flattenF2[index + 21]) * scT[ 9] + + (omazr01 * flattenF2[index + 22] + azr01 * flattenF2[index + 23]) * scT[10] + + (omazr01 * flattenF2[index + 24] + azr01 * flattenF2[index + 25]) * scT[11]; + index += 26; } return cf2; } /** * Computes Cm3 coefficients. - * @param am3 interpolated coefficients for foF2 - * @param t time argument + * @param flattenFm3 Fm3 coefficients used by the F2 layer (flatten array) + * @param azr effective sunspot number (Eq. 19) + * @param scT sines/cosines array of time argument * @return the Cm3 coefficients array */ - private double[] computeCm3(final double[][] am3, final double t) { - // Eq. 51 + private double[] computeCm3(final double[] flattenFm3, final double azr, final double[] scT) { + + // interpolation coefficients for effective spot number + final double azr01 = azr * 0.01; + final double omazr01 = 1 - azr01; + + // Eq. 44 and Eq. 51 merged into one loop final double[] cm3 = new double[49]; + int index = 0; for (int i = 0; i < cm3.length; i++) { - double sum = 0.0; - for (int k = 0; k < 4; k++) { - final SinCos sc = FastMath.sinCos((k + 1) * t); - sum += am3[i][2 * k + 1] * sc.sin() + am3[i][2 * (k + 1)] * sc.cos(); - } - cm3[i] = am3[i][0] + sum; + cm3[i] = omazr01 * flattenFm3[index ] + azr01 * flattenFm3[index + 1] + + (omazr01 * flattenFm3[index + 2] + azr01 * flattenFm3[index + 3]) * scT[ 0] + + (omazr01 * flattenFm3[index + 4] + azr01 * flattenFm3[index + 5]) * scT[ 1] + + (omazr01 * flattenFm3[index + 6] + azr01 * flattenFm3[index + 7]) * scT[ 2] + + (omazr01 * flattenFm3[index + 8] + azr01 * flattenFm3[index + 9]) * scT[ 3] + + (omazr01 * flattenFm3[index + 10] + azr01 * flattenFm3[index + 11]) * scT[ 4] + + (omazr01 * flattenFm3[index + 12] + azr01 * flattenFm3[index + 13]) * scT[ 5] + + (omazr01 * flattenFm3[index + 14] + azr01 * flattenFm3[index + 15]) * scT[ 6] + + (omazr01 * flattenFm3[index + 16] + azr01 * flattenFm3[index + 17]) * scT[ 7]; + index += 18; } return cm3; } @@ -478,20 +512,18 @@ private double[] computeCm3(final double[][] am3, final double t) { * @param modip modified DIP latitude, in degrees * @param cf2 Fourier time series for foF2 * @param latitude latitude in radians - * @param longitude longitude in radians + * @param scL sines/cosines array of longitude argument * @return the F2 layer critical frequency, MHz */ private double computefoF2(final double modip, final double[] cf2, - final double latitude, final double longitude) { + final double latitude, final double[] scL) { // Legendre grades (Eq. 63) final int[] q = new int[] { 12, 12, 9, 5, 2, 1, 1, 1, 1 }; - // Array for geographic terms - final double[] g = new double[cf2.length]; - g[0] = 1.0; + double frequency = cf2[0]; // MODIP coefficients Eq. 57 final double sinMODIP = FastMath.sin(FastMath.toRadians(modip)); @@ -499,30 +531,25 @@ private double computefoF2(final double modip, final double[] cf2, m[0] = 1.0; for (int i = 1; i < q[0]; i++) { m[i] = sinMODIP * m[i - 1]; - g[i] = m[i]; - } - - // Latitude coefficients (Eq. 58) - final double cosLat = FastMath.cos(latitude); - final double[] p = new double[8]; - p[0] = cosLat; - for (int n = 2; n < 9; n++) { - p[n - 1] = cosLat * p[n - 2]; + frequency += m[i] * cf2[i]; } // latitude and longitude terms int index = 12; + final double cosLat1 = FastMath.cos(latitude); + double cosLatI = cosLat1; for (int i = 1; i < q.length; i++) { - final SinCos sc = FastMath.sinCos(i * longitude); + final double c = cosLatI * scL[2 * i - 1]; + final double s = cosLatI * scL[2 * i - 2]; for (int j = 0; j < q[i]; j++) { - g[index++] = m[j] * p[i - 1] * sc.cos(); - g[index++] = m[j] * p[i - 1] * sc.sin(); + frequency += m[j] * c * cf2[index++]; + frequency += m[j] * s * cf2[index++]; } + cosLatI *= cosLat1; // Eq. 58 } - // Compute foF2 by linear combination - final double frequency = MathArrays.linearCombination(cf2, g); return frequency; + } /** @@ -530,20 +557,18 @@ private double computefoF2(final double modip, final double[] cf2, * @param modip modified DIP latitude, in degrees * @param cm3 Fourier time series for M(3000)F2 * @param latitude latitude in radians - * @param longitude longitude in radians + * @param scL sines/cosines array of longitude argument * @return the Maximum Usable Frequency factor */ private double computeMF2(final double modip, final double[] cm3, - final double latitude, final double longitude) { + final double latitude, final double[] scL) { // Legendre grades (Eq. 71) final int[] r = new int[] { 7, 8, 6, 3, 2, 1, 1 }; - // Array for geographic terms - final double[] g = new double[cm3.length]; - g[0] = 1.0; + double m3000 = cm3[0]; // MODIP coefficients Eq. 57 final double sinMODIP = FastMath.sin(FastMath.toRadians(modip)); @@ -552,31 +577,26 @@ private double computeMF2(final double modip, final double[] cm3, for (int i = 1; i < 12; i++) { m[i] = sinMODIP * m[i - 1]; if (i < 7) { - g[i] = m[i]; + m3000 += m[i] * cm3[i]; } } - // Latitude coefficients (Eq. 58) - final double cosLat = FastMath.cos(latitude); - final double[] p = new double[8]; - p[0] = cosLat; - for (int n = 2; n < 9; n++) { - p[n - 1] = cosLat * p[n - 2]; - } - // latitude and longitude terms int index = 7; + final double cosLat1 = FastMath.cos(latitude); + double cosLatI = cosLat1; for (int i = 1; i < r.length; i++) { - final SinCos sc = FastMath.sinCos(i * longitude); + final double c = cosLatI * scL[2 * i - 1]; + final double s = cosLatI * scL[2 * i - 2]; for (int j = 0; j < r[i]; j++) { - g[index++] = m[j] * p[i - 1] * sc.cos(); - g[index++] = m[j] * p[i - 1] * sc.sin(); + m3000 += m[j] * c * cm3[index++]; + m3000 += m[j] * s * cm3[index++]; } + cosLatI *= cosLat1; // Eq. 58 } - // Compute m3000 by linear combination - final double m3000 = MathArrays.linearCombination(g, cm3); return m3000; + } /** @@ -674,8 +694,8 @@ private double computeH0(final int month, final double azr) { final double v = (0.041163 * x - 0.183981) * x + 1.424472; // Topside thickness parameter (Eq. 106) - final double h = hA / v; - return h; + return hA / v; + } /** @@ -725,9 +745,8 @@ private double interpolate(final double z1, final double z2, final double a1 = 9.0 * g2 - g4; final double a2 = g3 - g1; final double a3 = g4 - g2; - final double zx = 0.0625 * (a0 + delta * (a1 + delta * (a2 + delta * a3))); + return 0.0625 * (a0 + delta * (a1 + delta * (a2 + delta * a3))); - return zx; } /** @@ -765,8 +784,8 @@ private double epst(final double x, final double y, final double z, final double w) { final double ex = clipExp((w - y) / z); final double opex = 1.0 + ex; - final double epst = x * ex / (opex * opex); - return epst; + return x * ex / (opex * opex); + } } diff --git a/src/main/java/org/orekit/models/earth/troposphere/EstimatedModel.java b/src/main/java/org/orekit/models/earth/troposphere/EstimatedModel.java index 5348320ac2..01bee66f92 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/EstimatedModel.java +++ b/src/main/java/org/orekit/models/earth/troposphere/EstimatedModel.java @@ -52,7 +52,7 @@ * the {@link NiellMappingFunctionModel Niell Mapping Function} *

    * The tropospheric zenith delay δh is computed empirically with a - * {@link DiscreteTroposphericModel tropospheric model} + * {@link TroposphericModel tropospheric model} * while the tropospheric total zenith delay δt is estimated as a {@link ParameterDriver}, * hence the wet part is the difference between the two. * @since 12.1 diff --git a/src/main/java/org/orekit/orbits/CircularLatitudeArgumentUtility.java b/src/main/java/org/orekit/orbits/CircularLatitudeArgumentUtility.java index 5733e2ba5a..91b097156d 100644 --- a/src/main/java/org/orekit/orbits/CircularLatitudeArgumentUtility.java +++ b/src/main/java/org/orekit/orbits/CircularLatitudeArgumentUtility.java @@ -19,6 +19,7 @@ import org.hipparchus.util.FastMath; import org.hipparchus.util.SinCos; import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; /** @@ -172,4 +173,48 @@ public static double meanToTrue(final double ex, final double ey, final double a return eccentricToTrue(ex, ey, alphaE); } + /** + * Convert argument of latitude. + * @param oldType old position angle type + * @param alpha old value for argument of latitude + * @param ex ex + * @param ey ey + * @param newType new position angle type + * @return convert argument of latitude + * @since 12.2 + */ + public static double convertAlpha(final PositionAngleType oldType, final double alpha, final double ex, + final double ey, final PositionAngleType newType) { + if (oldType == newType) { + return alpha; + + } else { + switch (newType) { + + case ECCENTRIC: + if (oldType == PositionAngleType.MEAN) { + return CircularLatitudeArgumentUtility.meanToEccentric(ex, ey, alpha); + } else { + return CircularLatitudeArgumentUtility.trueToEccentric(ex, ey, alpha); + } + + case MEAN: + if (oldType == PositionAngleType.TRUE) { + return CircularLatitudeArgumentUtility.trueToMean(ex, ey, alpha); + } else { + return CircularLatitudeArgumentUtility.eccentricToMean(ex, ey, alpha); + } + + case TRUE: + if (oldType == PositionAngleType.MEAN) { + return CircularLatitudeArgumentUtility.meanToTrue(ex, ey, alpha); + } else { + return CircularLatitudeArgumentUtility.eccentricToTrue(ex, ey, alpha); + } + + default: + throw new OrekitInternalError(null); + } + } + } } diff --git a/src/main/java/org/orekit/orbits/CircularOrbit.java b/src/main/java/org/orekit/orbits/CircularOrbit.java index 2405d81bf2..8e0ddac047 100644 --- a/src/main/java/org/orekit/orbits/CircularOrbit.java +++ b/src/main/java/org/orekit/orbits/CircularOrbit.java @@ -1032,37 +1032,7 @@ private UnivariateDerivative1 initializeCachedAlpha(final double alpha, final do * @since 12.1 */ private double initializeCachedAlpha(final double alpha, final PositionAngleType positionAngleType) { - if (positionAngleType == cachedPositionAngleType) { - return alpha; - - } else { - switch (cachedPositionAngleType) { - - case ECCENTRIC: - if (positionAngleType == PositionAngleType.MEAN) { - return CircularLatitudeArgumentUtility.meanToEccentric(ex, ey, alpha); - } else { - return CircularLatitudeArgumentUtility.trueToEccentric(ex, ey, alpha); - } - - case MEAN: - if (positionAngleType == PositionAngleType.TRUE) { - return CircularLatitudeArgumentUtility.trueToMean(ex, ey, alpha); - } else { - return CircularLatitudeArgumentUtility.eccentricToMean(ex, ey, alpha); - } - - case TRUE: - if (positionAngleType == PositionAngleType.MEAN) { - return CircularLatitudeArgumentUtility.meanToTrue(ex, ey, alpha); - } else { - return CircularLatitudeArgumentUtility.eccentricToTrue(ex, ey, alpha); - } - - default: - throw new OrekitInternalError(null); - } - } + return CircularLatitudeArgumentUtility.convertAlpha(positionAngleType, alpha, ex, ey, cachedPositionAngleType); } /** Compute non-Keplerian part of the acceleration from first time derivatives. @@ -1402,27 +1372,39 @@ protected double[][] computeJacobianTrueWrtCartesian() { @Override public void addKeplerContribution(final PositionAngleType type, final double gm, final double[] pDot) { - final double oMe2; + pDot[5] += computeKeplerianAlphaDot(type, a, ex, ey, gm, cachedAlpha, cachedPositionAngleType); + } + + /** + * Compute rate of argument of latitude. + * @param type position angle type of rate + * @param a semi major axis + * @param ex ex + * @param ey ey + * @param mu mu + * @param alpha argument of latitude + * @param cachedType position angle type of passed alpha + * @return first-order time derivative for alpha + * @since 12.2 + */ + private static double computeKeplerianAlphaDot(final PositionAngleType type, final double a, final double ex, + final double ey, final double mu, + final double alpha, final PositionAngleType cachedType) { + final double n = FastMath.sqrt(mu / a) / a; + if (type == PositionAngleType.MEAN) { + return n; + } final double ksi; - final double n = FastMath.sqrt(gm / a) / a; final SinCos sc; - switch (type) { - case MEAN : - pDot[5] += n; - break; - case ECCENTRIC : - sc = FastMath.sinCos(getAlphaE()); - ksi = 1. / (1 - ex * sc.cos() - ey * sc.sin()); - pDot[5] += n * ksi; - break; - case TRUE : - sc = FastMath.sinCos(getAlphaV()); - oMe2 = 1 - ex * ex - ey * ey; - ksi = 1 + ex * sc.cos() + ey * sc.sin(); - pDot[5] += n * ksi * ksi / (oMe2 * FastMath.sqrt(oMe2)); - break; - default : - throw new OrekitInternalError(null); + if (type == PositionAngleType.ECCENTRIC) { + sc = FastMath.sinCos(CircularLatitudeArgumentUtility.convertAlpha(cachedType, alpha, ex, ey, type)); + ksi = 1. / (1 - ex * sc.cos() - ey * sc.sin()); + return n * ksi; + } else { // TRUE + sc = FastMath.sinCos(CircularLatitudeArgumentUtility.convertAlpha(cachedType, alpha, ex, ey, type)); + final double oMe2 = 1 - ex * ex - ey * ey; + ksi = 1 + ex * sc.cos() + ey * sc.sin(); + return n * ksi * ksi / (oMe2 * FastMath.sqrt(oMe2)); } } diff --git a/src/main/java/org/orekit/orbits/EquinoctialLongitudeArgumentUtility.java b/src/main/java/org/orekit/orbits/EquinoctialLongitudeArgumentUtility.java index 68002b02b7..98805410e5 100644 --- a/src/main/java/org/orekit/orbits/EquinoctialLongitudeArgumentUtility.java +++ b/src/main/java/org/orekit/orbits/EquinoctialLongitudeArgumentUtility.java @@ -19,6 +19,7 @@ import org.hipparchus.util.FastMath; import org.hipparchus.util.SinCos; import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; /** @@ -172,4 +173,48 @@ public static double meanToTrue(final double ex, final double ey, final double l return eccentricToTrue(ex, ey, alphaE); } + /** + * Convert argument of longitude. + * @param oldType old position angle type + * @param l old value for argument of longitude + * @param ex ex + * @param ey ey + * @param newType new position angle type + * @return converted argument of longitude + * @since 12.2 + */ + public static double convertL(final PositionAngleType oldType, final double l, final double ex, + final double ey, final PositionAngleType newType) { + if (oldType == newType) { + return l; + + } else { + switch (newType) { + + case ECCENTRIC: + if (oldType == PositionAngleType.MEAN) { + return EquinoctialLongitudeArgumentUtility.meanToEccentric(ex, ey, l); + } else { + return EquinoctialLongitudeArgumentUtility.trueToEccentric(ex, ey, l); + } + + case MEAN: + if (oldType == PositionAngleType.TRUE) { + return EquinoctialLongitudeArgumentUtility.trueToMean(ex, ey, l); + } else { + return EquinoctialLongitudeArgumentUtility.eccentricToMean(ex, ey, l); + } + + case TRUE: + if (oldType == PositionAngleType.MEAN) { + return EquinoctialLongitudeArgumentUtility.meanToTrue(ex, ey, l); + } else { + return EquinoctialLongitudeArgumentUtility.eccentricToTrue(ex, ey, l); + } + + default: + throw new OrekitInternalError(null); + } + } + } } diff --git a/src/main/java/org/orekit/orbits/EquinoctialOrbit.java b/src/main/java/org/orekit/orbits/EquinoctialOrbit.java index 7b7fb0feac..f9a695608d 100644 --- a/src/main/java/org/orekit/orbits/EquinoctialOrbit.java +++ b/src/main/java/org/orekit/orbits/EquinoctialOrbit.java @@ -217,9 +217,9 @@ public EquinoctialOrbit(final double a, final double ex, final double ey, this.hyDot = hyDot; if (hasDerivatives()) { - final UnivariateDerivative1 alphaUD = initializeCachedL(l, lDot, type); - this.cachedL = alphaUD.getValue(); - this.cachedLDot = alphaUD.getFirstDerivative(); + final UnivariateDerivative1 lUD = initializeCachedL(l, lDot, type); + this.cachedL = lUD.getValue(); + this.cachedLDot = lUD.getFirstDerivative(); } else { this.cachedL = initializeCachedL(l, type); this.cachedLDot = Double.NaN; @@ -807,37 +807,7 @@ private UnivariateDerivative1 initializeCachedL(final double l, final double lDo * @since 12.1 */ private double initializeCachedL(final double l, final PositionAngleType positionAngleType) { - if (positionAngleType == cachedPositionAngleType) { - return l; - - } else { - switch (cachedPositionAngleType) { - - case ECCENTRIC: - if (positionAngleType == PositionAngleType.MEAN) { - return EquinoctialLongitudeArgumentUtility.meanToEccentric(ex, ey, l); - } else { - return EquinoctialLongitudeArgumentUtility.trueToEccentric(ex, ey, l); - } - - case MEAN: - if (positionAngleType == PositionAngleType.TRUE) { - return EquinoctialLongitudeArgumentUtility.trueToMean(ex, ey, l); - } else { - return EquinoctialLongitudeArgumentUtility.eccentricToMean(ex, ey, l); - } - - case TRUE: - if (positionAngleType == PositionAngleType.MEAN) { - return EquinoctialLongitudeArgumentUtility.meanToTrue(ex, ey, l); - } else { - return EquinoctialLongitudeArgumentUtility.eccentricToTrue(ex, ey, l); - } - - default: - throw new OrekitInternalError(null); - } - } + return EquinoctialLongitudeArgumentUtility.convertL(positionAngleType, l, ex, ey, cachedPositionAngleType); } /** Compute non-Keplerian part of the acceleration from first time derivatives. @@ -1131,27 +1101,40 @@ protected double[][] computeJacobianTrueWrtCartesian() { @Override public void addKeplerContribution(final PositionAngleType type, final double gm, final double[] pDot) { + pDot[5] += computeKeplerianLDot(type, a, ex, ey, gm, cachedL, cachedPositionAngleType); + } + + /** + * Compute rate of argument of longitude. + * @param type position angle type of rate + * @param a semi major axis + * @param ex ex + * @param ey ey + * @param mu mu + * @param l argument of longitude + * @param cachedType position angle type of passed l + * @return first-order time derivative for l + * @since 12.2 + */ + private static double computeKeplerianLDot(final PositionAngleType type, final double a, final double ex, + final double ey, final double mu, + final double l, final PositionAngleType cachedType) { + final double n = FastMath.sqrt(mu / a) / a; + if (type == PositionAngleType.MEAN) { + return n; + } final double oMe2; final double ksi; - final double n = FastMath.sqrt(gm / a) / a; final SinCos sc; - switch (type) { - case MEAN : - pDot[5] += n; - break; - case ECCENTRIC : - sc = FastMath.sinCos(getLE()); - ksi = 1. / (1 - ex * sc.cos() - ey * sc.sin()); - pDot[5] += n * ksi; - break; - case TRUE : - sc = FastMath.sinCos(getLv()); - oMe2 = 1 - ex * ex - ey * ey; - ksi = 1 + ex * sc.cos() + ey * sc.sin(); - pDot[5] += n * ksi * ksi / (oMe2 * FastMath.sqrt(oMe2)); - break; - default : - throw new OrekitInternalError(null); + if (type == PositionAngleType.ECCENTRIC) { + sc = FastMath.sinCos(EquinoctialLongitudeArgumentUtility.convertL(cachedType, l, ex, ey, type)); + ksi = 1. / (1 - ex * sc.cos() - ey * sc.sin()); + return n * ksi; + } else { // TRUE + sc = FastMath.sinCos(EquinoctialLongitudeArgumentUtility.convertL(cachedType, l, ex, ey, type)); + oMe2 = 1 - ex * ex - ey * ey; + ksi = 1 + ex * sc.cos() + ey * sc.sin(); + return n * ksi * ksi / (oMe2 * FastMath.sqrt(oMe2)); } } diff --git a/src/main/java/org/orekit/orbits/FieldCircularLatitudeArgumentUtility.java b/src/main/java/org/orekit/orbits/FieldCircularLatitudeArgumentUtility.java index b98dc91885..fd1302dc1b 100644 --- a/src/main/java/org/orekit/orbits/FieldCircularLatitudeArgumentUtility.java +++ b/src/main/java/org/orekit/orbits/FieldCircularLatitudeArgumentUtility.java @@ -20,6 +20,7 @@ import org.hipparchus.util.FastMath; import org.hipparchus.util.FieldSinCos; import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; /** @@ -186,4 +187,50 @@ public static > T meanToTrue(final T ex, final return eccentricToTrue(ex, ey, alphaE); } + /** + * Convert argument of latitude. + * @param oldType old position angle type + * @param alpha old value for argument of latitude + * @param ex ex + * @param ey ey + * @param newType new position angle type + * @param field type + * @return convert argument of latitude + * @since 12.2 + */ + public static > T convertAlpha(final PositionAngleType oldType, final T alpha, + final T ex, final T ey, + final PositionAngleType newType) { + if (oldType == newType) { + return alpha; + + } else { + switch (newType) { + + case ECCENTRIC: + if (oldType == PositionAngleType.MEAN) { + return FieldCircularLatitudeArgumentUtility.meanToEccentric(ex, ey, alpha); + } else { + return FieldCircularLatitudeArgumentUtility.trueToEccentric(ex, ey, alpha); + } + + case MEAN: + if (oldType == PositionAngleType.TRUE) { + return FieldCircularLatitudeArgumentUtility.trueToMean(ex, ey, alpha); + } else { + return FieldCircularLatitudeArgumentUtility.eccentricToMean(ex, ey, alpha); + } + + case TRUE: + if (oldType == PositionAngleType.MEAN) { + return FieldCircularLatitudeArgumentUtility.meanToTrue(ex, ey, alpha); + } else { + return FieldCircularLatitudeArgumentUtility.eccentricToTrue(ex, ey, alpha); + } + + default: + throw new OrekitInternalError(null); + } + } + } } diff --git a/src/main/java/org/orekit/orbits/FieldCircularOrbit.java b/src/main/java/org/orekit/orbits/FieldCircularOrbit.java index 2d123e407c..cf1d603ff7 100644 --- a/src/main/java/org/orekit/orbits/FieldCircularOrbit.java +++ b/src/main/java/org/orekit/orbits/FieldCircularOrbit.java @@ -1062,37 +1062,7 @@ private FieldUnivariateDerivative1 initializeCachedAlpha(final T alpha, final * @since 12.1 */ private T initializeCachedAlpha(final T alpha, final PositionAngleType positionAngleType) { - if (positionAngleType == cachedPositionAngleType) { - return alpha; - - } else { - switch (cachedPositionAngleType) { - - case ECCENTRIC: - if (positionAngleType == PositionAngleType.MEAN) { - return FieldCircularLatitudeArgumentUtility.meanToEccentric(ex, ey, alpha); - } else { - return FieldCircularLatitudeArgumentUtility.trueToEccentric(ex, ey, alpha); - } - - case MEAN: - if (positionAngleType == PositionAngleType.TRUE) { - return FieldCircularLatitudeArgumentUtility.trueToMean(ex, ey, alpha); - } else { - return FieldCircularLatitudeArgumentUtility.eccentricToMean(ex, ey, alpha); - } - - case TRUE: - if (positionAngleType == PositionAngleType.MEAN) { - return FieldCircularLatitudeArgumentUtility.meanToTrue(ex, ey, alpha); - } else { - return FieldCircularLatitudeArgumentUtility.eccentricToTrue(ex, ey, alpha); - } - - default: - throw new OrekitInternalError(null); - } - } + return FieldCircularLatitudeArgumentUtility.convertAlpha(positionAngleType, alpha, ex, ey, cachedPositionAngleType); } /** Compute non-Keplerian part of the acceleration from first time derivatives. @@ -1478,29 +1448,42 @@ protected T[][] computeJacobianTrueWrtCartesian() { /** {@inheritDoc} */ @Override - public void addKeplerContribution(final PositionAngleType type, final T gm, - final T[] pDot) { - final T oMe2; - final T ksi; - final T n = a.reciprocal().multiply(gm).sqrt().divide(a); + public void addKeplerContribution(final PositionAngleType type, final T gm, final T[] pDot) { + pDot[5] = pDot[5].add(computeKeplerianAlphaDot(type, a, ex, ey, gm, cachedAlpha, cachedPositionAngleType)); + } + + /** + * Compute rate of argument of latitude. + * @param type position angle type of rate + * @param a semi major axis + * @param ex ex + * @param ey ey + * @param mu mu + * @param alpha argument of latitude + * @param cachedType position angle type of passed alpha + * @param field type + * @return first-order time derivative for alpha + * @since 12.2 + */ + private static > T computeKeplerianAlphaDot(final PositionAngleType type, final T a, + final T ex, final T ey, final T mu, + final T alpha, final PositionAngleType cachedType) { + final T n = a.reciprocal().multiply(mu).sqrt().divide(a); + if (type == PositionAngleType.MEAN) { + return n; + } final FieldSinCos sc; - switch (type) { - case MEAN : - pDot[5] = pDot[5].add(n); - break; - case ECCENTRIC : - sc = FastMath.sinCos(getAlphaE()); - ksi = ((ex.multiply(sc.cos())).add(ey.multiply(sc.sin()))).negate().add(1).reciprocal(); - pDot[5] = pDot[5].add(n.multiply(ksi)); - break; - case TRUE : - sc = FastMath.sinCos(getAlphaV()); - oMe2 = getOne().subtract(ex.multiply(ex)).subtract(ey.multiply(ey)); - ksi = getOne().add(ex.multiply(sc.cos())).add(ey.multiply(sc.sin())); - pDot[5] = pDot[5].add(n.multiply(ksi).multiply(ksi).divide(oMe2.multiply(oMe2.sqrt()))); - break; - default : - throw new OrekitInternalError(null); + final T ksi; + if (type == PositionAngleType.ECCENTRIC) { + sc = FastMath.sinCos(FieldCircularLatitudeArgumentUtility.convertAlpha(cachedType, alpha, ex, ey, type)); + ksi = ((ex.multiply(sc.cos())).add(ey.multiply(sc.sin()))).negate().add(1).reciprocal(); + return n.multiply(ksi); + } else { // TRUE + sc = FastMath.sinCos(FieldCircularLatitudeArgumentUtility.convertAlpha(cachedType, alpha, ex, ey, type)); + final T one = n.getField().getOne(); + final T oMe2 = one.subtract(ex.square()).subtract(ey.square()); + ksi = one.add(ex.multiply(sc.cos())).add(ey.multiply(sc.sin())); + return n.multiply(ksi.square()).divide(oMe2.multiply(oMe2.sqrt())); } } diff --git a/src/main/java/org/orekit/orbits/FieldEquinoctialLongitudeArgumentUtility.java b/src/main/java/org/orekit/orbits/FieldEquinoctialLongitudeArgumentUtility.java index d0c8ae579a..8883134802 100644 --- a/src/main/java/org/orekit/orbits/FieldEquinoctialLongitudeArgumentUtility.java +++ b/src/main/java/org/orekit/orbits/FieldEquinoctialLongitudeArgumentUtility.java @@ -20,6 +20,7 @@ import org.hipparchus.util.FastMath; import org.hipparchus.util.FieldSinCos; import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; /** @@ -185,4 +186,49 @@ public static > T meanToTrue(final T ex, final return eccentricToTrue(ex, ey, alphaE); } + /** + * Convert argument of longitude. + * @param oldType old position angle type + * @param l old value for argument of longitude + * @param ex ex + * @param ey ey + * @param newType new position angle type + * @param field type + * @return converted argument of longitude + * @since 12.2 + */ + public static > T convertL(final PositionAngleType oldType, final T l, + final T ex, final T ey, final PositionAngleType newType) { + if (oldType == newType) { + return l; + + } else { + switch (newType) { + + case ECCENTRIC: + if (oldType == PositionAngleType.MEAN) { + return FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(ex, ey, l); + } else { + return FieldEquinoctialLongitudeArgumentUtility.trueToEccentric(ex, ey, l); + } + + case MEAN: + if (oldType == PositionAngleType.TRUE) { + return FieldEquinoctialLongitudeArgumentUtility.trueToMean(ex, ey, l); + } else { + return FieldEquinoctialLongitudeArgumentUtility.eccentricToMean(ex, ey, l); + } + + case TRUE: + if (oldType == PositionAngleType.MEAN) { + return FieldEquinoctialLongitudeArgumentUtility.meanToTrue(ex, ey, l); + } else { + return FieldEquinoctialLongitudeArgumentUtility.eccentricToTrue(ex, ey, l); + } + + default: + throw new OrekitInternalError(null); + } + } + } } diff --git a/src/main/java/org/orekit/orbits/FieldEquinoctialOrbit.java b/src/main/java/org/orekit/orbits/FieldEquinoctialOrbit.java index 539d57d44a..51e688c9d0 100644 --- a/src/main/java/org/orekit/orbits/FieldEquinoctialOrbit.java +++ b/src/main/java/org/orekit/orbits/FieldEquinoctialOrbit.java @@ -218,9 +218,9 @@ public FieldEquinoctialOrbit(final T a, final T ex, final T ey, this.hyDot = hyDot; if (hasDerivatives()) { - final FieldUnivariateDerivative1 alphaUD = initializeCachedL(l, lDot, type); - this.cachedL = alphaUD.getValue(); - this.cachedLDot = alphaUD.getFirstDerivative(); + final FieldUnivariateDerivative1 lUD = initializeCachedL(l, lDot, type); + this.cachedL = lUD.getValue(); + this.cachedLDot = lUD.getFirstDerivative(); } else { this.cachedL = initializeCachedL(l, type); this.cachedLDot = null; @@ -892,37 +892,7 @@ private FieldUnivariateDerivative1 initializeCachedL(final T l, final T lDot, * @since 12.1 */ private T initializeCachedL(final T l, final PositionAngleType positionAngleType) { - if (positionAngleType == cachedPositionAngleType) { - return l; - - } else { - switch (cachedPositionAngleType) { - - case ECCENTRIC: - if (positionAngleType == PositionAngleType.MEAN) { - return FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(ex, ey, l); - } else { - return FieldEquinoctialLongitudeArgumentUtility.trueToEccentric(ex, ey, l); - } - - case MEAN: - if (positionAngleType == PositionAngleType.TRUE) { - return FieldEquinoctialLongitudeArgumentUtility.trueToMean(ex, ey, l); - } else { - return FieldEquinoctialLongitudeArgumentUtility.eccentricToMean(ex, ey, l); - } - - case TRUE: - if (positionAngleType == PositionAngleType.MEAN) { - return FieldEquinoctialLongitudeArgumentUtility.meanToTrue(ex, ey, l); - } else { - return FieldEquinoctialLongitudeArgumentUtility.eccentricToTrue(ex, ey, l); - } - - default: - throw new OrekitInternalError(null); - } - } + return FieldEquinoctialLongitudeArgumentUtility.convertL(positionAngleType, l, ex, ey, cachedPositionAngleType); } /** Compute non-Keplerian part of the acceleration from first time derivatives. @@ -1238,27 +1208,39 @@ protected T[][] computeJacobianTrueWrtCartesian() { @Override public void addKeplerContribution(final PositionAngleType type, final T gm, final T[] pDot) { - final T oMe2; - final T ksi; - final T n = gm.divide(a).sqrt().divide(a); + pDot[5] = pDot[5].add(computeKeplerianLDot(type, a, ex, ey, gm, cachedL, cachedPositionAngleType)); + } + + /** + * Compute rate of argument of longitude. + * @param type position angle type of rate + * @param a semi major axis + * @param ex ex + * @param ey ey + * @param mu mu + * @param l argument of longitude + * @param cachedType position angle type of passed l + * @param field type + * @return first-order time derivative for l + * @since 12.2 + */ + private static > T computeKeplerianLDot(final PositionAngleType type, final T a, final T ex, + final T ey, final T mu, final T l, final PositionAngleType cachedType) { + final T n = mu.divide(a).sqrt().divide(a); + if (type == PositionAngleType.MEAN) { + return n; + } final FieldSinCos sc; - switch (type) { - case MEAN : - pDot[5] = pDot[5].add(n); - break; - case ECCENTRIC : - sc = FastMath.sinCos(getLE()); - ksi = ((ex.multiply(sc.cos())).add(ey.multiply(sc.sin()))).negate().add(1).reciprocal(); - pDot[5] = pDot[5].add(n.multiply(ksi)); - break; - case TRUE : - sc = FastMath.sinCos(getLv()); - oMe2 = getOne().subtract(ex.square()).subtract(ey.square()); - ksi = ex.multiply(sc.cos()).add(1).add(ey.multiply(sc.sin())); - pDot[5] = pDot[5].add(n.multiply(ksi).multiply(ksi).divide(oMe2.multiply(oMe2.sqrt()))); - break; - default : - throw new OrekitInternalError(null); + final T ksi; + if (type == PositionAngleType.ECCENTRIC) { + sc = FastMath.sinCos(FieldEquinoctialLongitudeArgumentUtility.convertL(cachedType, l, ex, ey, type)); + ksi = ((ex.multiply(sc.cos())).add(ey.multiply(sc.sin()))).negate().add(1).reciprocal(); + return n.multiply(ksi); + } else { + sc = FastMath.sinCos(FieldEquinoctialLongitudeArgumentUtility.convertL(cachedType, l, ex, ey, type)); + final T oMe2 = a.getField().getOne().subtract(ex.square()).subtract(ey.square()); + ksi = ex.multiply(sc.cos()).add(1).add(ey.multiply(sc.sin())); + return n.multiply(ksi).multiply(ksi).divide(oMe2.multiply(oMe2.sqrt())); } } diff --git a/src/main/java/org/orekit/orbits/FieldKeplerianAnomalyUtility.java b/src/main/java/org/orekit/orbits/FieldKeplerianAnomalyUtility.java index f9142043bd..e172acd9ae 100644 --- a/src/main/java/org/orekit/orbits/FieldKeplerianAnomalyUtility.java +++ b/src/main/java/org/orekit/orbits/FieldKeplerianAnomalyUtility.java @@ -23,6 +23,7 @@ import org.hipparchus.util.FieldSinCos; import org.hipparchus.util.MathUtils; import org.hipparchus.util.Precision; +import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; /** @@ -74,8 +75,7 @@ public static > T ellipticMeanToTrue(final T e */ public static > T ellipticTrueToMean(final T e, final T v) { final T E = ellipticTrueToEccentric(e, v); - final T M = ellipticEccentricToMean(e, E); - return M; + return ellipticEccentricToMean(e, E); } /** @@ -86,7 +86,7 @@ public static > T ellipticTrueToMean(final T e * @return elliptic true anomaly (rad) */ public static > T ellipticEccentricToTrue(final T e, final T E) { - final T beta = e.divide(e.multiply(e).negate().add(1).sqrt().add(1)); + final T beta = e.divide(e.square().negate().add(1).sqrt().add(1)); final FieldSinCos scE = FastMath.sinCos(E); return E.add(beta.multiply(scE.sin()).divide(beta.multiply(scE.cos()).subtract(1).negate()).atan().multiply(2)); } @@ -99,7 +99,7 @@ public static > T ellipticEccentricToTrue(fina * @return elliptic eccentric anomaly (rad) */ public static > T ellipticTrueToEccentric(final T e, final T v) { - final T beta = e.divide(e.multiply(e).negate().add(1).sqrt().add(1)); + final T beta = e.divide(e.square().negate().add(1).sqrt().add(1)); final FieldSinCos scv = FastMath.sinCos(v); return v.subtract((beta.multiply(scv.sin()).divide(beta.multiply(scv.cos()).add(1))).atan().multiply(2)); } @@ -166,9 +166,9 @@ public static > T ellipticMeanToEccentric(fina f = eMeSinE(e, E).subtract(reducedM); final T s = E.multiply(0.5).sin(); - fd = e1.add(e.multiply(s).multiply(s).multiply(2)); + fd = e1.add(e.multiply(s.square()).multiply(2)); } - final T dee = f.multiply(fd).divide(f.multiply(fdd).multiply(0.5).subtract(fd.multiply(fd))); + final T dee = f.multiply(fd).divide(f.multiply(fdd).multiply(0.5).subtract(fd.square())); // update eccentric anomaly, using expressions that limit underflow problems final T w = fd.add(dee.multiply(fdd.add(dee.multiply(fddd.divide(3)))).multiply(0.5)); @@ -243,8 +243,7 @@ public static > T hyperbolicMeanToTrue(final T */ public static > T hyperbolicTrueToMean(final T e, final T v) { final T H = hyperbolicTrueToEccentric(e, v); - final T M = hyperbolicEccentricToMean(e, H); - return M; + return hyperbolicEccentricToMean(e, H); } /** @@ -269,7 +268,7 @@ public static > T hyperbolicEccentricToTrue(fi */ public static > T hyperbolicTrueToEccentric(final T e, final T v) { final FieldSinCos scv = FastMath.sinCos(v); - final T sinhH = e.multiply(e).subtract(1).sqrt().multiply(scv.sin()).divide(e.multiply(scv.cos()).add(1)); + final T sinhH = e.square().subtract(1).sqrt().multiply(scv.sin()).divide(e.multiply(scv.cos()).add(1)); return sinhH.asinh(); } @@ -306,16 +305,16 @@ public static > T hyperbolicMeanToEccentric(fi if (L.isZero()) { return M.getField().getZero(); } - final T cl = L.multiply(L).add(one).sqrt(); + final T cl = L.square().add(one).sqrt(); final T al = L.asinh(); - final T w = g.multiply(g).multiply(al).divide(cl.multiply(cl).multiply(cl)); + final T w = g.square().multiply(al).divide(cl.multiply(cl).multiply(cl)); S = one.subtract(g.divide(cl)); - S = L.add(g.multiply(al).divide(S.multiply(S).multiply(S) + S = L.add(g.multiply(al).divide(S.square().multiply(S) .add(w.multiply(L).multiply(onePointFive.subtract(fourThirds.multiply(g)))).cbrt())); // Two iterations (at most) of Halley-then-Newton process. for (int i = 0; i < 2; ++i) { - final T s0 = S.multiply(S); + final T s0 = S.square(); final T s1 = s0.add(one); final T s2 = s1.sqrt(); final T s3 = s1.multiply(s2); @@ -376,4 +375,77 @@ public static > T hyperbolicEccentricToMean(fi return e.multiply(H.sinh()).subtract(H); } + /** + * Convert anomaly. + * @param oldType old position angle type + * @param anomaly old value for anomaly + * @param e eccentricity + * @param newType new position angle type + * @param field type + * @return converted anomaly + * @since 12.2 + */ + public static > T convertAnomaly(final PositionAngleType oldType, final T anomaly, + final T e, final PositionAngleType newType) { + if (oldType == newType) { + return anomaly; + + } else { + if (e.getReal() > 1) { + switch (newType) { + case MEAN: + if (oldType == PositionAngleType.ECCENTRIC) { + return FieldKeplerianAnomalyUtility.hyperbolicEccentricToMean(e, anomaly); + } else { + return FieldKeplerianAnomalyUtility.hyperbolicTrueToMean(e, anomaly); + } + + case ECCENTRIC: + if (oldType == PositionAngleType.MEAN) { + return FieldKeplerianAnomalyUtility.hyperbolicMeanToEccentric(e, anomaly); + } else { + return FieldKeplerianAnomalyUtility.hyperbolicTrueToEccentric(e, anomaly); + } + + case TRUE: + if (oldType == PositionAngleType.ECCENTRIC) { + return FieldKeplerianAnomalyUtility.hyperbolicEccentricToTrue(e, anomaly); + } else { + return FieldKeplerianAnomalyUtility.hyperbolicMeanToTrue(e, anomaly); + } + + default: + break; + } + + } else { + switch (newType) { + case MEAN: + if (oldType == PositionAngleType.ECCENTRIC) { + return FieldKeplerianAnomalyUtility.ellipticEccentricToMean(e, anomaly); + } else { + return FieldKeplerianAnomalyUtility.ellipticTrueToMean(e, anomaly); + } + + case ECCENTRIC: + if (oldType == PositionAngleType.MEAN) { + return FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(e, anomaly); + } else { + return FieldKeplerianAnomalyUtility.ellipticTrueToEccentric(e, anomaly); + } + + case TRUE: + if (oldType == PositionAngleType.ECCENTRIC) { + return FieldKeplerianAnomalyUtility.ellipticEccentricToTrue(e, anomaly); + } else { + return FieldKeplerianAnomalyUtility.ellipticMeanToTrue(e, anomaly); + } + + default: + break; + } + } + throw new OrekitInternalError(null); + } + } } diff --git a/src/main/java/org/orekit/orbits/FieldKeplerianOrbit.java b/src/main/java/org/orekit/orbits/FieldKeplerianOrbit.java index c4c2113a5a..24774545fe 100644 --- a/src/main/java/org/orekit/orbits/FieldKeplerianOrbit.java +++ b/src/main/java/org/orekit/orbits/FieldKeplerianOrbit.java @@ -986,70 +986,11 @@ private FieldUnivariateDerivative1 initializeCachedAnomaly(final T anomaly, f * @since 12.1 */ private T initializeCachedAnomaly(final T anomaly, final PositionAngleType inputType) { - if (inputType == cachedPositionAngleType) { - return anomaly; - - } else { - if (a.getReal() < 0) { - switch (cachedPositionAngleType) { - case MEAN: - if (inputType == PositionAngleType.ECCENTRIC) { - return FieldKeplerianAnomalyUtility.hyperbolicEccentricToMean(e, anomaly); - } else { - return FieldKeplerianAnomalyUtility.hyperbolicTrueToMean(e, anomaly); - } - - case ECCENTRIC: - if (inputType == PositionAngleType.MEAN) { - return FieldKeplerianAnomalyUtility.hyperbolicMeanToEccentric(e, anomaly); - } else { - return FieldKeplerianAnomalyUtility.hyperbolicTrueToEccentric(e, anomaly); - } - - case TRUE: - if (inputType == PositionAngleType.ECCENTRIC) { - return FieldKeplerianAnomalyUtility.hyperbolicEccentricToTrue(e, anomaly); - } else { - return FieldKeplerianAnomalyUtility.hyperbolicMeanToTrue(e, anomaly); - } - - default: - break; - } - - } else { - switch (cachedPositionAngleType) { - case MEAN: - if (inputType == PositionAngleType.ECCENTRIC) { - return FieldKeplerianAnomalyUtility.ellipticEccentricToMean(e, anomaly); - } else { - return FieldKeplerianAnomalyUtility.ellipticTrueToMean(e, anomaly); - } - - case ECCENTRIC: - if (inputType == PositionAngleType.MEAN) { - return FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(e, anomaly); - } else { - return FieldKeplerianAnomalyUtility.ellipticTrueToEccentric(e, anomaly); - } - - case TRUE: - if (inputType == PositionAngleType.ECCENTRIC) { - return FieldKeplerianAnomalyUtility.ellipticEccentricToTrue(e, anomaly); - } else { - return FieldKeplerianAnomalyUtility.ellipticMeanToTrue(e, anomaly); - } - - default: - break; - } - } - throw new OrekitInternalError(null); - } + return FieldKeplerianAnomalyUtility.convertAnomaly(inputType, anomaly, e, cachedPositionAngleType); } /** Compute reference axes. - * @return referecne axes + * @return reference axes * @since 12.0 */ private FieldVector3D[] referenceAxes() { @@ -1705,26 +1646,39 @@ private T[][] computeJacobianTrueWrtCartesianHyperbolic() { @Override public void addKeplerContribution(final PositionAngleType type, final T gm, final T[] pDot) { - final T oMe2; - final T ksi; + pDot[5] = pDot[5].add(computeKeplerianAnomalyDot(type, a, e, gm, cachedAnomaly, cachedPositionAngleType)); + } + + /** + * Compute rate of argument of latitude. + * @param type position angle type of rate + * @param a semi major axis + * @param e eccentricity + * @param mu mu + * @param anomaly anomaly + * @param cachedType position angle type of passed anomaly + * @param field type + * @return first-order time derivative for anomaly + * @since 12.2 + */ + private static > T computeKeplerianAnomalyDot(final PositionAngleType type, final T a, final T e, + final T mu, final T anomaly, final PositionAngleType cachedType) { final T absA = a.abs(); - final T n = absA.reciprocal().multiply(gm).sqrt().divide(absA); - switch (type) { - case MEAN : - pDot[5] = pDot[5].add(n); - break; - case ECCENTRIC : - oMe2 = e.square().negate().add(1).abs(); - ksi = e.multiply(getTrueAnomaly().cos()).add(1); - pDot[5] = pDot[5].add( n.multiply(ksi).divide(oMe2)); - break; - case TRUE : - oMe2 = e.square().negate().add(1).abs(); - ksi = e.multiply(getTrueAnomaly().cos()).add(1); - pDot[5] = pDot[5].add(n.multiply(ksi).multiply(ksi).divide(oMe2.multiply(oMe2.sqrt()))); - break; - default : - throw new OrekitInternalError(null); + final T n = absA.reciprocal().multiply(mu).sqrt().divide(absA); + if (type == PositionAngleType.MEAN) { + return n; + } + final T ksi; + final T oMe2; + final T trueAnomaly = FieldKeplerianAnomalyUtility.convertAnomaly(cachedType, anomaly, e, PositionAngleType.TRUE); + if (type == PositionAngleType.ECCENTRIC) { + oMe2 = e.square().negate().add(1).abs(); + ksi = e.multiply(trueAnomaly.cos()).add(1); + return n.multiply(ksi).divide(oMe2); + } else { + oMe2 = e.square().negate().add(1).abs(); + ksi = e.multiply(trueAnomaly.cos()).add(1); + return n.multiply(ksi).multiply(ksi).divide(oMe2.multiply(oMe2.sqrt())); } } diff --git a/src/main/java/org/orekit/orbits/KeplerianAnomalyUtility.java b/src/main/java/org/orekit/orbits/KeplerianAnomalyUtility.java index 49936e2454..63ee308b8e 100644 --- a/src/main/java/org/orekit/orbits/KeplerianAnomalyUtility.java +++ b/src/main/java/org/orekit/orbits/KeplerianAnomalyUtility.java @@ -20,6 +20,7 @@ import org.hipparchus.util.FastMath; import org.hipparchus.util.MathUtils; import org.hipparchus.util.SinCos; +import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; /** @@ -332,4 +333,76 @@ public static double hyperbolicEccentricToMean(final double e, final double H) { return e * FastMath.sinh(H) - H; } + /** + * Convert anomaly. + * @param oldType old position angle type + * @param anomaly old value for anomaly + * @param e eccentricity + * @param newType new position angle type + * @return converted anomaly + * @since 12.2 + */ + public static double convertAnomaly(final PositionAngleType oldType, final double anomaly, final double e, + final PositionAngleType newType) { + if (newType == oldType) { + return anomaly; + + } else { + if (e > 1) { + switch (newType) { + case MEAN: + if (oldType == PositionAngleType.ECCENTRIC) { + return KeplerianAnomalyUtility.hyperbolicEccentricToMean(e, anomaly); + } else { + return KeplerianAnomalyUtility.hyperbolicTrueToMean(e, anomaly); + } + + case ECCENTRIC: + if (oldType == PositionAngleType.MEAN) { + return KeplerianAnomalyUtility.hyperbolicMeanToEccentric(e, anomaly); + } else { + return KeplerianAnomalyUtility.hyperbolicTrueToEccentric(e, anomaly); + } + + case TRUE: + if (oldType == PositionAngleType.ECCENTRIC) { + return KeplerianAnomalyUtility.hyperbolicEccentricToTrue(e, anomaly); + } else { + return KeplerianAnomalyUtility.hyperbolicMeanToTrue(e, anomaly); + } + + default: + break; + } + + } else { + switch (newType) { + case MEAN: + if (oldType == PositionAngleType.ECCENTRIC) { + return KeplerianAnomalyUtility.ellipticEccentricToMean(e, anomaly); + } else { + return KeplerianAnomalyUtility.ellipticTrueToMean(e, anomaly); + } + + case ECCENTRIC: + if (oldType == PositionAngleType.MEAN) { + return KeplerianAnomalyUtility.ellipticMeanToEccentric(e, anomaly); + } else { + return KeplerianAnomalyUtility.ellipticTrueToEccentric(e, anomaly); + } + + case TRUE: + if (oldType == PositionAngleType.ECCENTRIC) { + return KeplerianAnomalyUtility.ellipticEccentricToTrue(e, anomaly); + } else { + return KeplerianAnomalyUtility.ellipticMeanToTrue(e, anomaly); + } + + default: + break; + } + } + throw new OrekitInternalError(null); + } + } } diff --git a/src/main/java/org/orekit/orbits/KeplerianOrbit.java b/src/main/java/org/orekit/orbits/KeplerianOrbit.java index f3cb4a06d6..e53b080c50 100644 --- a/src/main/java/org/orekit/orbits/KeplerianOrbit.java +++ b/src/main/java/org/orekit/orbits/KeplerianOrbit.java @@ -897,70 +897,11 @@ private UnivariateDerivative1 initializeCachedAnomaly(final double anomaly, fina * @since 12.1 */ private double initializeCachedAnomaly(final double anomaly, final PositionAngleType inputType) { - if (inputType == cachedPositionAngleType) { - return anomaly; - - } else { - if (a < 0) { - switch (cachedPositionAngleType) { - case MEAN: - if (inputType == PositionAngleType.ECCENTRIC) { - return KeplerianAnomalyUtility.hyperbolicEccentricToMean(e, anomaly); - } else { - return KeplerianAnomalyUtility.hyperbolicTrueToMean(e, anomaly); - } - - case ECCENTRIC: - if (inputType == PositionAngleType.MEAN) { - return KeplerianAnomalyUtility.hyperbolicMeanToEccentric(e, anomaly); - } else { - return KeplerianAnomalyUtility.hyperbolicTrueToEccentric(e, anomaly); - } - - case TRUE: - if (inputType == PositionAngleType.ECCENTRIC) { - return KeplerianAnomalyUtility.hyperbolicEccentricToTrue(e, anomaly); - } else { - return KeplerianAnomalyUtility.hyperbolicMeanToTrue(e, anomaly); - } - - default: - break; - } - - } else { - switch (cachedPositionAngleType) { - case MEAN: - if (inputType == PositionAngleType.ECCENTRIC) { - return KeplerianAnomalyUtility.ellipticEccentricToMean(e, anomaly); - } else { - return KeplerianAnomalyUtility.ellipticTrueToMean(e, anomaly); - } - - case ECCENTRIC: - if (inputType == PositionAngleType.MEAN) { - return KeplerianAnomalyUtility.ellipticMeanToEccentric(e, anomaly); - } else { - return KeplerianAnomalyUtility.ellipticTrueToEccentric(e, anomaly); - } - - case TRUE: - if (inputType == PositionAngleType.ECCENTRIC) { - return KeplerianAnomalyUtility.ellipticEccentricToTrue(e, anomaly); - } else { - return KeplerianAnomalyUtility.ellipticMeanToTrue(e, anomaly); - } - - default: - break; - } - } - throw new OrekitInternalError(null); - } + return KeplerianAnomalyUtility.convertAnomaly(inputType, anomaly, e, cachedPositionAngleType); } /** Compute reference axes. - * @return referecne axes + * @return reference axes * @since 12.0 */ private Vector3D[] referenceAxes() { @@ -1600,26 +1541,33 @@ private double[][] computeJacobianTrueWrtCartesianHyperbolic() { @Override public void addKeplerContribution(final PositionAngleType type, final double gm, final double[] pDot) { - final double oMe2; - final double ksi; + pDot[5] += computeKeplerianAnomalyDot(type, a, e, gm, cachedAnomaly, cachedPositionAngleType); + } + + /** + * Compute rate of argument of latitude. + * @param type position angle type of rate + * @param a semi major axis + * @param e eccentricity + * @param mu mu + * @param anomaly anomaly + * @param cachedType position angle type of passed anomaly + * @return first-order time derivative for anomaly + * @since 12.2 + */ + private static double computeKeplerianAnomalyDot(final PositionAngleType type, final double a, final double e, + final double mu, final double anomaly, final PositionAngleType cachedType) { final double absA = FastMath.abs(a); - final double n = FastMath.sqrt(gm / absA) / absA; - switch (type) { - case MEAN : - pDot[5] += n; - break; - case ECCENTRIC : - oMe2 = FastMath.abs(1 - e * e); - ksi = 1 + e * FastMath.cos(getTrueAnomaly()); - pDot[5] += n * ksi / oMe2; - break; - case TRUE : - oMe2 = FastMath.abs(1 - e * e); - ksi = 1 + e * FastMath.cos(getTrueAnomaly()); - pDot[5] += n * ksi * ksi / (oMe2 * FastMath.sqrt(oMe2)); - break; - default : - throw new OrekitInternalError(null); + final double n = FastMath.sqrt(mu / absA) / absA; + if (type == PositionAngleType.MEAN) { + return n; + } + final double oMe2 = FastMath.abs(1 - e * e); + final double ksi = 1 + e * FastMath.cos(KeplerianAnomalyUtility.convertAnomaly(cachedType, anomaly, e, PositionAngleType.TRUE)); + if (type == PositionAngleType.ECCENTRIC) { + return n * ksi / oMe2; + } else { // TRUE + return n * ksi * ksi / (oMe2 * FastMath.sqrt(oMe2)); } } diff --git a/src/main/java/org/orekit/overview.html b/src/main/java/org/orekit/overview.html index 650c88adca..7bfa1c1b7c 100644 --- a/src/main/java/org/orekit/overview.html +++ b/src/main/java/org/orekit/overview.html @@ -122,8 +122,8 @@

    2. Features

    • central attraction
    • gravity models including time-dependent like trends and pulsations (automatic - reading of ICGEM (new Eigen models), SHM (old Eigen models), EGM and GRGS gravity - field files formats, even compressed)
    • + reading of ICGEM (new Eigen models), SHM (old Eigen models), EGM, + SHA (GRGM1200B and GRGM1200L) and GRGS gravity field files formats, even compressed)
    • atmospheric drag
    • third body attraction (with data for Sun, Moon and all solar systems planets)
    • radiation pressure with eclipses (multiple oblate spheroids occulting bodies, @@ -242,6 +242,7 @@

      2. Features

      yaw compensation, yaw-steering)
    • orbit referenced attitudes (LOF aligned, offset on all axes)
    • space referenced attitudes (inertial, celestial body-pointed, spin-stabilized)
    • +
    • attitude aligned with one target and constrained by another target
    • tabulated attitudes, either respective to inertial frame or respective to Local Orbital Frames
    • specific law for GNSS satellites: GPS (block IIA, block IIF, block IIF), @@ -417,6 +418,13 @@

      2. Features

    • construction of trajectories using loxodromes (commonly, a rhumb line)
  • +
  • Indirect optimal control +
      +
    • adjoint equations as defined by Pontryagin's Maximum Principle with Cartesian coordinates for a range of forces: (gravitational, inertial) including J2
    • +
    • so-called energy cost functions (proportional to the integral of the control vector's squared norm), with Hamitonian evaluation
    • +
    • single shooting based on Newton algorithm for the case of fixed time, fixed Cartesian bounds
    • +
    +
  • Collisions
    • loading and writing of CCSDS Conjunction Data Messages (CDM in both KVN and XML diff --git a/src/main/java/org/orekit/propagation/FieldStateCovariance.java b/src/main/java/org/orekit/propagation/FieldStateCovariance.java index 7001bf8b65..bf997395c4 100644 --- a/src/main/java/org/orekit/propagation/FieldStateCovariance.java +++ b/src/main/java/org/orekit/propagation/FieldStateCovariance.java @@ -25,7 +25,7 @@ import org.hipparchus.util.MathArrays; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; -import org.orekit.frames.FieldTransform; +import org.orekit.frames.FieldKinematicTransform; import org.orekit.frames.Frame; import org.orekit.frames.LOF; import org.orekit.orbits.FieldOrbit; @@ -34,7 +34,6 @@ import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.FieldTimeStamped; -import org.orekit.utils.CartesianDerivativesFilter; /** * This class is the representation of a covariance matrix at a given date. @@ -214,7 +213,7 @@ public FieldStateCovariance changeCovarianceType(final FieldOrbit orbit, f final PositionAngleType outAngleType) { // Handle case where the covariance is already expressed in the output type - if (outOrbitType == orbitType && (outAngleType == angleType || outOrbitType == OrbitType.CARTESIAN)) { + if (outOrbitType == orbitType && (outOrbitType == OrbitType.CARTESIAN || outAngleType == angleType)) { if (lof == null) { return new FieldStateCovariance<>(orbitalCovariance, epoch, frame, orbitType, angleType); } @@ -261,6 +260,11 @@ public FieldStateCovariance changeCovarianceFrame(final FieldOrbit orbit, // Verify current covariance frame if (lof != null) { + // Check specific case where output covariance will be the same + if (lofOut == lof) { + return new FieldStateCovariance<>(orbitalCovariance, epoch, lof); + } + // Change the covariance local orbital frame return changeFrameAndCreate(orbit, epoch, lof, lofOut, orbitalCovariance); @@ -297,6 +301,11 @@ public FieldStateCovariance changeCovarianceFrame(final FieldOrbit orbit, } else { + // Check specific case where output covariance will be the same + if (frame == frameOut) { + return new FieldStateCovariance<>(orbitalCovariance, epoch, frame, orbitType, angleType); + } + // Change covariance frame return changeFrameAndCreate(orbit, epoch, frame, frameOut, orbitalCovariance, orbitType, angleType); @@ -435,6 +444,12 @@ private FieldStateCovariance changeTypeAndCreate(final FieldOrbit orbit, final PositionAngleType outAngleType, final FieldMatrix inputCov) { + // Check if type change is really necessary, if not then return input covariance + if (StateCovariance.inputAndOutputOrbitTypesAreCartesian(inOrbitType, outOrbitType) || + StateCovariance.inputAndOutputAreIdentical(inOrbitType, inAngleType, outOrbitType, outAngleType)) { + return new FieldStateCovariance<>(inputCov, date, covFrame, inOrbitType, inAngleType); + } + // Notations: // I: Input orbit type // O: Output orbit type @@ -491,8 +506,7 @@ private FieldStateCovariance changeFrameAndCreate(final FieldOrbit orbit, // Builds the matrix to perform covariance transformation final FieldMatrix jacobianFromLofInToLofOut = - getJacobian(date.getField(), - LOF.transformFromLOFInToLOFOut(lofIn, lofOut, date, orbit.getPVCoordinates())); + getJacobian(LOF.transformFromLOFInToLOFOut(lofIn, lofOut, date, orbit.getPVCoordinates())); // Get the Cartesian covariance matrix converted to frameOut final FieldMatrix cartesianCovarianceOut = @@ -550,7 +564,7 @@ private FieldStateCovariance changeFrameAndCreate(final FieldOrbit orbit, // Builds the matrix to perform covariance transformation final FieldMatrix jacobianFromFrameInToLofOut = - getJacobian(date.getField(), lofOut.transformFromInertial(date, orbit.getPVCoordinates(frameIn))); + getJacobian(lofOut.transformFromInertial(date, orbit.getPVCoordinates(frameIn))); // Get the Cartesian covariance matrix converted to frameOut final FieldMatrix cartesianCovarianceOut = @@ -612,8 +626,7 @@ private FieldStateCovariance changeFrameAndCreate(final FieldOrbit orbit, // Builds the matrix to perform covariance transformation final FieldMatrix jacobianFromLofInToFrameOut = - getJacobian(date.getField(), - lofIn.transformFromInertial(date, orbit.getPVCoordinates(frameOut)).getInverse()); + getJacobian(lofIn.transformFromInertial(date, orbit.getPVCoordinates(frameOut)).getInverse()); // Transform covariance final FieldMatrix transformedCovariance = @@ -631,8 +644,7 @@ private FieldStateCovariance changeFrameAndCreate(final FieldOrbit orbit, // Builds the matrix to perform covariance transformation final FieldMatrix jacobianFromLofInToOrbitFrame = - getJacobian(date.getField(), - lofIn.transformFromInertial(date, orbit.getPVCoordinates()).getInverse()); + getJacobian(lofIn.transformFromInertial(date, orbit.getPVCoordinates()).getInverse()); // Get the Cartesian covariance matrix converted to orbit inertial frame final FieldMatrix cartesianCovarianceInOrbitFrame = jacobianFromLofInToOrbitFrame.multiply( @@ -685,10 +697,10 @@ private FieldStateCovariance changeFrameAndCreate(final FieldOrbit orbit, final PositionAngleType covAngleType) { // Get the transform from the covariance frame to the output frame - final FieldTransform inToOut = frameIn.getTransformTo(frameOut, orbit.getDate()); + final FieldKinematicTransform inToOut = frameIn.getKinematicTransformTo(frameOut, orbit.getDate()); // Matrix to perform the covariance transformation - final FieldMatrix j = getJacobian(date.getField(), inToOut); + final FieldMatrix j = getJacobian(inToOut); // Input frame pseudo-inertial if (frameIn.isPseudoInertial()) { @@ -739,18 +751,12 @@ private FieldStateCovariance changeFrameAndCreate(final FieldOrbit orbit, /** * Builds the matrix to perform covariance frame transformation. * - * @param field to which the elements belong * @param transform input transformation * * @return the matrix to perform the covariance frame transformation */ - private FieldMatrix getJacobian(final Field field, final FieldTransform transform) { - // Get the Jacobian of the transform - final T[][] jacobian = MathArrays.buildArray(field, STATE_DIMENSION, STATE_DIMENSION); - transform.getJacobian(CartesianDerivativesFilter.USE_PV, jacobian); - // Return - return new Array2DRowFieldMatrix<>(jacobian, false); - + private FieldMatrix getJacobian(final FieldKinematicTransform transform) { + return MatrixUtils.createFieldMatrix(transform.getPVJacobian()); } /** diff --git a/src/main/java/org/orekit/propagation/StateCovariance.java b/src/main/java/org/orekit/propagation/StateCovariance.java index 6b24e7693d..f5ec1668b2 100644 --- a/src/main/java/org/orekit/propagation/StateCovariance.java +++ b/src/main/java/org/orekit/propagation/StateCovariance.java @@ -22,14 +22,14 @@ import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; +import org.orekit.frames.KinematicTransform; import org.orekit.frames.LOF; -import org.orekit.frames.Transform; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; import org.orekit.orbits.PositionAngleType; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeStamped; -import org.orekit.utils.CartesianDerivativesFilter; +import org.orekit.utils.CartesianCovarianceUtils; /** This class is the representation of a covariance matrix at a given date. *

      @@ -143,6 +143,31 @@ public static void checkFrameAndOrbitTypeConsistency(final Frame covarianceFrame } } + /** + * Checks if input/output orbit and angle types are identical. + * + * @param inOrbitType input orbit type + * @param inAngleType input angle type + * @param outOrbitType output orbit type + * @param outAngleType output angle type + * @return flag defining if input/output orbit and angle types are identical + */ + public static boolean inputAndOutputAreIdentical(final OrbitType inOrbitType, final PositionAngleType inAngleType, + final OrbitType outOrbitType, final PositionAngleType outAngleType) { + return inOrbitType == outOrbitType && inAngleType == outAngleType; + } + + /** + * Checks if input and output orbit types are both {@code OrbitType.CARTESIAN}. + * + * @param inOrbitType input orbit type + * @param outOrbitType output orbit type + * @return flag defining if input and output orbit types are both {@code OrbitType.CARTESIAN} + */ + public static boolean inputAndOutputOrbitTypesAreCartesian(final OrbitType inOrbitType, final OrbitType outOrbitType) { + return inOrbitType == OrbitType.CARTESIAN && outOrbitType == OrbitType.CARTESIAN; + } + /** {@inheritDoc}. */ @Override public AbsoluteDate getDate() { @@ -215,7 +240,7 @@ public StateCovariance changeCovarianceType(final Orbit orbit, final OrbitType o final PositionAngleType outAngleType) { // Handle case where the covariance is already expressed in the output type - if (outOrbitType == orbitType && (outAngleType == angleType || outOrbitType == OrbitType.CARTESIAN)) { + if (outOrbitType == orbitType && (outOrbitType == OrbitType.CARTESIAN || outAngleType == angleType)) { if (lof == null) { return new StateCovariance(orbitalCovariance, epoch, frame, orbitType, angleType); } @@ -261,6 +286,11 @@ public StateCovariance changeCovarianceFrame(final Orbit orbit, final LOF lofOut // Verify current covariance frame if (lof != null) { + // Check specific case where output covariance will be the same + if (lofOut == lof) { + return new StateCovariance(orbitalCovariance, epoch, lof); + } + // Change the covariance local orbital frame return changeFrameAndCreate(orbit, epoch, lof, lofOut, orbitalCovariance); @@ -296,6 +326,11 @@ public StateCovariance changeCovarianceFrame(final Orbit orbit, final Frame fram } else { + // Check specific case where output covariance will be the same + if (frame == frameOut) { + return new StateCovariance(orbitalCovariance, epoch, frame, orbitType, angleType); + } + // Change covariance frame return changeFrameAndCreate(orbit, epoch, frame, frameOut, orbitalCovariance, orbitType, angleType); @@ -393,12 +428,19 @@ public StateCovariance shiftedBy(final Orbit orbit, final double dt) { * @param inputCov input covariance * @return the covariance expressed in the target orbit type with the target position angle */ - private static StateCovariance changeTypeAndCreate(final Orbit orbit, final AbsoluteDate date, + private static StateCovariance changeTypeAndCreate(final Orbit orbit, + final AbsoluteDate date, final Frame covFrame, final OrbitType inOrbitType, final PositionAngleType inAngleType, final OrbitType outOrbitType, final PositionAngleType outAngleType, final RealMatrix inputCov) { + // Check if type change is really necessary, if not then return input covariance + if (inputAndOutputOrbitTypesAreCartesian(inOrbitType, outOrbitType) || + inputAndOutputAreIdentical(inOrbitType, inAngleType, outOrbitType, outAngleType)) { + return new StateCovariance(inputCov, date, covFrame, inOrbitType, inAngleType); + } + // Notations: // I: Input orbit type // O: Output orbit type @@ -629,12 +671,6 @@ private static StateCovariance changeFrameAndCreate(final Orbit orbit, final Abs final OrbitType covOrbitType, final PositionAngleType covAngleType) { - // Get the transform from the covariance frame to the output frame - final Transform inToOut = frameIn.getTransformTo(frameOut, orbit.getDate()); - - // Matrix to perform the covariance transformation - final RealMatrix j = getJacobian(inToOut); - // Input frame pseudo-inertial if (frameIn.isPseudoInertial()) { @@ -645,7 +681,8 @@ private static StateCovariance changeFrameAndCreate(final Orbit orbit, final Abs inputCov).getMatrix(); // Get the Cartesian covariance matrix converted to frameOut - final RealMatrix cartesianCovarianceOut = j.multiply(cartesianCovarianceIn.multiplyTransposed(j)); + final RealMatrix cartesianCovarianceOut = CartesianCovarianceUtils.changeReferenceFrame(frameIn, + cartesianCovarianceIn, date, frameOut); // Output frame is pseudo-inertial if (frameOut.isPseudoInertial()) { @@ -672,7 +709,8 @@ private static StateCovariance changeFrameAndCreate(final Orbit orbit, final Abs // Method checkInputConsistency already verify that input covariance is defined in CARTESIAN type // Convert covariance matrix to frameOut - final RealMatrix covInFrameOut = j.multiply(inputCov.multiplyTransposed(j)); + final RealMatrix covInFrameOut = CartesianCovarianceUtils.changeReferenceFrame(frameIn, + inputCov, date, frameOut); // Output the Cartesian covariance matrix converted to frameOut return new StateCovariance(covInFrameOut, date, frameOut, OrbitType.CARTESIAN, DEFAULT_POSITION_ANGLE); @@ -686,13 +724,8 @@ private static StateCovariance changeFrameAndCreate(final Orbit orbit, final Abs * @param transform input transformation * @return the matrix to perform the covariance frame transformation */ - private static RealMatrix getJacobian(final Transform transform) { - // Get the Jacobian of the transform - final double[][] jacobian = new double[STATE_DIMENSION][STATE_DIMENSION]; - transform.getJacobian(CartesianDerivativesFilter.USE_PV, jacobian); - // Return - return new Array2DRowRealMatrix(jacobian, false); - + private static RealMatrix getJacobian(final KinematicTransform transform) { + return MatrixUtils.createRealMatrix(transform.getPVJacobian()); } /** diff --git a/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalPropagator.java b/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalPropagator.java index 533741e0f0..55c02550ae 100644 --- a/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalPropagator.java @@ -158,6 +158,11 @@ public SpacecraftState propagate(final AbsoluteDate start, final AbsoluteDate ta } while (!isLastStep); + // Finalize event detectors + for (final EventState es : eventsStates) { + es.finish(state); + } + // return the last computed state lastPropagationEnd = state.getDate(); setStartDate(state.getDate()); diff --git a/src/main/java/org/orekit/propagation/analytical/BrouwerLyddanePropagator.java b/src/main/java/org/orekit/propagation/analytical/BrouwerLyddanePropagator.java index f2b8e8046f..a13af94aae 100644 --- a/src/main/java/org/orekit/propagation/analytical/BrouwerLyddanePropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/BrouwerLyddanePropagator.java @@ -33,6 +33,7 @@ import org.orekit.errors.OrekitMessages; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics; +import org.orekit.orbits.EquinoctialOrbit; import org.orekit.orbits.FieldKeplerianAnomalyUtility; import org.orekit.orbits.KeplerianOrbit; import org.orekit.orbits.Orbit; @@ -86,8 +87,12 @@ * @see "Phipps Jr, Warren E. Parallelization of the Navy Space Surveillance Center * (NAVSPASUR) Satellite Model. NAVAL POSTGRADUATE SCHOOL MONTEREY CA, 1992." * + * @see "Solomon, Daniel, THE NAVSPASUR Satellite Motion Model, + * Naval Research Laboratory, August 8, 1991." + * * @author Melina Vanel * @author Bryan Cazabonne + * @author Pascal Parraud * @since 11.1 */ public class BrouwerLyddanePropagator extends AbstractAnalyticalPropagator implements ParameterDriversProvider { @@ -99,10 +104,13 @@ public class BrouwerLyddanePropagator extends AbstractAnalyticalPropagator imple public static final double M2 = 0.0; /** Default convergence threshold for mean parameters conversion. */ - private static final double EPSILON_DEFAULT = 1.0e-13; + public static final double EPSILON_DEFAULT = 1.0e-13; /** Default value for maxIterations. */ - private static final int MAX_ITERATIONS_DEFAULT = 200; + public static final int MAX_ITERATIONS_DEFAULT = 200; + + /** Default value for damping. */ + public static final double DAMPING_DEFAULT = 1.0; /** Parameters scaling factor. *

      @@ -115,6 +123,9 @@ public class BrouwerLyddanePropagator extends AbstractAnalyticalPropagator imple /** Beta constant used by T2 function. */ private static final double BETA = FastMath.scalb(100, -11); + /** Max value for the eccentricity. */ + private static final double MAX_ECC = 0.999999; + /** Initial Brouwer-Lyddane model. */ private BLModel initialModel; @@ -710,8 +721,8 @@ protected void resetIntermediateState(final SpacecraftState state, final boolean stateChanged(state); } - /** Compute mean parameters according to the Brouwer-Lyddane analytical model computation - * in order to do the propagation. + /** Compute mean parameters according to the Brouwer-Lyddane analytical model, + * using an intermediate equinoctial orbit to avoid singularities. * @param osculating osculating orbit * @param mass constant mass * @param epsilon convergence threshold for mean parameters conversion @@ -721,6 +732,9 @@ protected void resetIntermediateState(final SpacecraftState state, final boolean private BLModel computeMeanParameters(final KeplerianOrbit osculating, final double mass, final double epsilon, final int maxIterations) { + // damping factor + final double damping = DAMPING_DEFAULT; + // sanity check if (osculating.getA() < referenceRadius) { throw new OrekitException(OrekitMessages.TRAJECTORY_INSIDE_BRILLOUIN_SPHERE, @@ -730,10 +744,19 @@ private BLModel computeMeanParameters(final KeplerianOrbit osculating, final dou // rough initialization of the mean parameters BLModel current = new BLModel(osculating, mass, referenceRadius, mu, ck0); + // Get equinoctial parameters + double sma = osculating.getA(); + double ex = osculating.getEquinoctialEx(); + double ey = osculating.getEquinoctialEy(); + double hx = osculating.getHx(); + double hy = osculating.getHy(); + double lv = osculating.getLv(); + // threshold for each parameter - final double thresholdA = epsilon * (1 + FastMath.abs(current.mean.getA())); - final double thresholdE = epsilon * (1 + current.mean.getE()); - final double thresholdAngles = epsilon * FastMath.PI; + final double thresholdA = epsilon * (1 + FastMath.abs(osculating.getA())); + final double thresholdE = epsilon * (1 + FastMath.hypot(ex, ey)); + final double thresholdH = epsilon * (1 + FastMath.hypot(hx, hy)); + final double thresholdLv = epsilon * FastMath.PI; int i = 0; while (i++ < maxIterations) { @@ -742,38 +765,39 @@ private BLModel computeMeanParameters(final KeplerianOrbit osculating, final dou final KeplerianOrbit parameters = current.propagateParameters(current.mean.getDate()); // adapted parameters residuals - final double deltaA = osculating.getA() - parameters.getA(); - final double deltaE = osculating.getE() - parameters.getE(); - final double deltaI = osculating.getI() - parameters.getI(); - final double deltaOmega = MathUtils.normalizeAngle(osculating.getPerigeeArgument() - - parameters.getPerigeeArgument(), - 0.0); - final double deltaRAAN = MathUtils.normalizeAngle(osculating.getRightAscensionOfAscendingNode() - - parameters.getRightAscensionOfAscendingNode(), - 0.0); - final double deltaAnom = MathUtils.normalizeAngle(osculating.getMeanAnomaly() - - parameters.getMeanAnomaly(), - 0.0); - + final double deltaA = osculating.getA() - parameters.getA(); + final double deltaEx = osculating.getEquinoctialEx() - parameters.getEquinoctialEx(); + final double deltaEy = osculating.getEquinoctialEy() - parameters.getEquinoctialEy(); + final double deltaHx = osculating.getHx() - parameters.getHx(); + final double deltaHy = osculating.getHy() - parameters.getHy(); + final double deltaLv = MathUtils.normalizeAngle(osculating.getLv() - parameters.getLv(), 0.0); + + // update state + sma += damping * deltaA; + ex += damping * deltaEx; + ey += damping * deltaEy; + hx += damping * deltaHx; + hy += damping * deltaHy; + lv += damping * deltaLv; + + // Update mean orbit + final EquinoctialOrbit mean = new EquinoctialOrbit(sma, ex, ey, hx, hy, lv, + PositionAngleType.TRUE, + osculating.getFrame(), + osculating.getDate(), + osculating.getMu()); + final KeplerianOrbit meanOrb = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(mean); // update mean parameters - current = new BLModel(new KeplerianOrbit(current.mean.getA() + deltaA, - FastMath.max(current.mean.getE() + deltaE, 0.0), - current.mean.getI() + deltaI, - current.mean.getPerigeeArgument() + deltaOmega, - current.mean.getRightAscensionOfAscendingNode() + deltaRAAN, - current.mean.getMeanAnomaly() + deltaAnom, - PositionAngleType.MEAN, PositionAngleType.MEAN, - current.mean.getFrame(), - current.mean.getDate(), mu), - mass, referenceRadius, mu, ck0); + current = new BLModel(meanOrb, mass, referenceRadius, mu, ck0); + // check convergence - if (FastMath.abs(deltaA) < thresholdA && - FastMath.abs(deltaE) < thresholdE && - FastMath.abs(deltaI) < thresholdAngles && - FastMath.abs(deltaOmega) < thresholdAngles && - FastMath.abs(deltaRAAN) < thresholdAngles && - FastMath.abs(deltaAnom) < thresholdAngles) { + if (FastMath.abs(deltaA) < thresholdA && + FastMath.abs(deltaEx) < thresholdE && + FastMath.abs(deltaEy) < thresholdE && + FastMath.abs(deltaHx) < thresholdH && + FastMath.abs(deltaHy) < thresholdH && + FastMath.abs(deltaLv) < thresholdLv) { return current; } } @@ -865,96 +889,62 @@ protected List getJacobiansColumnsNames() { /** Local class for Brouwer-Lyddane model. */ private class BLModel { - /** Mean orbit. */ - private final KeplerianOrbit mean; - /** Constant mass. */ private final double mass; - // CHECKSTYLE: stop JavadocVariable check - - // preprocessed values - - // Constant for secular terms l'', g'', h'' - // l standing for mean anomaly, g for perigee argument and h for raan - private final double xnotDot; - private final double n; - private final double lt; - private final double gt; - private final double ht; - - - // Long period terms - private final double dei3sg; - private final double de2sg; - private final double deisg; - private final double de; - - - private final double dlgs2g; - private final double dlgc3g; - private final double dlgcg; - - - private final double dh2sgcg; - private final double dhsgcg; - private final double dhcg; - - - // Short period terms - private final double aC; - private final double aCbis; - private final double ac2g2f; - - - private final double eC; - private final double ecf; - private final double e2cf; - private final double e3cf; - private final double ec2f2g; - private final double ecfc2f2g; - private final double e2cfc2f2g; - private final double e3cfc2f2g; - private final double ec2gf; - private final double ec2g3f; - - - private final double ide; - private final double isfs2f2g; - private final double icfc2f2g; - private final double ic2f2g; - - - private final double glf; - private final double gll; - private final double glsf; - private final double glosf; - private final double gls2f2g; - private final double gls2gf; - private final double glos2gf; - private final double gls2g3f; - private final double glos2g3f; - - - private final double hf; - private final double hl; - private final double hsf; - private final double hcfs2g2f; - private final double hs2g2f; - private final double hsfc2g2f; + /** Brouwer-Lyddane mean orbit. */ + private final KeplerianOrbit mean; + // Preprocessed values - private final double edls2g; - private final double edlcg; - private final double edlc3g; - private final double edlsf; - private final double edls2gf; - private final double edls2g3f; + /** Mean mean motion: n0 = √(μ/a")/a". */ + private final double n0; - // Drag terms + /** η = √(1 - e"²). */ + private final double n; + /** η². */ + private final double n2; + /** η³. */ + private final double n3; + /** η + 1 / (1 + η). */ + private final double t8; + + /** Secular correction for mean anomaly l: δsl. */ + private final double dsl; + /** Secular correction for perigee argument g: δsg. */ + private final double dsg; + /** Secular correction for raan h: δsh. */ + private final double dsh; + + /** Secular rate of change of semi-major axis due to drag. */ private final double aRate; + /** Secular rate of change of eccentricity due to drag. */ private final double eRate; + // CHECKSTYLE: stop JavadocVariable check + + // Storage for speed-up + private final double yp2; + private final double ci; + private final double si; + private final double oneMci2; + private final double ci2X3M1; + + // Long periodic corrections factors + private final double vle1; + private final double vle2; + private final double vle3; + private final double vli1; + private final double vli2; + private final double vli3; + private final double vll2; + private final double vlh1I; + private final double vlh2I; + private final double vlh3I; + private final double vls1; + private final double vls2; + private final double vls3; + // CHECKSTYLE: resume JavadocVariable check /** Create a model for specified mean orbit. @@ -967,200 +957,170 @@ private class BLModel { BLModel(final KeplerianOrbit mean, final double mass, final double referenceRadius, final double mu, final double[] ck0) { - this.mean = mean; this.mass = mass; - final double app = mean.getA(); - xnotDot = FastMath.sqrt(mu / app) / app; + // mean orbit + this.mean = mean; - // preliminary processing - final double q = referenceRadius / app; - double ql = q * q; - final double y2 = -0.5 * ck0[2] * ql; + // mean eccentricity e" + final double epp = mean.getE(); + if (epp >= 1) { + // Only for elliptical (e < 1) orbits + throw new OrekitException(OrekitMessages.TOO_LARGE_ECCENTRICITY_FOR_PROPAGATION_MODEL, + epp); + } + final double epp2 = epp * epp; - n = FastMath.sqrt(1 - mean.getE() * mean.getE()); - final double n2 = n * n; - final double n3 = n2 * n; - final double n4 = n2 * n2; - final double n6 = n4 * n2; - final double n8 = n4 * n4; - final double n10 = n8 * n2; + // η + n2 = 1. - epp2; + n = FastMath.sqrt(n2); + n3 = n2 * n; + t8 = n + 1. / (1. + n); - final double yp2 = y2 / n4; - ql *= q; - final double yp3 = ck0[3] * ql / n6; - ql *= q; - final double yp4 = 0.375 * ck0[4] * ql / n8; - ql *= q; - final double yp5 = ck0[5] * ql / n10; + // mean semi-major axis a" + final double app = mean.getA(); + // mean mean motion + n0 = FastMath.sqrt(mu / app) / app; - final SinCos sc = FastMath.sinCos(mean.getI()); - final double sinI1 = sc.sin(); - final double sinI2 = sinI1 * sinI1; - final double cosI1 = sc.cos(); - final double cosI2 = cosI1 * cosI1; - final double cosI3 = cosI2 * cosI1; - final double cosI4 = cosI2 * cosI2; - final double cosI6 = cosI4 * cosI2; - final double C5c2 = 1.0 / T2(cosI1); - final double C3c2 = 3.0 * cosI2 - 1.0; + // ae/a" + final double q = referenceRadius / app; - final double epp = mean.getE(); - final double epp2 = epp * epp; - final double epp3 = epp2 * epp; - final double epp4 = epp2 * epp2; + // γ2' + double ql = q * q; + double nl = n2 * n2; + yp2 = -0.5 * ck0[2] * ql / nl; + final double yp22 = yp2 * yp2; - if (epp >= 1) { - // Only for elliptical (e < 1) orbits - throw new OrekitException(OrekitMessages.TOO_LARGE_ECCENTRICITY_FOR_PROPAGATION_MODEL, - mean.getE()); - } + // γ3' + ql *= q; + nl *= n2; + final double yp3 = ck0[3] * ql / nl; - // secular multiplicative - lt = 1 + - 1.5 * yp2 * n * C3c2 + - 0.09375 * yp2 * yp2 * n * (-15.0 + 16.0 * n + 25.0 * n2 + (30.0 - 96.0 * n - 90.0 * n2) * cosI2 + (105.0 + 144.0 * n + 25.0 * n2) * cosI4) + - 0.9375 * yp4 * n * epp2 * (3.0 - 30.0 * cosI2 + 35.0 * cosI4); - - gt = -1.5 * yp2 * C5c2 + - 0.09375 * yp2 * yp2 * (-35.0 + 24.0 * n + 25.0 * n2 + (90.0 - 192.0 * n - 126.0 * n2) * cosI2 + (385.0 + 360.0 * n + 45.0 * n2) * cosI4) + - 0.3125 * yp4 * (21.0 - 9.0 * n2 + (-270.0 + 126.0 * n2) * cosI2 + (385.0 - 189.0 * n2) * cosI4 ); - - ht = -3.0 * yp2 * cosI1 + - 0.375 * yp2 * yp2 * ((-5.0 + 12.0 * n + 9.0 * n2) * cosI1 + (-35.0 - 36.0 * n - 5.0 * n2) * cosI3) + - 1.25 * yp4 * (5.0 - 3.0 * n2) * cosI1 * (3.0 - 7.0 * cosI2); - - final double cA = 1.0 - 11.0 * cosI2 - 40.0 * cosI4 / C5c2; - final double cB = 1.0 - 3.0 * cosI2 - 8.0 * cosI4 / C5c2; - final double cC = 1.0 - 9.0 * cosI2 - 24.0 * cosI4 / C5c2; - final double cD = 1.0 - 5.0 * cosI2 - 16.0 * cosI4 / C5c2; - - final double qyp2_4 = 3.0 * yp2 * yp2 * cA - - 10.0 * yp4 * cB; - final double qyp52 = epp3 * cosI1 * (0.5 * cD / sinI1 + - sinI1 * (5.0 + 32.0 * cosI2 / C5c2 + 80.0 * cosI4 / C5c2 / C5c2)); - final double qyp22 = 2.0 + epp2 - 11.0 * (2.0 + 3.0 * epp2) * cosI2 - - 40.0 * (2.0 + 5.0 * epp2) * cosI4 / C5c2 - - 400.0 * epp2 * cosI6 / C5c2 / C5c2; - final double qyp42 = ( qyp22 + 4.0 * (2.0 + epp2 - (2.0 + 3.0 * epp2) * cosI2) ) / 5.0; - final double qyp52bis = epp * cosI1 * sinI1 * - (4.0 + 3.0 * epp2) * - (3.0 + 16.0 * cosI2 / C5c2 + 40.0 * cosI4 / C5c2 / C5c2); - - // long periodic multiplicative - dei3sg = 35.0 / 96.0 * yp5 / yp2 * epp2 * n2 * cD * sinI1; - de2sg = -1.0 / 12.0 * epp * n2 / yp2 * qyp2_4; - deisg = ( -35.0 / 128.0 * yp5 / yp2 * epp2 * n2 * cD + - 1.0 / 4.0 * n2 / yp2 * (yp3 + 5.0 / 16.0 * yp5 * (4.0 + 3.0 * epp2) * cC)) * sinI1; - de = epp2 * n2 / 24.0 / yp2 * qyp2_4; - - final double qyp52quotient = epp * (-32.0 + 81.0 * epp4) / (4.0 + 3.0 * epp2 + n * (4.0 + 9.0 * epp2)); - dlgs2g = 1.0 / 48.0 / yp2 * (-3.0 * yp2 * yp2 * qyp22 + - 10.0 * yp4 * qyp42 ) + - n3 / yp2 * qyp2_4 / 24.0; - dlgc3g = 35.0 / 384.0 * yp5 / yp2 * n3 * epp * cD * sinI1 + - 35.0 / 1152.0 * yp5 / yp2 * (2.0 * qyp52 * cosI1 - epp * cD * sinI1 * (3.0 + 2.0 * epp2)); - dlgcg = -yp3 * epp * cosI2 / ( 4.0 * yp2 * sinI1) + - 0.078125 * yp5 / yp2 * (-epp * cosI2 / sinI1 * (4.0 + 3.0 * epp2) + epp2 * sinI1 * (26.0 + 9.0 * epp2)) * cC - - 0.46875 * yp5 / yp2 * qyp52bis * cosI1 + - 0.25 * yp3 / yp2 * sinI1 * epp / (1.0 + n3) * (3.0 - epp2 * (3.0 - epp2)) + - 0.078125 * yp5 / yp2 * n2 * cC * qyp52quotient * sinI1; - - - final double qyp24 = 3.0 * yp2 * yp2 * (11.0 + 80.0 * cosI2 / sinI1 + 200.0 * cosI4 / sinI2) - - 10.0 * yp4 * (3.0 + 16.0 * cosI2 / sinI1 + 40.0 * cosI4 / sinI2); - dh2sgcg = 35.0 / 144.0 * yp5 / yp2 * qyp52; - dhsgcg = -epp2 * cosI1 / (12.0 * yp2) * qyp24; - dhcg = -35.0 / 576.0 * yp5 / yp2 * qyp52 + - epp * cosI1 / (4.0 * yp2 * sinI1) * (yp3 + 0.3125 * yp5 * (4.0 + 3.0 * epp2) * cC) + - 1.875 / (4.0 * yp2) * yp5 * qyp52bis; - - // short periodic multiplicative - aC = -yp2 * C3c2 * app / n3; - aCbis = y2 * app * C3c2; - ac2g2f = y2 * app * 3.0 * sinI2; - - double qe = 0.5 * n2 * y2 * C3c2 / n6; - eC = qe * epp / (1.0 + n3) * (3.0 - epp2 * (3.0 - epp2)); - ecf = 3.0 * qe; - e2cf = 3.0 * epp * qe; - e3cf = epp2 * qe; - qe = 0.5 * n2 * y2 * 3.0 * (1.0 - cosI2) / n6; - ec2f2g = qe * epp; - ecfc2f2g = 3.0 * qe; - e2cfc2f2g = 3.0 * epp * qe; - e3cfc2f2g = epp2 * qe; - qe = -0.5 * yp2 * n2 * (1.0 - cosI2); - ec2gf = 3.0 * qe; - ec2g3f = qe; - - double qi = epp * yp2 * cosI1 * sinI1; - ide = -epp * cosI1 / (n2 * sinI1); - isfs2f2g = qi; - icfc2f2g = 2.0 * qi; - qi = yp2 * cosI1 * sinI1; - ic2f2g = 1.5 * qi; - - double qgl1 = 0.25 * yp2; - double qgl2 = 0.25 * yp2 * epp * n2 / (1.0 + n); - glf = qgl1 * -6.0 * C5c2; - gll = qgl1 * 6.0 * C5c2; - glsf = qgl1 * -6.0 * C5c2 * epp + - qgl2 * 2.0 * C3c2; - glosf = qgl2 * 2.0 * C3c2; - qgl1 = qgl1 * (3.0 - 5.0 * cosI2); - qgl2 = qgl2 * 3.0 * (1.0 - cosI2); - gls2f2g = 3.0 * qgl1; - gls2gf = 3.0 * epp * qgl1 + - qgl2; - glos2gf = -1.0 * qgl2; - gls2g3f = qgl1 * epp + - 1.0 / 3.0 * qgl2; - glos2g3f = qgl2; - - final double qh = 3.0 * yp2 * cosI1; - hf = -qh; - hl = qh; - hsf = -epp * qh; - hcfs2g2f = 2.0 * epp * yp2 * cosI1; - hs2g2f = 1.5 * yp2 * cosI1; - hsfc2g2f = -epp * yp2 * cosI1; - - final double qedl = -0.25 * yp2 * n3; - edls2g = 1.0 / 24.0 * epp * n3 / yp2 * qyp2_4; - edlcg = -0.25 * yp3 / yp2 * n3 * sinI1 - - 0.078125 * yp5 / yp2 * n3 * sinI1 * (4.0 + 9.0 * epp2) * cC; - edlc3g = 35.0 / 384.0 * yp5 / yp2 * n3 * epp2 * cD * sinI1; - edlsf = 2.0 * qedl * C3c2; - edls2gf = 3.0 * qedl * (1.0 - cosI2); - edls2g3f = 1.0 / 3.0 * qedl; - - // secular rates of the mean semi-major axis and eccentricity - // Eq. 2.41 and Eq. 2.45 of Phipps' 1992 thesis - aRate = -4.0 * app / (3.0 * xnotDot); - eRate = -4.0 * epp * n * n / (3.0 * xnotDot); + // γ4' + ql *= q; + nl *= n2; + final double yp4 = 0.375 * ck0[4] * ql / nl; + // γ5' + ql *= q; + nl *= n2; + final double yp5 = ck0[5] * ql / nl; + + // mean inclination I" sin & cos + final SinCos sci = FastMath.sinCos(mean.getI()); + si = sci.sin(); + ci = sci.cos(); + final double ci2 = ci * ci; + oneMci2 = 1.0 - ci2; + ci2X3M1 = 3.0 * ci2 - 1.0; + final double ci2X5M1 = 5.0 * ci2 - 1.0; + + // secular corrections + // true anomaly + dsl = 1.5 * yp2 * n * (ci2X3M1 + + 0.0625 * yp2 * (-15.0 + n * (16.0 + 25.0 * n) + + ci2 * (30.0 - n * (96.0 + 90.0 * n) + + ci2 * (105.0 + n * (144.0 + 25.0 * n))))) + + 0.9375 * yp4 * n * epp2 * (3.0 - ci2 * (30.0 - 35.0 * ci2)); + // perigee argument + dsg = 1.5 * yp2 * ci2X5M1 + + 0.09375 * yp22 * (-35.0 + n * (24.0 + 25.0 * n) + + ci2 * (90.0 - n * (192.0 + 126.0 * n) + + ci2 * (385.0 + n * (360.0 + 45.0 * n)))) + + 0.3125 * yp4 * (21.0 - 9.0 * n2 + ci2 * (-270.0 + 126.0 * n2 + + ci2 * (385.0 - 189.0 * n2))); + // right ascension of ascending node + dsh = (-3.0 * yp2 + + 0.375 * yp22 * (-5.0 + n * (12.0 + 9.0 * n) - + ci2 * (35.0 + n * (36.0 + 5.0 * n))) + + 1.25 * yp4 * (5.0 - 3.0 * n2) * (3.0 - 7.0 * ci2)) * ci; + + // secular rates of change due to drag + // Eq. 2.41 and Eq. 2.45 of Phipps' 1992 thesis + final double coef = -4.0 / (3.0 * n0 * (1 + dsl)); + aRate = coef * app; + eRate = coef * epp * n2; + + // singular term 1/(1 - 5 * cos²(I")) replaced by T2 function + final double t2 = T2(ci); + + // factors for long periodic corrections + final double fs12 = yp3 / yp2; + final double fs13 = 10. * yp4 / (3. * yp2); + final double fs14 = yp5 / yp2; + + final double ci2Xt2 = ci2 * t2; + final double cA = 1. - ci2 * (11. + 40. * ci2Xt2); + final double cB = 1. - ci2 * ( 3. + 8. * ci2Xt2); + final double cC = 1. - ci2 * ( 9. + 24. * ci2Xt2); + final double cD = 1. - ci2 * ( 5. + 16. * ci2Xt2); + final double cE = 1. - ci2 * (33. + 200. * ci2Xt2); + final double cF = 1. - ci2 * ( 9. + 40. * ci2Xt2); + + final double p5p = 1. + ci2Xt2 * (8. + 20 * ci2Xt2); + final double p5p2 = 1. + 2. * p5p; + final double p5p4 = 1. + 4. * p5p; + final double p5p10 = 1. + 10. * p5p; + + final double e2X3P4 = 4. + 3. * epp2; + final double ciO1Pci = ci / (1. + ci); + + final double q1 = 0.125 * (yp2 * cA - fs13 * cB); + final double q2 = 0.125 * epp2 * ci * (yp2 * p5p10 - fs13 * p5p2); + final double q5 = 0.25 * (fs12 + 0.3125 * e2X3P4 * fs14 * cC); + final double p2 = 0.46875 * p5p2 * epp * ci * si * e2X3P4 * fs14; + final double p3 = 0.15625 * epp * si * fs14 * cC; + final double kf = 35. / 1152.; + final double p4 = kf * epp * fs14 * cD; + final double p5 = 2. * kf * epp * epp2 * ci * si * fs14 * p5p4; + + vle1 = epp * n2 * q1; + vle2 = n2 * si * q5; + vle3 = -3.0 * epp * n2 * si * p4; + + vli1 = -epp * q1 / si; + vli2 = -epp * ci * q5; + vli3 = -3.0 * epp2 * ci * p4; + + vll2 = vle2 + 3.0 * epp * n2 * p3; + + vlh1I = -si * q2; + vlh2I = epp * ci * q5 + si * p2; + vlh3I = -epp2 * ci * p4 - si * p5; + + vls1 = (n3 - 1.0) * q1 - + q2 + + 25.0 * epp2 * ci2 * ci2Xt2 * ci2Xt2 * (yp2 - 0.2 * fs13) - + 0.0625 * epp2 * (yp2 * cE - fs13 * cF); + + vls2 = epp * si * (t8 + ciO1Pci) * q5 + + (11.0 + 3.0 * (epp2 - n3)) * p3 + + (1.0 - ci) * p2; + + vls3 = si * p4 * (3.0 * (n3 - 1.0) - epp2 * (2.0 + ciO1Pci)) - + (1.0 - ci) * p5; } /** - * Gets eccentric anomaly from mean anomaly. - * @param mk the mean anomaly (rad) - * @return the eccentric anomaly (rad) + * Get true anomaly from mean anomaly. + * @param lM the mean anomaly (rad) + * @param ecc the eccentricity + * @return the true anomaly (rad) */ - private UnivariateDerivative1 getEccentricAnomaly(final UnivariateDerivative1 mk) { + private UnivariateDerivative1 getTrueAnomaly(final UnivariateDerivative1 lM, + final UnivariateDerivative1 ecc) { // reduce M to [-PI PI] interval - final UnivariateDerivative1 reducedM = new UnivariateDerivative1(MathUtils.normalizeAngle(mk.getValue(), 0.0), - mk.getFirstDerivative()); + final double reducedM = MathUtils.normalizeAngle(lM.getValue(), 0.); - final UnivariateDerivative1 meanE = new UnivariateDerivative1(mean.getE(), 0.); - UnivariateDerivative1 ek = FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(meanE, mk); + // compute the true anomaly + UnivariateDerivative1 lV = FieldKeplerianAnomalyUtility.ellipticMeanToTrue(ecc, lM); // expand the result back to original range - ek = ek.add(mk.getValue() - reducedM.getValue()); + lV = lV.add(lM.getValue() - reducedM); - // Returns the eccentric anomaly - return ek; + // Returns the true anomaly + return lV; } /** @@ -1168,16 +1128,16 @@ private UnivariateDerivative1 getEccentricAnomaly(final UnivariateDerivative1 mk * critical inclination (i = 63.4°). *

      * This method, based on Warren Phipps's 1992 thesis (Eq. 2.47 and 2.48), - * approximate the factor (1.0 - 5.0 * cos²(inc))^-1 (causing the singularity) + * approximate the factor (1.0 - 5.0 * cos²(i))-1 (causing the singularity) * by a function, named T2 in the thesis. *

      - * @param cosInc cosine of the mean inclination - * @return an approximation of (1.0 - 5.0 * cos²(inc))^-1 term + * @param cosI cosine of the mean inclination + * @return an approximation of (1.0 - 5.0 * cos²(i))-1 term */ - private double T2(final double cosInc) { + private double T2(final double cosI) { - // X = (1.0 - 5.0 * cos²(inc)) - final double x = 1.0 - 5.0 * cosInc * cosInc; + // X = 1.0 - 5.0 * cos²(i) + final double x = 1.0 - 5.0 * cosI * cosI; final double x2 = x * x; // Eq. 2.48 @@ -1195,7 +1155,6 @@ private double T2(final double cosInc) { // Return (Eq. 2.47) return BETA * x * sum * product; - } /** Extrapolate an orbit up to a specific target date. @@ -1208,154 +1167,224 @@ public KeplerianOrbit propagateParameters(final AbsoluteDate date) { final double m2 = M2Driver.getValue(); // Keplerian evolution - final UnivariateDerivative1 dt = new UnivariateDerivative1(date.durationFrom(mean.getDate()), 1.0); - final UnivariateDerivative1 xnot = dt.multiply(xnotDot); + final UnivariateDerivative1 dt = new UnivariateDerivative1(date.durationFrom(mean.getDate()), 1.0); + final UnivariateDerivative1 not = dt.multiply(n0); - //____________________________________ - // secular effects - - // mean mean anomaly (with drag Eq. 2.38 of Phipps' 1992 thesis) final UnivariateDerivative1 dtM2 = dt.multiply(m2); final UnivariateDerivative1 dt2M2 = dt.multiply(dtM2); - final UnivariateDerivative1 lpp = new UnivariateDerivative1(MathUtils.normalizeAngle(mean.getMeanAnomaly() + lt * xnot.getValue() + dt2M2.getValue(), 0), - lt * xnotDot + 2.0 * dtM2.getValue()); - // mean argument of perigee - final UnivariateDerivative1 gpp = new UnivariateDerivative1(MathUtils.normalizeAngle(mean.getPerigeeArgument() + gt * xnot.getValue(), 0), - gt * xnotDot); - // mean longitude of ascending node - final UnivariateDerivative1 hpp = new UnivariateDerivative1(MathUtils.normalizeAngle(mean.getRightAscensionOfAscendingNode() + ht * xnot.getValue(), 0), - ht * xnotDot); - - // ________________________________________________ - // secular rates of the mean semi-major axis and eccentricity - - // semi-major axis - final UnivariateDerivative1 appDrag = dt.multiply(aRate * m2); - - // eccentricity - final UnivariateDerivative1 eppDrag = dt.multiply(eRate * m2); - - //____________________________________ - // Long periodical terms - final FieldSinCos sinCosCg1 = gpp.sinCos(); - final UnivariateDerivative1 cg1 = sinCosCg1.cos(); - final UnivariateDerivative1 sg1 = sinCosCg1.sin(); - final UnivariateDerivative1 c2g = cg1.square().subtract(sg1.square()); - final UnivariateDerivative1 s2g = cg1.multiply(sg1).add(sg1.multiply(cg1)); - final UnivariateDerivative1 c3g = c2g.multiply(cg1).subtract(s2g.multiply(sg1)); - final UnivariateDerivative1 sg2 = sg1.square(); - final UnivariateDerivative1 sg3 = sg1.multiply(sg2); - - - // de eccentricity - final UnivariateDerivative1 d1e = sg3.multiply(dei3sg). - add(sg1.multiply(deisg)). - add(sg2.multiply(de2sg)). - add(de); - - // l' + g' - final UnivariateDerivative1 lpPGp = s2g.multiply(dlgs2g). - add(c3g.multiply(dlgc3g)). - add(cg1.multiply(dlgcg)). - add(lpp). - add(gpp); - - // h' - final UnivariateDerivative1 hp = sg2.multiply(cg1).multiply(dh2sgcg). - add(sg1.multiply(cg1).multiply(dhsgcg)). - add(cg1.multiply(dhcg)). - add(hpp); - - // Short periodical terms - // eccentric anomaly - final UnivariateDerivative1 Ep = getEccentricAnomaly(lpp); - final FieldSinCos sinCosEp = Ep.sinCos(); - final UnivariateDerivative1 cf1 = (sinCosEp.cos().subtract(mean.getE())).divide(sinCosEp.cos().multiply(-mean.getE()).add(1.0)); - final UnivariateDerivative1 sf1 = (sinCosEp.sin().multiply(n)).divide(sinCosEp.cos().multiply(-mean.getE()).add(1.0)); - final UnivariateDerivative1 f = FastMath.atan2(sf1, cf1); - - final UnivariateDerivative1 cf2 = cf1.square(); - final UnivariateDerivative1 c2f = cf2.subtract(sf1.multiply(sf1)); - final UnivariateDerivative1 s2f = cf1.multiply(sf1).add(sf1.multiply(cf1)); - final UnivariateDerivative1 c3f = c2f.multiply(cf1).subtract(s2f.multiply(sf1)); - final UnivariateDerivative1 s3f = c2f.multiply(sf1).add(s2f.multiply(cf1)); - final UnivariateDerivative1 cf3 = cf1.multiply(cf2); - - final UnivariateDerivative1 c2g1f = cf1.multiply(c2g).subtract(sf1.multiply(s2g)); - final UnivariateDerivative1 c2g2f = c2f.multiply(c2g).subtract(s2f.multiply(s2g)); - final UnivariateDerivative1 c2g3f = c3f.multiply(c2g).subtract(s3f.multiply(s2g)); - final UnivariateDerivative1 s2g1f = cf1.multiply(s2g).add(c2g.multiply(sf1)); - final UnivariateDerivative1 s2g2f = c2f.multiply(s2g).add(c2g.multiply(s2f)); - final UnivariateDerivative1 s2g3f = c3f.multiply(s2g).add(c2g.multiply(s3f)); - - final UnivariateDerivative1 eE = (sinCosEp.cos().multiply(-mean.getE()).add(1.0)).reciprocal(); - final UnivariateDerivative1 eE3 = eE.square().multiply(eE); - final UnivariateDerivative1 sigma = eE.multiply(n * n).multiply(eE).add(eE); - - // Semi-major axis (with drag Eq. 2.41 of Phipps' 1992 thesis) - final UnivariateDerivative1 a = eE3.multiply(aCbis).add(appDrag.add(mean.getA())). - add(aC). - add(eE3.multiply(c2g2f).multiply(ac2g2f)); - - // Eccentricity (with drag Eq. 2.45 of Phipps' 1992 thesis) - final UnivariateDerivative1 e = d1e.add(eppDrag.add(mean.getE())). - add(eC). - add(cf1.multiply(ecf)). - add(cf2.multiply(e2cf)). - add(cf3.multiply(e3cf)). - add(c2g2f.multiply(ec2f2g)). - add(c2g2f.multiply(cf1).multiply(ecfc2f2g)). - add(c2g2f.multiply(cf2).multiply(e2cfc2f2g)). - add(c2g2f.multiply(cf3).multiply(e3cfc2f2g)). - add(c2g1f.multiply(ec2gf)). - add(c2g3f.multiply(ec2g3f)); - // Inclination - final UnivariateDerivative1 i = d1e.multiply(ide). - add(mean.getI()). - add(sf1.multiply(s2g2f.multiply(isfs2f2g))). - add(cf1.multiply(c2g2f.multiply(icfc2f2g))). - add(c2g2f.multiply(ic2f2g)); - - // Argument of perigee + mean anomaly - final UnivariateDerivative1 gPL = lpPGp.add(f.multiply(glf)). - add(lpp.multiply(gll)). - add(sf1.multiply(glsf)). - add(sigma.multiply(sf1).multiply(glosf)). - add(s2g2f.multiply(gls2f2g)). - add(s2g1f.multiply(gls2gf)). - add(sigma.multiply(s2g1f).multiply(glos2gf)). - add(s2g3f.multiply(gls2g3f)). - add(sigma.multiply(s2g3f).multiply(glos2g3f)); + // Secular corrections + // ------------------- + + // semi-major axis (with drag Eq. 2.41 of Phipps' 1992 thesis) + final UnivariateDerivative1 app = dtM2.multiply(aRate).add(mean.getA()); + + // eccentricity (with drag Eq. 2.45 of Phipps' 1992 thesis) reduced to [0, 1[ + final UnivariateDerivative1 tmp = dtM2.multiply(eRate).add(mean.getE()); + final UnivariateDerivative1 epp = tmp.withValue(FastMath.max(0., FastMath.min(tmp.getValue(), MAX_ECC))); + + // argument of perigee + final double gppVal = mean.getPerigeeArgument() + dsg * not.getValue(); + final UnivariateDerivative1 gpp = new UnivariateDerivative1(MathUtils.normalizeAngle(gppVal, 0.), + dsg * n0); + + // longitude of ascending node + final double hppVal = mean.getRightAscensionOfAscendingNode() + dsh * not.getValue(); + final UnivariateDerivative1 hpp = new UnivariateDerivative1(MathUtils.normalizeAngle(hppVal, 0.), + dsh * n0); + + // mean anomaly (with drag Eq. 2.38 of Phipps' 1992 thesis) + final double lppVal = mean.getMeanAnomaly() + (1. + dsl) * not.getValue() + dt2M2.getValue(); + final double dlppdt = (1. + dsl) * n0 + 2.0 * dtM2.getValue(); + final UnivariateDerivative1 lpp = new UnivariateDerivative1(MathUtils.normalizeAngle(lppVal, 0.), + dlppdt); + + // Long period corrections + //------------------------ + final FieldSinCos scgpp = gpp.sinCos(); + final UnivariateDerivative1 cgpp = scgpp.cos(); + final UnivariateDerivative1 sgpp = scgpp.sin(); + final FieldSinCos sc2gpp = gpp.multiply(2).sinCos(); + final UnivariateDerivative1 c2gpp = sc2gpp.cos(); + final UnivariateDerivative1 s2gpp = sc2gpp.sin(); + final FieldSinCos sc3gpp = gpp.multiply(3).sinCos(); + final UnivariateDerivative1 c3gpp = sc3gpp.cos(); + final UnivariateDerivative1 s3gpp = sc3gpp.sin(); + + // δ1e + final UnivariateDerivative1 d1e = c2gpp.multiply(vle1). + add(sgpp.multiply(vle2)). + add(s3gpp.multiply(vle3)); + + // δ1I + UnivariateDerivative1 d1I = sgpp.multiply(vli2). + add(s3gpp.multiply(vli3)); + // Pseudo singular term, not to add if Ipp is zero + if (Double.isFinite(vli1)) { + d1I = d1I.add(c2gpp.multiply(vli1)); + } + // e"δ1l + final UnivariateDerivative1 eppd1l = s2gpp.multiply(vle1). + subtract(cgpp.multiply(vll2)). + subtract(c3gpp.multiply(vle3)). + multiply(n); + + // δ1h + final UnivariateDerivative1 sIppd1h = s2gpp.multiply(vlh1I). + add(cgpp.multiply(vlh2I)). + add(c3gpp.multiply(vlh3I)); + + // δ1z = δ1l + δ1g + δ1h + final UnivariateDerivative1 d1z = s2gpp.multiply(vls1). + add(cgpp.multiply(vls2)). + add(c3gpp.multiply(vls3)); + + // Short period corrections + // ------------------------ + + // true anomaly + final UnivariateDerivative1 fpp = getTrueAnomaly(lpp, epp); + final FieldSinCos scfpp = fpp.sinCos(); + final UnivariateDerivative1 cfpp = scfpp.cos(); + final UnivariateDerivative1 sfpp = scfpp.sin(); + + // e"sin(f') + final UnivariateDerivative1 eppsfpp = epp.multiply(sfpp); + // e"cos(f') + final UnivariateDerivative1 eppcfpp = epp.multiply(cfpp); + // 1 + e"cos(f') + final UnivariateDerivative1 eppcfppP1 = eppcfpp.add(1.); + // 2 + e"cos(f') + final UnivariateDerivative1 eppcfppP2 = eppcfpp.add(2.); + // 3 + e"cos(f') + final UnivariateDerivative1 eppcfppP3 = eppcfpp.add(3.); + // (1 + e"cos(f'))³ + final UnivariateDerivative1 eppcfppP1_3 = eppcfppP1.square().multiply(eppcfppP1); + + // 2g" + final UnivariateDerivative1 g2 = gpp.multiply(2); + + // 2g" + f" + final UnivariateDerivative1 g2f = g2.add(fpp); + final FieldSinCos sc2gf = g2f.sinCos(); + final UnivariateDerivative1 c2gf = sc2gf.cos(); + final UnivariateDerivative1 s2gf = sc2gf.sin(); + final UnivariateDerivative1 eppc2gf = epp.multiply(c2gf); + final UnivariateDerivative1 epps2gf = epp.multiply(s2gf); + + // 2g" + 2f" + final UnivariateDerivative1 g2f2 = g2.add(fpp.multiply(2)); + final FieldSinCos sc2g2f = g2f2.sinCos(); + final UnivariateDerivative1 c2g2f = sc2g2f.cos(); + final UnivariateDerivative1 s2g2f = sc2g2f.sin(); + + // 2g" + 3f" + final UnivariateDerivative1 g2f3 = g2.add(fpp.multiply(3)); + final FieldSinCos sc2g3f = g2f3.sinCos(); + final UnivariateDerivative1 c2g3f = sc2g3f.cos(); + final UnivariateDerivative1 s2g3f = sc2g3f.sin(); + + // e"cos(2g" + 3f") + final UnivariateDerivative1 eppc2g3f = epp.multiply(c2g3f); + // e"sin(2g" + 3f") + final UnivariateDerivative1 epps2g3f = epp.multiply(s2g3f); + + // f" + e"sin(f") - l" + final UnivariateDerivative1 w17 = fpp.add(eppsfpp).subtract(lpp); + + // ((e"cos(f") + 3)e"cos(f") + 3)cos(f") + final UnivariateDerivative1 w20 = cfpp.multiply(eppcfppP3.multiply(eppcfpp).add(3.)); + + // 3sin(2g" + 2f") + 3e"sin(2g" + f") + e"sin(2g" + f") + final UnivariateDerivative1 w21 = s2g2f.add(epps2gf).multiply(3).add(epps2g3f); + + // (1 + e"cos(f"))(2 + e"cos(f"))/η² + final UnivariateDerivative1 w22 = eppcfppP1.multiply(eppcfppP2).divide(n2); + + // sinCos(I"/2) + final SinCos sci = FastMath.sinCos(0.5 * mean.getI()); + final double siO2 = sci.sin(); + final double ciO2 = sci.cos(); + + // δ2a + final UnivariateDerivative1 d2a = app.multiply(yp2 / n2). + multiply(eppcfppP1_3.subtract(n3).multiply(ci2X3M1). + add(eppcfppP1_3.multiply(c2g2f).multiply(3 * oneMci2))); + + // δ2e + final UnivariateDerivative1 d2e = (w20.add(epp.multiply(t8))).multiply(ci2X3M1). + add((w20.add(epp.multiply(c2g2f))).multiply(3 * oneMci2)). + subtract((eppc2gf.multiply(3).add(eppc2g3f)).multiply(n2 * oneMci2)). + multiply(0.5 * yp2); + + // δ2I + final UnivariateDerivative1 d2I = ((c2g2f.add(eppc2gf)).multiply(3).add(eppc2g3f)). + multiply(0.5 * yp2 * ci * si); + + // e"δ2l + final UnivariateDerivative1 eppd2l = (w22.add(1).multiply(sfpp).multiply(2 * oneMci2). + add((w22.subtract(1).negate().multiply(s2gf)). + add(w22.add(1. / 3.).multiply(s2g3f)). + multiply(3 * oneMci2))). + multiply(0.25 * yp2 * n3).negate(); + + // sinI"δ2h + final UnivariateDerivative1 sIppd2h = (w21.subtract(w17.multiply(6))). + multiply(0.5 * yp2 * ci * si); + + // δ2z = δ2l + δ2g + δ2h + final UnivariateDerivative1 d2z = (epp.multiply(eppd2l).multiply(t8 - 1.).divide(n3). + add(w17.multiply(6. * (1. + ci * (2 - 5. * ci))) + .subtract(w21.multiply(3. + ci * (2 - 5. * ci))).multiply(0.25 * yp2))). + negate(); + + // Assembling elements + // ------------------- + + // e" + δe + final UnivariateDerivative1 de = epp.add(d1e).add(d2e); + + // e"δl + final UnivariateDerivative1 dl = eppd1l.add(eppd2l); + + // sin(I"/2)δh = sin(I")δh / cos(I"/2) (singular for I" = π, very unlikely) + final UnivariateDerivative1 dh = sIppd1h.add(sIppd2h).divide(2. * ciO2); + + // δI + final UnivariateDerivative1 di = d1I.add(d2I).multiply(0.5 * ciO2).add(siO2); + + // z = l" + g" + h" + δ1z + δ2z + final UnivariateDerivative1 z = lpp.add(gpp).add(hpp).add(d1z).add(d2z); + + // Osculating elements + // ------------------- - // Longitude of ascending node - final UnivariateDerivative1 h = hp.add(f.multiply(hf)). - add(lpp.multiply(hl)). - add(sf1.multiply(hsf)). - add(cf1.multiply(s2g2f).multiply(hcfs2g2f)). - add(s2g2f.multiply(hs2g2f)). - add(c2g2f.multiply(sf1).multiply(hsfc2g2f)); - - final UnivariateDerivative1 edl = s2g.multiply(edls2g). - add(cg1.multiply(edlcg)). - add(c3g.multiply(edlc3g)). - add(sf1.multiply(edlsf)). - add(s2g1f.multiply(edls2gf)). - add(s2g3f.multiply(edls2g3f)). - add(sf1.multiply(sigma).multiply(edlsf)). - add(s2g1f.multiply(sigma).multiply(-edls2gf)). - add(s2g3f.multiply(sigma).multiply(3.0 * edls2g3f)); - - final FieldSinCos sinCosLpp = lpp.sinCos(); - final UnivariateDerivative1 A = e.multiply(sinCosLpp.cos()).subtract(edl.multiply(sinCosLpp.sin())); - final UnivariateDerivative1 B = e.multiply(sinCosLpp.sin()).add(edl.multiply(sinCosLpp.cos())); + // Semi-major axis + final UnivariateDerivative1 a = app.add(d2a); + + // Eccentricity + final UnivariateDerivative1 e = FastMath.sqrt(de.square().add(dl.square())); // Mean anomaly - final UnivariateDerivative1 l = FastMath.atan2(B, A); + final FieldSinCos sclpp = lpp.sinCos(); + final UnivariateDerivative1 clpp = sclpp.cos(); + final UnivariateDerivative1 slpp = sclpp.sin(); + final UnivariateDerivative1 l = FastMath.atan2(de.multiply(slpp).add(dl.multiply(clpp)), + de.multiply(clpp).subtract(dl.multiply(slpp))); + + // Inclination + final UnivariateDerivative1 i = FastMath.acos(di.square().add(dh.square()).multiply(2).negate().add(1.)); + + // Longitude of ascending node + final FieldSinCos schpp = hpp.sinCos(); + final UnivariateDerivative1 chpp = schpp.cos(); + final UnivariateDerivative1 shpp = schpp.sin(); + final UnivariateDerivative1 h = FastMath.atan2(di.multiply(shpp).add(dh.multiply(chpp)), + di.multiply(chpp).subtract(dh.multiply(shpp))); // Argument of perigee - final UnivariateDerivative1 g = gPL.subtract(l); + final UnivariateDerivative1 g = z.subtract(l).subtract(h); // Return a Keplerian orbit return new KeplerianOrbit(a.getValue(), e.getValue(), i.getValue(), diff --git a/src/main/java/org/orekit/propagation/analytical/FieldAbstractAnalyticalPropagator.java b/src/main/java/org/orekit/propagation/analytical/FieldAbstractAnalyticalPropagator.java index b917c4f0cf..7f5751872f 100644 --- a/src/main/java/org/orekit/propagation/analytical/FieldAbstractAnalyticalPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/FieldAbstractAnalyticalPropagator.java @@ -167,6 +167,11 @@ public FieldSpacecraftState propagate(final FieldAbsoluteDate start, final } while (!isLastStep); + // Finalize event detectors + for (final FieldEventState es : eventsStates) { + es.finish(state); + } + // return the last computed state lastPropagationEnd = state.getDate(); setStartDate(state.getDate()); diff --git a/src/main/java/org/orekit/propagation/analytical/FieldBrouwerLyddanePropagator.java b/src/main/java/org/orekit/propagation/analytical/FieldBrouwerLyddanePropagator.java index 29c8feeedf..af3b0fc67c 100644 --- a/src/main/java/org/orekit/propagation/analytical/FieldBrouwerLyddanePropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/FieldBrouwerLyddanePropagator.java @@ -32,9 +32,10 @@ import org.orekit.errors.OrekitMessages; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics; +import org.orekit.orbits.FieldEquinoctialOrbit; +import org.orekit.orbits.FieldKeplerianAnomalyUtility; import org.orekit.orbits.FieldKeplerianOrbit; import org.orekit.orbits.FieldOrbit; -import org.orekit.orbits.FieldKeplerianAnomalyUtility; import org.orekit.orbits.OrbitType; import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.FieldSpacecraftState; @@ -80,30 +81,31 @@ * @see "Phipps Jr, Warren E. Parallelization of the Navy Space Surveillance Center * (NAVSPASUR) Satellite Model. NAVAL POSTGRADUATE SCHOOL MONTEREY CA, 1992." * + * @see "Solomon, Daniel, THE NAVSPASUR Satellite Motion Model, + * Naval Research Laboratory, August 8, 1991." + * * @author Melina Vanel * @author Bryan Cazabonne + * @author Pascal Parraud * @since 11.1 * @param type of the field elements */ public class FieldBrouwerLyddanePropagator> extends FieldAbstractAnalyticalPropagator { - /** Default convergence threshold for mean parameters conversion. */ - private static final double EPSILON_DEFAULT = 1.0e-13; - - /** Default value for maxIterations. */ - private static final int MAX_ITERATIONS_DEFAULT = 200; - /** Parameters scaling factor. *

      * We use a power of 2 to avoid numeric noise introduction * in the multiplications/divisions sequences. *

      */ - private static final double SCALE = FastMath.scalb(1.0, -20); + private static final double SCALE = FastMath.scalb(1.0, -32); /** Beta constant used by T2 function. */ private static final double BETA = FastMath.scalb(100, -11); + /** Max value for the eccentricity. */ + private static final double MAX_ECC = 0.999999; + /** Initial Brouwer-Lyddane model. */ private FieldBLModel initialModel; @@ -399,7 +401,8 @@ public FieldBrouwerLyddanePropagator(final FieldOrbit initialOrbit, final UnnormalizedSphericalHarmonicsProvider provider, final PropagationType initialType, final double M2) { - this(initialOrbit, attitudeProv, mass, provider, provider.onDate(initialOrbit.getDate().toAbsoluteDate()), initialType, M2); + this(initialOrbit, attitudeProv, mass, provider, + provider.onDate(initialOrbit.getDate().toAbsoluteDate()), initialType, M2); } /** @@ -466,7 +469,9 @@ public FieldBrouwerLyddanePropagator(final FieldOrbit initialOrbit, final PropagationType initialType, final double M2) { this(initialOrbit, attitudeProv, mass, referenceRadius, mu, - c20, c30, c40, c50, initialType, M2, EPSILON_DEFAULT, MAX_ITERATIONS_DEFAULT); + c20, c30, c40, c50, initialType, M2, + BrouwerLyddanePropagator.EPSILON_DEFAULT, + BrouwerLyddanePropagator.MAX_ITERATIONS_DEFAULT); } /** Build a propagator from orbit, attitude provider, mass and potential. @@ -522,11 +527,11 @@ public FieldBrouwerLyddanePropagator(final FieldOrbit initialOrbit, // compute mean parameters if needed resetInitialState(new FieldSpacecraftState<>(initialOrbit, - attitudeProv.getAttitude(initialOrbit, - initialOrbit.getDate(), - initialOrbit.getFrame()), - mass), - initialType, epsilon, maxIterations); + attitudeProv.getAttitude(initialOrbit, + initialOrbit.getDate(), + initialOrbit.getFrame()), + mass), + initialType, epsilon, maxIterations); } @@ -557,7 +562,9 @@ public static > FieldKeplerianOrbit compute final UnnormalizedSphericalHarmonicsProvider provider, final UnnormalizedSphericalHarmonics harmonics, final double M2Value) { - return computeMeanOrbit(osculating, provider, harmonics, M2Value, EPSILON_DEFAULT, MAX_ITERATIONS_DEFAULT); + return computeMeanOrbit(osculating, provider, harmonics, M2Value, + BrouwerLyddanePropagator.EPSILON_DEFAULT, + BrouwerLyddanePropagator.MAX_ITERATIONS_DEFAULT); } /** Conversion from osculating to mean orbit. @@ -629,10 +636,15 @@ public static > FieldKeplerianOrbit compute * @since 11.2 */ public static > FieldKeplerianOrbit computeMeanOrbit(final FieldOrbit osculating, - final double referenceRadius, final double mu, - final double c20, final double c30, final double c40, - final double c50, final double M2Value, - final double epsilon, final int maxIterations) { + final double referenceRadius, + final double mu, + final double c20, + final double c30, + final double c40, + final double c50, + final double M2Value, + final double epsilon, + final int maxIterations) { final FieldBrouwerLyddanePropagator propagator = new FieldBrouwerLyddanePropagator<>(osculating, FrameAlignedProvider.of(osculating.getFrame()), @@ -659,7 +671,9 @@ public void resetInitialState(final FieldSpacecraftState state) { * @param stateType mean Brouwer-Lyddane orbit or osculating orbit */ public void resetInitialState(final FieldSpacecraftState state, final PropagationType stateType) { - resetInitialState(state, stateType, EPSILON_DEFAULT, MAX_ITERATIONS_DEFAULT); + resetInitialState(state, stateType, + BrouwerLyddanePropagator.EPSILON_DEFAULT, + BrouwerLyddanePropagator.MAX_ITERATIONS_DEFAULT); } /** Reset the propagator initial state. @@ -682,7 +696,9 @@ public void resetInitialState(final FieldSpacecraftState state, final Propaga /** {@inheritDoc} */ @Override protected void resetIntermediateState(final FieldSpacecraftState state, final boolean forward) { - resetIntermediateState(state, forward, EPSILON_DEFAULT, MAX_ITERATIONS_DEFAULT); + resetIntermediateState(state, forward, + BrouwerLyddanePropagator.EPSILON_DEFAULT, + BrouwerLyddanePropagator.MAX_ITERATIONS_DEFAULT); } /** Reset an intermediate state. @@ -716,56 +732,78 @@ protected void resetIntermediateState(final FieldSpacecraftState state, final private FieldBLModel computeMeanParameters(final FieldKeplerianOrbit osculating, final T mass, final double epsilon, final int maxIterations) { + // damping factor + final double damping = BrouwerLyddanePropagator.DAMPING_DEFAULT; + // sanity check if (osculating.getA().getReal() < referenceRadius) { throw new OrekitException(OrekitMessages.TRAJECTORY_INSIDE_BRILLOUIN_SPHERE, osculating.getA()); } - final Field field = mass.getField(); - final T one = field.getOne(); + final Field field = osculating.getDate().getField(); final T zero = field.getZero(); + final T pi = zero.getPi(); + // rough initialization of the mean parameters FieldBLModel current = new FieldBLModel<>(osculating, mass, referenceRadius, mu, ck0); + // Get equinoctial parameters + T sma = osculating.getA(); + T ex = osculating.getEquinoctialEx(); + T ey = osculating.getEquinoctialEy(); + T hx = osculating.getHx(); + T hy = osculating.getHy(); + T lv = osculating.getLv(); + // threshold for each parameter - final T thresholdA = current.mean.getA().abs().add(1.0).multiply(epsilon); - final T thresholdE = current.mean.getE().add(1.0).multiply(epsilon); - final T thresholdAngles = one.getPi().multiply(epsilon); + final T thresholdA = osculating.getA().abs().add(1).multiply(epsilon); + final T thresholdE = FastMath.hypot(ex, ey).add(1).multiply(epsilon); + final T thresholdH = FastMath.hypot(hx, hy).add(1).multiply(epsilon); + final T thresholdLv = pi.multiply(epsilon); int i = 0; while (i++ < maxIterations) { // recompute the osculating parameters from the current mean parameters - final FieldKeplerianOrbit parameters = current.propagateParameters(current.mean.getDate(), getParameters(mass.getField(), current.mean.getDate())); + final FieldKeplerianOrbit parameters = current.propagateParameters(current.mean.getDate(), + getParameters(field, + current.mean.getDate())); // adapted parameters residuals - final T deltaA = osculating.getA() .subtract(parameters.getA()); - final T deltaE = osculating.getE() .subtract(parameters.getE()); - final T deltaI = osculating.getI() .subtract(parameters.getI()); - final T deltaOmega = MathUtils.normalizeAngle(osculating.getPerigeeArgument().subtract(parameters.getPerigeeArgument()), zero); - final T deltaRAAN = MathUtils.normalizeAngle(osculating.getRightAscensionOfAscendingNode().subtract(parameters.getRightAscensionOfAscendingNode()), zero); - final T deltaAnom = MathUtils.normalizeAngle(osculating.getMeanAnomaly().subtract(parameters.getMeanAnomaly()), zero); - + final T deltaA = osculating.getA().subtract(parameters.getA()); + final T deltaEx = osculating.getEquinoctialEx().subtract(parameters.getEquinoctialEx()); + final T deltaEy = osculating.getEquinoctialEy().subtract(parameters.getEquinoctialEy()); + final T deltaHx = osculating.getHx().subtract(parameters.getHx()); + final T deltaHy = osculating.getHy().subtract(parameters.getHy()); + final T deltaLv = MathUtils.normalizeAngle(osculating.getLv().subtract(parameters.getLv()), zero); + + // update state + sma = sma.add(deltaA.multiply(damping)); + ex = ex.add(deltaEx.multiply(damping)); + ey = ey.add(deltaEy.multiply(damping)); + hx = hx.add(deltaHx.multiply(damping)); + hy = hy.add(deltaHy.multiply(damping)); + lv = lv.add(deltaLv.multiply(damping)); + + // Update mean orbit + final FieldEquinoctialOrbit meanEquinoctial = + new FieldEquinoctialOrbit<>(sma, ex, ey, hx, hy, lv, PositionAngleType.TRUE, + osculating.getFrame(), osculating.getDate(), + osculating.getMu()); + final FieldKeplerianOrbit meanKeplerian = + (FieldKeplerianOrbit) OrbitType.KEPLERIAN.convertType(meanEquinoctial); // update mean parameters - current = new FieldBLModel<>(new FieldKeplerianOrbit<>(current.mean.getA() .add(deltaA), - FastMath.max(current.mean.getE().add(deltaE), zero), - current.mean.getI() .add(deltaI), - current.mean.getPerigeeArgument() .add(deltaOmega), - current.mean.getRightAscensionOfAscendingNode().add(deltaRAAN), - current.mean.getMeanAnomaly() .add(deltaAnom), - PositionAngleType.MEAN, PositionAngleType.MEAN, - current.mean.getFrame(), - current.mean.getDate(), mu), - mass, referenceRadius, mu, ck0); + current = new FieldBLModel<>(meanKeplerian, mass, referenceRadius, mu, ck0); + // check convergence - if (FastMath.abs(deltaA.getReal()) < thresholdA.getReal() && - FastMath.abs(deltaE.getReal()) < thresholdE.getReal() && - FastMath.abs(deltaI.getReal()) < thresholdAngles.getReal() && - FastMath.abs(deltaOmega.getReal()) < thresholdAngles.getReal() && - FastMath.abs(deltaRAAN.getReal()) < thresholdAngles.getReal() && - FastMath.abs(deltaAnom.getReal()) < thresholdAngles.getReal()) { + if (FastMath.abs(deltaA.getReal()) < thresholdA.getReal() && + FastMath.abs(deltaEx.getReal()) < thresholdE.getReal() && + FastMath.abs(deltaEy.getReal()) < thresholdE.getReal() && + FastMath.abs(deltaHx.getReal()) < thresholdH.getReal() && + FastMath.abs(deltaHy.getReal()) < thresholdH.getReal() && + FastMath.abs(deltaLv.getReal()) < thresholdLv.getReal()) { return current; } } @@ -800,99 +838,65 @@ public double getM2(final AbsoluteDate date) { /** Local class for Brouwer-Lyddane model. */ private static class FieldBLModel> { - /** Mean orbit. */ - private final FieldKeplerianOrbit mean; - /** Constant mass. */ private final T mass; /** Central attraction coefficient. */ private final T mu; - // CHECKSTYLE: stop JavadocVariable check - - // preprocessed values - - // Constant for secular terms l'', g'', h'' - // l standing for mean anomaly, g for perigee argument and h for raan - private final T xnotDot; - private final T n; - private final T lt; - private final T gt; - private final T ht; - - - // Long periodT - private final T dei3sg; - private final T de2sg; - private final T deisg; - private final T de; - - - private final T dlgs2g; - private final T dlgc3g; - private final T dlgcg; - - - private final T dh2sgcg; - private final T dhsgcg; - private final T dhcg; - - - // Short perioTs - private final T aC; - private final T aCbis; - private final T ac2g2f; - - - private final T eC; - private final T ecf; - private final T e2cf; - private final T e3cf; - private final T ec2f2g; - private final T ecfc2f2g; - private final T e2cfc2f2g; - private final T e3cfc2f2g; - private final T ec2gf; - private final T ec2g3f; - - - private final T ide; - private final T isfs2f2g; - private final T icfc2f2g; - private final T ic2f2g; - - - private final T glf; - private final T gll; - private final T glsf; - private final T glosf; - private final T gls2f2g; - private final T gls2gf; - private final T glos2gf; - private final T gls2g3f; - private final T glos2g3f; - - - private final T hf; - private final T hl; - private final T hsf; - private final T hcfs2g2f; - private final T hs2g2f; - private final T hsfc2g2f; + /** Brouwer-Lyddane mean orbit. */ + private final FieldKeplerianOrbit mean; + // Preprocessed values - private final T edls2g; - private final T edlcg; - private final T edlc3g; - private final T edlsf; - private final T edls2gf; - private final T edls2g3f; + /** Mean mean motion: n0 = √(μ/a")/a". */ + private final T n0; - // Drag terms + /** η = √(1 - e"²). */ + private final T n; + /** η². */ + private final T n2; + /** η³. */ + private final T n3; + /** η + 1 / (1 + η). */ + private final T t8; + + /** Secular correction for mean anomaly l: δsl. */ + private final T dsl; + /** Secular correction for perigee argument g: δsg. */ + private final T dsg; + /** Secular correction for raan h: δsh. */ + private final T dsh; + + /** Secular rate of change of semi-major axis due to drag. */ private final T aRate; + /** Secular rate of change of eccentricity due to drag. */ private final T eRate; + // CHECKSTYLE: stop JavadocVariable check + + // Storage for speed-up + private final T yp2; + private final T ci; + private final T si; + private final T oneMci2; + private final T ci2X3M1; + + // Long periodic corrections factors + private final T vle1; + private final T vle2; + private final T vle3; + private final T vli1; + private final T vli2; + private final T vli3; + private final T vll2; + private final T vlh1I; + private final T vlh2I; + private final T vlh3I; + private final T vls1; + private final T vls2; + private final T vls3; + // CHECKSTYLE: resume JavadocVariable check /** Create a model for specified mean orbit. @@ -903,217 +907,206 @@ private static class FieldBLModel> { * @param ck0 un-normalized zonal coefficients */ FieldBLModel(final FieldKeplerianOrbit mean, final T mass, - final double referenceRadius, final T mu, final double[] ck0) { + final double referenceRadius, final T mu, final double[] ck0) { - this.mean = mean; this.mass = mass; this.mu = mu; - final T one = mass.getField().getOne(); + // mean orbit + this.mean = mean; + + final T one = mass.getField().getOne(); + + // mean eccentricity e" + final T epp = mean.getE(); + if (epp.getReal() >= 1) { + // Only for elliptical (e < 1) orbits + throw new OrekitException(OrekitMessages.TOO_LARGE_ECCENTRICITY_FOR_PROPAGATION_MODEL, + epp.getReal()); + } + final T epp2 = epp.square(); + + // η + n2 = one.subtract(epp2); + n = n2.sqrt(); + n3 = n2.multiply(n); + t8 = n.add(one.add(n).reciprocal()); + + // mean semi-major axis a" final T app = mean.getA(); - xnotDot = mu.divide(app).sqrt().divide(app); - // preliminary processing + // mean mean motion + n0 = mu.divide(app).sqrt().divide(app); + + // ae/a" final T q = app.divide(referenceRadius).reciprocal(); - T ql = q.square(); - final T y2 = ql.multiply(-0.5 * ck0[2]); - n = ((mean.getE().square().negate()).add(1.0)).sqrt(); - final T n2 = n.square(); - final T n3 = n2.multiply(n); - final T n4 = n2.square(); - final T n6 = n4.multiply(n2); - final T n8 = n4.square(); - final T n10 = n8.multiply(n2); + // γ2' + T ql = q.square(); + T nl = n2.square(); + yp2 = ql.multiply(-0.5 * ck0[2]).divide(nl); + final T yp22 = yp2.square(); - final T yp2 = y2.divide(n4); + // γ3' ql = ql.multiply(q); - final T yp3 = ql.multiply(ck0[3]).divide(n6); - ql = ql.multiply(q); - final T yp4 = ql.multiply(0.375 * ck0[4]).divide(n8); + nl = nl.multiply(n2); + final T yp3 = ql.multiply(ck0[3]).divide(nl); + + // γ4' ql = ql.multiply(q); - final T yp5 = ql.multiply(ck0[5]).divide(n10); + nl = nl.multiply(n2); + final T yp4 = ql.multiply(0.375 * ck0[4]).divide(nl); + // γ5' + ql = ql.multiply(q); + nl = nl.multiply(n2); + final T yp5 = ql.multiply(ck0[5]).divide(nl); + // mean inclination I" sin & cos final FieldSinCos sc = FastMath.sinCos(mean.getI()); - final T sinI1 = sc.sin(); - final T sinI2 = sinI1.square(); - final T cosI1 = sc.cos(); - final T cosI2 = cosI1.square(); - final T cosI3 = cosI2.multiply(cosI1); - final T cosI4 = cosI2.square(); - final T cosI6 = cosI4.multiply(cosI2); - final T C5c2 = T2(cosI1).reciprocal(); - final T C3c2 = cosI2.multiply(3.0).subtract(1.0); - - final T epp = mean.getE(); - final T epp2 = epp.square(); - final T epp3 = epp2.multiply(epp); - final T epp4 = epp2.square(); - - if (epp.getReal() >= 1) { - // Only for elliptical (e < 1) orbits - throw new OrekitException(OrekitMessages.TOO_LARGE_ECCENTRICITY_FOR_PROPAGATION_MODEL, - mean.getE().getReal()); - } - - // secular multiplicative - lt = one.add(yp2.multiply(n).multiply(C3c2).multiply(1.5)). - add(yp2.multiply(0.09375).multiply(yp2).multiply(n).multiply(n2.multiply(25.0).add(n.multiply(16.0)).add(-15.0). - add(cosI2.multiply(n2.multiply(-90.0).add(n.multiply(-96.0)).add(30.0))). - add(cosI4.multiply(n2.multiply(25.0).add(n.multiply(144.0)).add(105.0))))). - add(yp4.multiply(0.9375).multiply(n).multiply(epp2).multiply(cosI4.multiply(35.0).add(cosI2.multiply(-30.0)).add(3.0))); - - gt = yp2.multiply(-1.5).multiply(C5c2). - add(yp2.multiply(0.09375).multiply(yp2).multiply(n2.multiply(25.0).add(n.multiply(24.0)).add(-35.0). - add(cosI2.multiply(n2.multiply(-126.0).add(n.multiply(-192.0)).add(90.0))). - add(cosI4.multiply(n2.multiply(45.0).add(n.multiply(360.0)).add(385.0))))). - add(yp4.multiply(0.3125).multiply(n2.multiply(-9.0).add(21.0). - add(cosI2.multiply(n2.multiply(126.0).add(-270.0))). - add(cosI4.multiply(n2.multiply(-189.0).add(385.0))))); - - ht = yp2.multiply(-3.0).multiply(cosI1). - add(yp2.multiply(0.375).multiply(yp2).multiply(cosI1.multiply(n2.multiply(9.0).add(n.multiply(12.0)).add(-5.0)). - add(cosI3.multiply(n2.multiply(-5.0).add(n.multiply(-36.0)).add(-35.0))))). - add(yp4.multiply(1.25).multiply(cosI1).multiply(n2.multiply(-3.0).add(5.0)).multiply(cosI2.multiply(-7.0).add(3.0))); - - final T cA = one.subtract(cosI2.multiply(11.0)).subtract(cosI4.multiply(40.0).divide(C5c2)); - final T cB = one.subtract(cosI2.multiply(3.0)).subtract(cosI4.multiply(8.0).divide(C5c2)); - final T cC = one.subtract(cosI2.multiply(9.0)).subtract(cosI4.multiply(24.0).divide(C5c2)); - final T cD = one.subtract(cosI2.multiply(5.0)).subtract(cosI4.multiply(16.0).divide(C5c2)); - - final T qyp2_4 = yp2.multiply(3.0).multiply(yp2).multiply(cA). - subtract(yp4.multiply(10.0).multiply(cB)); - final T qyp52 = cosI1.multiply(epp3).multiply(cD.multiply(0.5).divide(sinI1). - add(sinI1.multiply(cosI4.divide(C5c2).divide(C5c2).multiply(80.0). - add(cosI2.divide(C5c2).multiply(32.0). - add(5.0))))); - final T qyp22 = epp2.add(2.0).subtract(epp2.multiply(3.0).add(2.0).multiply(11.0).multiply(cosI2)). - subtract(epp2.multiply(5.0).add(2.0).multiply(40.0).multiply(cosI4.divide(C5c2))). - subtract(epp2.multiply(400.0).multiply(cosI6).divide(C5c2).divide(C5c2)); - final T qyp42 = one.divide(5.0).multiply(qyp22. - add(one.newInstance(4.0).multiply(epp2. - add(2.0). - subtract(cosI2.multiply(epp2.multiply(3.0).add(2.0)))))); - final T qyp52bis = cosI1.multiply(sinI1).multiply(epp).multiply(epp2.multiply(3.0).add(4.0)). - multiply(cosI4.divide(C5c2).divide(C5c2).multiply(40.0). - add(cosI2.divide(C5c2).multiply(16.0)). - add(3.0)); - // long periodic multiplicative - dei3sg = yp5.divide(yp2).multiply(35.0 / 96.0).multiply(epp2).multiply(n2).multiply(cD).multiply(sinI1); - de2sg = qyp2_4.divide(yp2).multiply(epp).multiply(n2).multiply(-1.0 / 12.0); - deisg = sinI1.multiply(yp5.multiply(-35.0 / 128.0).divide(yp2).multiply(epp2).multiply(n2).multiply(cD). - add(n2.multiply(0.25).divide(yp2).multiply(yp3.add(yp5.multiply(5.0 / 16.0).multiply(epp2.multiply(3.0).add(4.0)).multiply(cC))))); - de = epp2.multiply(n2).multiply(qyp2_4).divide(24.0).divide(yp2); - - final T qyp52quotient = epp.multiply(epp4.multiply(81.0).add(-32.0)).divide(n.multiply(epp2.multiply(9.0).add(4.0)).add(epp2.multiply(3.0)).add(4.0)); - dlgs2g = yp2.multiply(48.0).reciprocal().multiply(yp2.multiply(-3.0).multiply(yp2).multiply(qyp22). - add(yp4.multiply(10.0).multiply(qyp42))). - add(n3.divide(yp2).multiply(qyp2_4).divide(24.0)); - dlgc3g = yp5.multiply(35.0 / 384.0).divide(yp2).multiply(n3).multiply(epp).multiply(cD).multiply(sinI1). - add(yp5.multiply(35.0 / 1152.0).divide(yp2).multiply(qyp52.multiply(2.0).multiply(cosI1).subtract(epp.multiply(cD).multiply(sinI1).multiply(epp2.multiply(2.0).add(3.0))))); - dlgcg = yp3.negate().multiply(cosI2).multiply(epp).divide(yp2.multiply(sinI1).multiply(4.0)). - add(yp5.divide(yp2).multiply(0.078125).multiply(cC).multiply(cosI2.divide(sinI1).multiply(epp.negate()).multiply(epp2.multiply(3.0).add(4.0)). - add(sinI1.multiply(epp2).multiply(epp2.multiply(9.0).add(26.0)))). - subtract(yp5.divide(yp2).multiply(0.46875).multiply(qyp52bis).multiply(cosI1)). - add(yp3.divide(yp2).multiply(0.25).multiply(sinI1).multiply(epp).divide(n3.add(1.0)).multiply(epp2.multiply(epp2.subtract(3.0)).add(3.0))). - add(yp5.divide(yp2).multiply(0.078125).multiply(n2).multiply(cC).multiply(qyp52quotient).multiply(sinI1))); - final T qyp24 = yp2.multiply(yp2).multiply(3.0).multiply(cosI4.divide(sinI2).multiply(200.0).add(cosI2.divide(sinI1).multiply(80.0)).add(11.0)). - subtract(yp4.multiply(10.0).multiply(cosI4.divide(sinI2).multiply(40.0).add(cosI2.divide(sinI1).multiply(16.0)).add(3.0))); - dh2sgcg = yp5.divide(yp2).multiply(qyp52).multiply(35.0 / 144.0); - dhsgcg = qyp24.multiply(cosI1).multiply(epp2.negate()).divide(yp2.multiply(12.0)); - dhcg = yp5.divide(yp2).multiply(qyp52).multiply(-35.0 / 576.0). - add(cosI1.multiply(epp).divide(yp2.multiply(sinI1).multiply(4.0)).multiply(yp3.add(yp5.multiply(0.3125).multiply(cC).multiply(epp2.multiply(3.0).add(4.0))))). - add(yp5.multiply(qyp52bis).multiply(1.875).divide(yp2.multiply(4.0))); - - // short periodic multiplicative - aC = yp2.negate().multiply(C3c2).multiply(app).divide(n3); - aCbis = y2.multiply(app).multiply(C3c2); - ac2g2f = y2.multiply(app).multiply(sinI2).multiply(3.0); - - T qe = y2.multiply(C3c2).multiply(0.5).multiply(n2).divide(n6); - eC = qe.multiply(epp).divide(n3.add(1.0)).multiply(epp2.multiply(epp2.subtract(3.0)).add(3.0)); - ecf = qe.multiply(3.0); - e2cf = qe.multiply(3.0).multiply(epp); - e3cf = qe.multiply(epp2); - qe = y2.multiply(0.5).multiply(n2).multiply(3.0).multiply(cosI2.negate().add(1.0)).divide(n6); - ec2f2g = qe.multiply(epp); - ecfc2f2g = qe.multiply(3.0); - e2cfc2f2g = qe.multiply(3.0).multiply(epp); - e3cfc2f2g = qe.multiply(epp2); - qe = yp2.multiply(-0.5).multiply(n2).multiply(cosI2.negate().add(1.0)); - ec2gf = qe.multiply(3.0); - ec2g3f = qe; - - T qi = yp2.multiply(epp).multiply(cosI1).multiply(sinI1); - ide = cosI1.multiply(epp.negate()).divide(sinI1.multiply(n2)); - isfs2f2g = qi; - icfc2f2g = qi.multiply(2.0); - qi = yp2.multiply(cosI1).multiply(sinI1); - ic2f2g = qi.multiply(1.5); - - T qgl1 = yp2.multiply(0.25); - T qgl2 = yp2.multiply(epp).multiply(n2).multiply(0.25).divide(n.add(1.0)); - glf = qgl1.multiply(C5c2).multiply(-6.0); - gll = qgl1.multiply(C5c2).multiply(6.0); - glsf = qgl1.multiply(C5c2).multiply(-6.0).multiply(epp). - add(qgl2.multiply(C3c2).multiply(2.0)); - glosf = qgl2.multiply(C3c2).multiply(2.0); - qgl1 = qgl1.multiply(cosI2.multiply(-5.0).add(3.0)); - qgl2 = qgl2.multiply(3.0).multiply(cosI2.negate().add(1.0)); - gls2f2g = qgl1.multiply(3.0); - gls2gf = qgl1.multiply(3.0).multiply(epp). - add(qgl2); - glos2gf = qgl2.negate(); - gls2g3f = qgl1.multiply(epp). - add(qgl2.multiply(1.0 / 3.0)); - glos2g3f = qgl2; - - final T qh = yp2.multiply(cosI1).multiply(3.0); - hf = qh.negate(); - hl = qh; - hsf = qh.multiply(epp).negate(); - hcfs2g2f = yp2.multiply(cosI1).multiply(epp).multiply(2.0); - hs2g2f = yp2.multiply(cosI1).multiply(1.5); - hsfc2g2f = yp2.multiply(cosI1).multiply(epp).negate(); - - final T qedl = yp2.multiply(n3).multiply(-0.25); - edls2g = qyp2_4.multiply(1.0 / 24.0).multiply(epp).multiply(n3).divide(yp2); - edlcg = yp3.divide(yp2).multiply(-0.25).multiply(n3).multiply(sinI1). - subtract(yp5.divide(yp2).multiply(0.078125).multiply(n3).multiply(sinI1).multiply(cC).multiply(epp2.multiply(9.0).add(4.0))); - edlc3g = yp5.divide(yp2).multiply(n3).multiply(epp2).multiply(cD).multiply(sinI1).multiply(35.0 / 384.0); - edlsf = qedl.multiply(C3c2).multiply(2.0); - edls2gf = qedl.multiply(3.0).multiply(cosI2.negate().add(1.0)); - edls2g3f = qedl.multiply(1.0 / 3.0); - - // secular rates of the mean semi-major axis and eccentricity + si = sc.sin(); + ci = sc.cos(); + final T ci2 = ci.square(); + oneMci2 = one.subtract(ci2); + ci2X3M1 = ci2.multiply(3.).subtract(one); + final T ci2X5M1 = ci2.multiply(5.).subtract(one); + + // secular corrections + // true anomaly + final T dsl1 = yp2.multiply(n).multiply(1.5); + final T dsl2a = n.multiply(n.multiply(25.).add(16.)).subtract(15.); + final T dsl2b = n.multiply(n.multiply(90.).add(96.)).negate().add(30.); + final T dsl2c = n.multiply(n.multiply(25.).add(144.)).add(105.); + final T dsl21 = dsl2a.add(ci2.multiply(dsl2b.add(ci2.multiply(dsl2c)))); + final T dsl2 = ci2X3M1.add(yp2.multiply(0.0625).multiply(dsl21)); + final T dsl3 = yp4.multiply(n).multiply(epp2).multiply(0.9375). + multiply(ci2.multiply(35.0).subtract(30.0).multiply(ci2).add(3.)); + dsl = dsl1.multiply(dsl2).add(dsl3); + + // perigee argument + final T dsg1 = yp2.multiply(1.5).multiply(ci2X5M1); + final T dsg2a = n.multiply(25.).add(24.).multiply(n).add(-35.); + final T dsg2b = n.multiply(126.).add(192.).multiply(n).negate().add(90.); + final T dsg2c = n.multiply(45.).add(360.).multiply(n).add(385.); + final T dsg21 = dsg2a.add(ci2.multiply(dsg2b.add(ci2.multiply(dsg2c)))); + final T dsg2 = yp22.multiply(0.09375).multiply(dsg21); + final T dsg3a = n2.multiply(-9.).add(21.); + final T dsg3b = n2.multiply(126.).add(-270.); + final T dsg3c = n2.multiply(-189.).add(385.); + final T dsg31 = dsg3a.add(ci2.multiply(dsg3b.add(ci2.multiply(dsg3c)))); + final T dsg3 = yp4.multiply(0.3125).multiply(dsg31); + dsg = dsg1.add(dsg2).add(dsg3); + + // right ascension of ascending node + final T dsh1 = yp2.multiply(-3.); + final T dsh2a = n.multiply(9.).add(12.).multiply(n).add(-5.); + final T dsh2b = n.multiply(5.).add(36.).multiply(n).add(35.); + final T dsh21 = dsh2a.subtract(ci2.multiply(dsh2b)); + final T dsh2 = yp22.multiply(0.375).multiply(dsh21); + final T dsh31 = n2.multiply(3.).subtract(5.); + final T dsh32 = ci2.multiply(7.).subtract(3.); + final T dsh3 = yp4.multiply(1.25).multiply(dsh31).multiply(dsh32); + dsh = ci.multiply(dsh1.add(dsh2).add(dsh3)); + + // secular rates of change due to drag // Eq. 2.41 and Eq. 2.45 of Phipps' 1992 thesis - aRate = app.multiply(-4.0).divide(xnotDot.multiply(3.0)); - eRate = epp.multiply(n).multiply(n).multiply(-4.0).divide(xnotDot.multiply(3.0)); - + final T coef = n0.multiply(one.add(dsl)).multiply(3.).reciprocal().multiply(-4); + aRate = coef.multiply(app); + eRate = coef.multiply(epp).multiply(n2); + + // singular term 1/(1 - 5 * cos²(I")) replaced by T2 function + final T t2 = T2(ci); + + // factors for long periodic corrections + final T fs12 = yp3.divide(yp2); + final T fs13 = yp4.multiply(10).divide(yp2.multiply(3)); + final T fs14 = yp5.divide(yp2); + + final T ci2Xt2 = ci2.multiply(t2); + final T cA = one.subtract(ci2.multiply(ci2Xt2.multiply(40.) .add(11.))); + final T cB = one.subtract(ci2.multiply(ci2Xt2.multiply(8.) .add(3.))); + final T cC = one.subtract(ci2.multiply(ci2Xt2.multiply(24.) .add(9.))); + final T cD = one.subtract(ci2.multiply(ci2Xt2.multiply(16.) .add(5.))); + final T cE = one.subtract(ci2.multiply(ci2Xt2.multiply(200.).add(33.))); + final T cF = one.subtract(ci2.multiply(ci2Xt2.multiply(40.) .add(9.))); + + final T p5p = one.add(ci2Xt2.multiply(ci2Xt2.multiply(20.).add(8.))); + final T p5p2 = one.add(p5p.multiply(2.)); + final T p5p4 = one.add(p5p.multiply(4.)); + final T p5p10 = one.add(p5p.multiply(10.)); + + final T e2X3P4 = epp2.multiply(3.).add(4.); + final T ciO1Pci = ci.divide(one.add(ci)); + final T oneMci = one.subtract(ci); + + final T q1 = (yp2.multiply(cA).subtract(fs13.multiply(cB))). + multiply(0.125); + final T q2 = (yp2.multiply(p5p10).subtract(fs13.multiply(p5p2))). + multiply(epp2).multiply(ci).multiply(0.125); + final T q5 = (fs12.add(e2X3P4.multiply(fs14).multiply(cC).multiply(0.3125))). + multiply(0.25); + final T p2 = p5p2.multiply(epp).multiply(ci).multiply(si).multiply(e2X3P4).multiply(fs14). + multiply(0.46875); + final T p3 = epp.multiply(si).multiply(fs14).multiply(cC). + multiply(0.15625); + final double kf = 35. / 1152.; + final T p4 = epp.multiply(fs14).multiply(cD). + multiply(kf); + final T p5 = epp.multiply(epp2).multiply(ci).multiply(si).multiply(fs14).multiply(p5p4). + multiply(2. * kf); + + vle1 = epp.multiply(n2).multiply(q1); + vle2 = n2.multiply(si).multiply(q5); + vle3 = epp.multiply(n2).multiply(si).multiply(p4).multiply(-3.0); + + vli1 = epp.multiply(q1).divide(si).negate(); + vli2 = epp.multiply(ci).multiply(q5).negate(); + vli3 = epp2.multiply(ci).multiply(p4).multiply(-3.0); + + vll2 = vle2.add(epp.multiply(n2).multiply(p3).multiply(3.0)); + + vlh1I = si.multiply(q2).negate(); + vlh2I = epp.multiply(ci).multiply(q5).add(si.multiply(p2)); + vlh3I = (epp2.multiply(ci).multiply(p4).add(si.multiply(p5))).negate(); + + vls1 = q1.multiply(n3.subtract(one)). + subtract(q2). + add(epp2.multiply(ci2).multiply(ci2Xt2).multiply(ci2Xt2). + multiply(yp2.subtract(fs13.multiply(0.2))).multiply(25.0)). + subtract(epp2.multiply(yp2.multiply(cE).subtract(fs13.multiply(cF))).multiply(0.0625)); + + vls2 = epp.multiply(si).multiply(t8.add(ciO1Pci)).multiply(q5). + add((epp2.subtract(n3).multiply(3.).add(11.)).multiply(p3)). + add(oneMci.multiply(p2)); + + vls3 = si.multiply(p4).multiply(n3.subtract(one).multiply(3.). + subtract(epp2.multiply(ciO1Pci.add(2.)))). + subtract(oneMci.multiply(p5)); } /** - * Gets eccentric anomaly from mean anomaly. - * @param mk the mean anomaly (rad) - * @return the eccentric anomaly (rad) + * Get true anomaly from mean anomaly. + * @param lM the mean anomaly (rad) + * @param ecc the eccentricity + * @return the true anomaly (rad) */ - private FieldUnivariateDerivative1 getEccentricAnomaly(final FieldUnivariateDerivative1 mk) { + private FieldUnivariateDerivative1 getTrueAnomaly(final FieldUnivariateDerivative1 lM, + final FieldUnivariateDerivative1 ecc) { final T zero = mean.getE().getField().getZero(); // reduce M to [-PI PI] interval - final FieldUnivariateDerivative1 reducedM = new FieldUnivariateDerivative1<>(MathUtils.normalizeAngle(mk.getValue(), zero ), - mk.getFirstDerivative()); + final FieldUnivariateDerivative1 reducedM = new FieldUnivariateDerivative1<>(MathUtils.normalizeAngle(lM.getValue(), zero), + lM.getFirstDerivative()); - final FieldUnivariateDerivative1 meanE = new FieldUnivariateDerivative1<>(mean.getE(), zero); - FieldUnivariateDerivative1 ek = FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(meanE, mk); + // compute the true anomaly + FieldUnivariateDerivative1 lV = FieldKeplerianAnomalyUtility.ellipticMeanToTrue(ecc, lM); // expand the result back to original range - ek = ek.add(mk.getValue().subtract(reducedM.getValue())); + lV = lV.add(lM.getValue().subtract(reducedM.getValue())); - // Returns the eccentric anomaly - return ek; + // Returns the true anomaly + return lV; } /** @@ -1121,34 +1114,38 @@ private FieldUnivariateDerivative1 getEccentricAnomaly(final FieldUnivariateD * critical inclination (i = 63.4°). *

      * This method, based on Warren Phipps's 1992 thesis (Eq. 2.47 and 2.48), - * approximate the factor (1.0 - 5.0 * cos²(inc))^-1 (causing the singularity) + * approximate the factor (1.0 - 5.0 * cos²(i))-1 (causing the singularity) * by a function, named T2 in the thesis. *

      - * @param cosInc cosine of the mean inclination - * @return an approximation of (1.0 - 5.0 * cos²(inc))^-1 term + * @param cosI cosine of the mean inclination + * @return an approximation of (1.0 - 5.0 * cos²(i))-1 term */ - private T T2(final T cosInc) { + private T T2(final T cosI) { - // X = (1.0 - 5.0 * cos²(inc)) - final T x = cosInc.square().multiply(-5.0).add(1.0); + // X = (1.0 - 5.0 * cos²(i)) + final T x = cosI.square().multiply(-5.0).add(1.0); final T x2 = x.square(); + final T xb = x2.multiply(BETA); // Eq. 2.48 T sum = x.getField().getZero(); for (int i = 0; i <= 12; i++) { final double sign = i % 2 == 0 ? +1.0 : -1.0; - sum = sum.add(FastMath.pow(x2, i).multiply(FastMath.pow(BETA, i)).multiply(sign).divide(CombinatoricsUtils.factorialDouble(i + 1))); + sum = sum.add(FastMath.pow(x2, i). + multiply(FastMath.pow(BETA, i)). + multiply(sign). + divide(CombinatoricsUtils.factorialDouble(i + 1))); } // Right term of equation 2.47 - T product = x.getField().getOne(); + final T one = x.getField().getOne(); + T product = one; for (int i = 0; i <= 10; i++) { - product = product.multiply(FastMath.exp(x2.multiply(BETA).multiply(FastMath.scalb(-1.0, i))).add(1.0)); + product = product.multiply(one.add(FastMath.exp(xb.multiply(FastMath.scalb(-1.0, i))))); } // Return (Eq. 2.47) return x.multiply(BETA).multiply(sum).multiply(product); - } /** Extrapolate an orbit up to a specific target date. @@ -1167,152 +1164,224 @@ public FieldKeplerianOrbit propagateParameters(final FieldAbsoluteDate dat final T m2 = parameters[0]; // Keplerian evolution - final FieldUnivariateDerivative1 dt = new FieldUnivariateDerivative1<>(date.durationFrom(mean.getDate()), one); - final FieldUnivariateDerivative1 xnot = dt.multiply(xnotDot); - - //____________________________________ - // secular effects + final FieldUnivariateDerivative1 dt = new FieldUnivariateDerivative1<>(date.durationFrom(mean.getDate()), one); + final FieldUnivariateDerivative1 not = dt.multiply(n0); - // mean mean anomaly final FieldUnivariateDerivative1 dtM2 = dt.multiply(m2); final FieldUnivariateDerivative1 dt2M2 = dt.multiply(dtM2); - final FieldUnivariateDerivative1 lpp = new FieldUnivariateDerivative1<>(MathUtils.normalizeAngle(mean.getMeanAnomaly().add(lt.multiply(xnot.getValue())).add(dt2M2.getValue()), zero), - lt.multiply(xnotDot).add(dtM2.multiply(2.0).getValue())); + + // Secular corrections + // ------------------- + + // semi-major axis (with drag Eq. 2.41 of Phipps' 1992 thesis) + final FieldUnivariateDerivative1 app = dtM2.multiply(aRate).add(mean.getA()); + + // eccentricity (with drag Eq. 2.45 of Phipps' 1992 thesis) reduced to [0, 1[ + final FieldUnivariateDerivative1 tmp = dtM2.multiply(eRate).add(mean.getE()); + final FieldUnivariateDerivative1 epp = FastMath.max(FastMath.min(tmp, MAX_ECC), 0.); + // mean argument of perigee - final FieldUnivariateDerivative1 gpp = new FieldUnivariateDerivative1<>(MathUtils.normalizeAngle(mean.getPerigeeArgument().add(gt.multiply(xnot.getValue())), zero), - gt.multiply(xnotDot)); + final T gp0 = MathUtils.normalizeAngle(mean.getPerigeeArgument().add(dsg.multiply(not.getValue())), zero); + final T gp1 = dsg.multiply(n0); + final FieldUnivariateDerivative1 gpp = new FieldUnivariateDerivative1<>(gp0, gp1); + // mean longitude of ascending node - final FieldUnivariateDerivative1 hpp = new FieldUnivariateDerivative1<>(MathUtils.normalizeAngle(mean.getRightAscensionOfAscendingNode().add(ht.multiply(xnot.getValue())), zero), - ht.multiply(xnotDot)); - - // ________________________________________________ - // secular rates of the mean semi-major axis and eccentricity - - // semi-major axis - final FieldUnivariateDerivative1 appDrag = dt.multiply(aRate.multiply(m2)); - - // eccentricity - final FieldUnivariateDerivative1 eppDrag = dt.multiply(eRate.multiply(m2)); - - //____________________________________ - // Long periodical terms - final FieldSinCos> sinCosGpp = gpp.sinCos(); - final FieldUnivariateDerivative1 cg1 = sinCosGpp.cos(); - final FieldUnivariateDerivative1 sg1 = sinCosGpp.sin(); - final FieldUnivariateDerivative1 c2g = cg1.multiply(cg1).subtract(sg1.multiply(sg1)); - final FieldUnivariateDerivative1 s2g = cg1.multiply(sg1).add(sg1.multiply(cg1)); - final FieldUnivariateDerivative1 c3g = c2g.multiply(cg1).subtract(s2g.multiply(sg1)); - final FieldUnivariateDerivative1 sg2 = sg1.square(); - final FieldUnivariateDerivative1 sg3 = sg1.multiply(sg2); - - - // de eccentricity - final FieldUnivariateDerivative1 d1e = sg3.multiply(dei3sg). - add(sg1.multiply(deisg)). - add(sg2.multiply(de2sg)). - add(de); - - // l' + g' - final FieldUnivariateDerivative1 lpPGp = s2g.multiply(dlgs2g). - add(c3g.multiply(dlgc3g)). - add(cg1.multiply(dlgcg)). - add(lpp). - add(gpp); - - // h' - final FieldUnivariateDerivative1 hp = sg2.multiply(cg1).multiply(dh2sgcg). - add(sg1.multiply(cg1).multiply(dhsgcg)). - add(cg1.multiply(dhcg)). - add(hpp); - - // Short periodical terms - // eccentric anomaly - final FieldUnivariateDerivative1 Ep = getEccentricAnomaly(lpp); - final FieldSinCos> sinCosEp = Ep.sinCos(); - final FieldUnivariateDerivative1 cf1 = (sinCosEp.cos().subtract(mean.getE())).divide(sinCosEp.cos().multiply(mean.getE().negate()).add(1.0)); - final FieldUnivariateDerivative1 sf1 = (sinCosEp.sin().multiply(n)).divide(sinCosEp.cos().multiply(mean.getE().negate()).add(1.0)); - final FieldUnivariateDerivative1 f = FastMath.atan2(sf1, cf1); - - final FieldUnivariateDerivative1 cf2 = cf1.square(); - final FieldUnivariateDerivative1 c2f = cf2.subtract(sf1.multiply(sf1)); - final FieldUnivariateDerivative1 s2f = cf1.multiply(sf1).add(sf1.multiply(cf1)); - final FieldUnivariateDerivative1 c3f = c2f.multiply(cf1).subtract(s2f.multiply(sf1)); - final FieldUnivariateDerivative1 s3f = c2f.multiply(sf1).add(s2f.multiply(cf1)); - final FieldUnivariateDerivative1 cf3 = cf1.multiply(cf2); - - final FieldUnivariateDerivative1 c2g1f = cf1.multiply(c2g).subtract(sf1.multiply(s2g)); - final FieldUnivariateDerivative1 c2g2f = c2f.multiply(c2g).subtract(s2f.multiply(s2g)); - final FieldUnivariateDerivative1 c2g3f = c3f.multiply(c2g).subtract(s3f.multiply(s2g)); - final FieldUnivariateDerivative1 s2g1f = cf1.multiply(s2g).add(c2g.multiply(sf1)); - final FieldUnivariateDerivative1 s2g2f = c2f.multiply(s2g).add(c2g.multiply(s2f)); - final FieldUnivariateDerivative1 s2g3f = c3f.multiply(s2g).add(c2g.multiply(s3f)); - - final FieldUnivariateDerivative1 eE = (sinCosEp.cos().multiply(mean.getE().negate()).add(1.0)).reciprocal(); - final FieldUnivariateDerivative1 eE3 = eE.square().multiply(eE); - final FieldUnivariateDerivative1 sigma = eE.multiply(n.square()).multiply(eE).add(eE); + final T hp0 = MathUtils.normalizeAngle(mean.getRightAscensionOfAscendingNode().add(dsh.multiply(not.getValue())), zero); + final T hp1 = dsh.multiply(n0); + final FieldUnivariateDerivative1 hpp = new FieldUnivariateDerivative1<>(hp0, hp1); + + // mean anomaly (with drag Eq. 2.38 of Phipps' 1992 thesis) + final T lp0 = MathUtils.normalizeAngle(mean.getMeanAnomaly().add(dsl.add(one).multiply(not.getValue())).add(dt2M2.getValue()), zero); + final T lp1 = dsl.add(one).multiply(n0).add(dtM2.multiply(2.0).getValue()); + final FieldUnivariateDerivative1 lpp = new FieldUnivariateDerivative1<>(lp0, lp1); + + // Long period corrections + //------------------------ + final FieldSinCos> scgpp = gpp.sinCos(); + final FieldUnivariateDerivative1 cgpp = scgpp.cos(); + final FieldUnivariateDerivative1 sgpp = scgpp.sin(); + final FieldSinCos> sc2gpp = gpp.multiply(2).sinCos(); + final FieldUnivariateDerivative1 c2gpp = sc2gpp.cos(); + final FieldUnivariateDerivative1 s2gpp = sc2gpp.sin(); + final FieldSinCos> sc3gpp = gpp.multiply(3).sinCos(); + final FieldUnivariateDerivative1 c3gpp = sc3gpp.cos(); + final FieldUnivariateDerivative1 s3gpp = sc3gpp.sin(); + + // δ1e + final FieldUnivariateDerivative1 d1e = c2gpp.multiply(vle1). + add(sgpp.multiply(vle2)). + add(s3gpp.multiply(vle3)); + + // δ1I + FieldUnivariateDerivative1 d1I = sgpp.multiply(vli2). + add(s3gpp.multiply(vli3)); + // Pseudo singular term, not to add if I" is zero + if (Double.isFinite(vli1.getReal())) { + d1I = d1I.add(c2gpp.multiply(vli1)); + } + + // e"δ1l + final FieldUnivariateDerivative1 eppd1l = s2gpp.multiply(vle1). + subtract(cgpp.multiply(vll2)). + subtract(c3gpp.multiply(vle3)). + multiply(n); + + // sinI"δ1h + final FieldUnivariateDerivative1 sIppd1h = s2gpp.multiply(vlh1I). + add(cgpp.multiply(vlh2I)). + add(c3gpp.multiply(vlh3I)); + + // δ1z = δ1l + δ1g + δ1h + final FieldUnivariateDerivative1 d1z = s2gpp.multiply(vls1). + add(cgpp.multiply(vls2)). + add(c3gpp.multiply(vls3)); + + // Short period corrections + // ------------------------ + + // true anomaly + final FieldUnivariateDerivative1 fpp = getTrueAnomaly(lpp, epp); + final FieldSinCos> scfpp = fpp.sinCos(); + final FieldUnivariateDerivative1 cfpp = scfpp.cos(); + final FieldUnivariateDerivative1 sfpp = scfpp.sin(); + + // e"sin(f') + final FieldUnivariateDerivative1 eppsfpp = epp.multiply(sfpp); + // e"cos(f') + final FieldUnivariateDerivative1 eppcfpp = epp.multiply(cfpp); + // 1 + e"cos(f') + final FieldUnivariateDerivative1 eppcfppP1 = eppcfpp.add(1); + // 2 + e"cos(f') + final FieldUnivariateDerivative1 eppcfppP2 = eppcfpp.add(2); + // 3 + e"cos(f') + final FieldUnivariateDerivative1 eppcfppP3 = eppcfpp.add(3); + // (1 + e"cos(f'))³ + final FieldUnivariateDerivative1 eppcfppP1_3 = eppcfppP1.square().multiply(eppcfppP1); + + // 2g" + final FieldUnivariateDerivative1 g2 = gpp.multiply(2); + + // 2g" + f" + final FieldUnivariateDerivative1 g2f = g2.add(fpp); + final FieldSinCos> sc2gf = g2f.sinCos(); + final FieldUnivariateDerivative1 c2gf = sc2gf.cos(); + final FieldUnivariateDerivative1 s2gf = sc2gf.sin(); + final FieldUnivariateDerivative1 eppc2gf = epp.multiply(c2gf); + final FieldUnivariateDerivative1 epps2gf = epp.multiply(s2gf); + + // 2g" + 2f" + final FieldUnivariateDerivative1 g2f2 = g2.add(fpp.multiply(2)); + final FieldSinCos> sc2g2f = g2f2.sinCos(); + final FieldUnivariateDerivative1 c2g2f = sc2g2f.cos(); + final FieldUnivariateDerivative1 s2g2f = sc2g2f.sin(); + + // 2g" + 3f" + final FieldUnivariateDerivative1 g2f3 = g2.add(fpp.multiply(3)); + final FieldSinCos> sc2g3f = g2f3.sinCos(); + final FieldUnivariateDerivative1 c2g3f = sc2g3f.cos(); + final FieldUnivariateDerivative1 s2g3f = sc2g3f.sin(); + + // e"cos(2g" + 3f") + final FieldUnivariateDerivative1 eppc2g3f = epp.multiply(c2g3f); + // e"sin(2g" + 3f") + final FieldUnivariateDerivative1 epps2g3f = epp.multiply(s2g3f); + + // f" + e"sin(f") - l" + final FieldUnivariateDerivative1 w17 = fpp.add(eppsfpp).subtract(lpp); + + // ((e"cos(f") + 3)e"cos(f") + 3)cos(f") + final FieldUnivariateDerivative1 w20 = cfpp.multiply(eppcfppP3.multiply(eppcfpp).add(3.)); + + // 3sin(2g" + 2f") + 3e"sin(2g" + f") + e"sin(2g" + f") + final FieldUnivariateDerivative1 w21 = s2g2f.add(epps2gf).multiply(3).add(epps2g3f); + + // (1 + e"cos(f"))(2 + e"cos(f"))/η² + final FieldUnivariateDerivative1 w22 = eppcfppP1.multiply(eppcfppP2).divide(n2); + + // sinCos(I"/2) + final FieldSinCos sci = FastMath.sinCos(mean.getI().divide(2.)); + final T siO2 = sci.sin(); + final T ciO2 = sci.cos(); + + // δ2a + final FieldUnivariateDerivative1 d2a = app.multiply(yp2).divide(n2). + multiply(eppcfppP1_3.subtract(n3).multiply(ci2X3M1). + add(c2g2f.multiply(eppcfppP1_3).multiply(oneMci2).multiply(3.))); + + // δ2e + final FieldUnivariateDerivative1 d2e = (w20.add(epp.multiply(t8))).multiply(ci2X3M1). + add((w20.add(epp.multiply(c2g2f))).multiply(oneMci2.multiply(3))). + subtract((eppc2gf.multiply(3).add(eppc2g3f)).multiply(oneMci2.multiply(n2))). + multiply(yp2.multiply(0.5)); + + // δ2I + final FieldUnivariateDerivative1 d2I = ((c2g2f.add(eppc2gf)).multiply(3).add(eppc2g3f)). + multiply(yp2.divide(2.).multiply(ci).multiply(si)); + + // e"δ2l + final FieldUnivariateDerivative1 eppd2l = (w22.add(1).multiply(sfpp).multiply(oneMci2).multiply(2.). + add((w22.subtract(1).negate().multiply(s2gf)). + add(w22.add(1. / 3.).multiply(s2g3f)). + multiply(oneMci2.multiply(3.)))). + multiply(yp2.divide(4.).multiply(n3)).negate(); + + // sinI"δ2h + final FieldUnivariateDerivative1 sIppd2h = (w21.subtract(w17.multiply(6))). + multiply(yp2).multiply(ci).multiply(si).divide(2.); + + // δ2z = δ2l + δ2g + δ2h + final T ttt = one.add(ci.multiply(ci.multiply(-5).add(2.))); + final FieldUnivariateDerivative1 d2z = (epp.multiply(eppd2l).multiply(t8.subtract(one)).divide(n3). + add(w17.multiply(ttt).multiply(6).subtract(w21.multiply(ttt.add(2.))). + multiply(yp2.divide(4.)))). + negate(); + + // Assembling elements + // ------------------- + + // e" + δe + final FieldUnivariateDerivative1 de = epp.add(d1e).add(d2e); + + // e"δl + final FieldUnivariateDerivative1 dl = eppd1l.add(eppd2l); + + // sin(I"/2)δh = sin(I")δh / cos(I"/2) (singular for I" = π, very unlikely) + final FieldUnivariateDerivative1 dh = sIppd1h.add(sIppd2h).divide(ciO2.multiply(2.)); + + // δI + final FieldUnivariateDerivative1 di = d1I.add(d2I).multiply(ciO2).divide(2.).add(siO2); + + // z = l" + g" + h" + δ1z + δ2z + final FieldUnivariateDerivative1 z = lpp.add(gpp).add(hpp).add(d1z).add(d2z); + + // Osculating elements + // ------------------- // Semi-major axis - final FieldUnivariateDerivative1 a = eE3.multiply(aCbis).add(appDrag.add(mean.getA())). - add(aC). - add(eE3.multiply(c2g2f).multiply(ac2g2f)); + final FieldUnivariateDerivative1 a = app.add(d2a); // Eccentricity - final FieldUnivariateDerivative1 e = d1e.add(eppDrag.add(mean.getE())). - add(eC). - add(cf1.multiply(ecf)). - add(cf2.multiply(e2cf)). - add(cf3.multiply(e3cf)). - add(c2g2f.multiply(ec2f2g)). - add(c2g2f.multiply(cf1).multiply(ecfc2f2g)). - add(c2g2f.multiply(cf2).multiply(e2cfc2f2g)). - add(c2g2f.multiply(cf3).multiply(e3cfc2f2g)). - add(c2g1f.multiply(ec2gf)). - add(c2g3f.multiply(ec2g3f)); + final FieldUnivariateDerivative1 e = FastMath.sqrt(de.square().add(dl.square())); + + // Mean anomaly + final FieldSinCos> sclpp = lpp.sinCos(); + final FieldUnivariateDerivative1 clpp = sclpp.cos(); + final FieldUnivariateDerivative1 slpp = sclpp.sin(); + final FieldUnivariateDerivative1 l = FastMath.atan2(de.multiply(slpp).add(dl.multiply(clpp)), + de.multiply(clpp).subtract(dl.multiply(slpp))); // Inclination - final FieldUnivariateDerivative1 i = d1e.multiply(ide). - add(mean.getI()). - add(sf1.multiply(s2g2f.multiply(isfs2f2g))). - add(cf1.multiply(c2g2f.multiply(icfc2f2g))). - add(c2g2f.multiply(ic2f2g)); - - // Argument of perigee + mean anomaly - final FieldUnivariateDerivative1 gPL = lpPGp.add(f.multiply(glf)). - add(lpp.multiply(gll)). - add(sf1.multiply(glsf)). - add(sigma.multiply(sf1).multiply(glosf)). - add(s2g2f.multiply(gls2f2g)). - add(s2g1f.multiply(gls2gf)). - add(sigma.multiply(s2g1f).multiply(glos2gf)). - add(s2g3f.multiply(gls2g3f)). - add(sigma.multiply(s2g3f).multiply(glos2g3f)); + final FieldUnivariateDerivative1 i = FastMath.acos(di.square().add(dh.square()).multiply(2).negate().add(1.)); // Longitude of ascending node - final FieldUnivariateDerivative1 h = hp.add(f.multiply(hf)). - add(lpp.multiply(hl)). - add(sf1.multiply(hsf)). - add(cf1.multiply(s2g2f).multiply(hcfs2g2f)). - add(s2g2f.multiply(hs2g2f)). - add(c2g2f.multiply(sf1).multiply(hsfc2g2f)); - - final FieldUnivariateDerivative1 edl = s2g.multiply(edls2g). - add(cg1.multiply(edlcg)). - add(c3g.multiply(edlc3g)). - add(sf1.multiply(edlsf)). - add(s2g1f.multiply(edls2gf)). - add(s2g3f.multiply(edls2g3f)). - add(sf1.multiply(sigma).multiply(edlsf)). - add(s2g1f.multiply(sigma).multiply(edls2gf.negate())). - add(s2g3f.multiply(sigma).multiply(edls2g3f.multiply(3.0))); - - final FieldUnivariateDerivative1 A = e.multiply(lpp.cos()).subtract(edl.multiply(lpp.sin())); - final FieldUnivariateDerivative1 B = e.multiply(lpp.sin()).add(edl.multiply(lpp.cos())); - - // Mean anomaly - final FieldUnivariateDerivative1 l = FastMath.atan2(B, A); + final FieldSinCos> schpp = hpp.sinCos(); + final FieldUnivariateDerivative1 chpp = schpp.cos(); + final FieldUnivariateDerivative1 shpp = schpp.sin(); + final FieldUnivariateDerivative1 h = FastMath.atan2(di.multiply(shpp).add(dh.multiply(chpp)), + di.multiply(chpp).subtract(dh.multiply(shpp))); // Argument of perigee - final FieldUnivariateDerivative1 g = gPL.subtract(l); + final FieldUnivariateDerivative1 g = z.subtract(l).subtract(h); // Return a Keplerian orbit return new FieldKeplerianOrbit<>(a.getValue(), e.getValue(), i.getValue(), @@ -1320,7 +1389,6 @@ public FieldKeplerianOrbit propagateParameters(final FieldAbsoluteDate dat a.getFirstDerivative(), e.getFirstDerivative(), i.getFirstDerivative(), g.getFirstDerivative(), h.getFirstDerivative(), l.getFirstDerivative(), PositionAngleType.MEAN, mean.getFrame(), date, this.mu); - } } diff --git a/src/main/java/org/orekit/propagation/analytical/tle/TLEPropagator.java b/src/main/java/org/orekit/propagation/analytical/tle/TLEPropagator.java index 38918bf67b..62a70a28f5 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/TLEPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/TLEPropagator.java @@ -51,7 +51,6 @@ import org.orekit.utils.TimeSpanMap; import org.orekit.utils.TimeSpanMap.Span; - /** This class provides elements to propagate TLE's. *

      * The models used are SGP4 and SDP4, initially proposed by NORAD as the unique convenient @@ -247,13 +246,21 @@ public static TLEPropagator selectExtrapolator(final TLE tle) { * @param teme TEME frame. * @return the correct propagator. * @since 10.1 + * @see #selectExtrapolator(TLE, Frame, AttitudeProvider) */ public static TLEPropagator selectExtrapolator(final TLE tle, final Frame teme) { - return selectExtrapolator( - tle, - FrameAlignedProvider.of(teme), - DEFAULT_MASS, - teme); + return selectExtrapolator(tle, teme, FrameAlignedProvider.of(teme)); + } + + /** Selects the extrapolator to use with the selected TLE. + * @param tle the TLE to propagate. + * @param teme TEME frame. + * @param attitudeProvider provider for attitude computation + * @return the correct propagator. + * @since 12.2 + */ + public static TLEPropagator selectExtrapolator(final TLE tle, final Frame teme, final AttitudeProvider attitudeProvider) { + return selectExtrapolator(tle, attitudeProvider, DEFAULT_MASS, teme); } /** Selects the extrapolator to use with the selected TLE. diff --git a/src/main/java/org/orekit/propagation/conversion/AbstractAnalyticalPropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/AbstractAnalyticalPropagatorBuilder.java new file mode 100644 index 0000000000..23a13e88b6 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/AbstractAnalyticalPropagatorBuilder.java @@ -0,0 +1,109 @@ +/* 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.propagation.conversion; + + +import org.orekit.attitudes.AttitudeProvider; +import org.orekit.estimation.leastsquares.AbstractBatchLSModel; +import org.orekit.estimation.leastsquares.BatchLSModel; +import org.orekit.estimation.leastsquares.ModelObserver; +import org.orekit.estimation.measurements.ObservedMeasurement; +import org.orekit.forces.maneuvers.ImpulseManeuver; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.PositionAngleType; +import org.orekit.utils.ParameterDriversList; + +import java.util.ArrayList; +import java.util.List; + +/** + * Abstract class for propagator builders of analytical models (except for ephemeris i.e. interpolated ones). + * + * @author Romain Serra + * @since 12.2 + */ +public abstract class AbstractAnalyticalPropagatorBuilder extends AbstractPropagatorBuilder { + + /** Impulse maneuvers. */ + private final List impulseManeuvers; + + /** Build a new instance. + *

      + * The template orbit is used as a model to {@link + * #createInitialOrbit() create initial orbit}. It defines the + * inertial frame, the central attraction coefficient, the orbit type, and is also + * used together with the {@code positionScale} to convert from the {@link + * org.orekit.utils.ParameterDriver#setNormalizedValue(double) normalized} parameters used by the + * callers of this builder to the real orbital parameters. The default attitude + * provider is aligned with the orbit's inertial frame. + *

      + *

      + * By default, all the {@link #getOrbitalParametersDrivers() orbital parameters drivers} + * are selected, which means that if the builder is used for orbit determination or + * propagator conversion, all orbital parameters will be estimated. If only a subset + * of the orbital parameters must be estimated, caller must retrieve the orbital + * parameters by calling {@link #getOrbitalParametersDrivers()} and then call + * {@link org.orekit.utils.ParameterDriver#setSelected(boolean) setSelected(false)}. + *

      + * @param templateOrbit reference orbit from which real orbits will be built + * @param positionAngleType position angle type to use + * @param positionScale scaling factor used for orbital parameters normalization + * (typically set to the expected standard deviation of the position) + * @param addDriverForCentralAttraction if true, a {@link org.orekit.utils.ParameterDriver} should + * be set up for central attraction coefficient + * @param attitudeProvider for the propagator + * @param initialMass mass + */ + protected AbstractAnalyticalPropagatorBuilder(final Orbit templateOrbit, final PositionAngleType positionAngleType, + final double positionScale, final boolean addDriverForCentralAttraction, + final AttitudeProvider attitudeProvider, final double initialMass) { + super(templateOrbit, positionAngleType, positionScale, addDriverForCentralAttraction, attitudeProvider, initialMass); + this.impulseManeuvers = new ArrayList<>(); + } + + /** + * Protected getter for the impulse maneuvers. + * @return impulse maneuvers + */ + protected List getImpulseManeuvers() { + return new ArrayList<>(impulseManeuvers); + } + + /** + * Add impulse maneuver. + * @param impulseManeuver impulse maneuver + */ + public void addImpulseManeuver(final ImpulseManeuver impulseManeuver) { + impulseManeuvers.add(impulseManeuver); + } + + /** + * Remove all impulse maneuvers. + */ + public void clearImpulseManeuvers() { + impulseManeuvers.clear(); + } + + /** {@inheritDoc} */ + @Override + public AbstractBatchLSModel buildLeastSquaresModel(final PropagatorBuilder[] builders, + final List> measurements, + final ParameterDriversList estimatedMeasurementsParameters, + final ModelObserver observer) { + return new BatchLSModel(builders, measurements, estimatedMeasurementsParameters, observer); + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/AbstractFieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/AbstractFieldIntegratorBuilder.java index ee0afda19d..01a0e8ae28 100644 --- a/src/main/java/org/orekit/propagation/conversion/AbstractFieldIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/AbstractFieldIntegratorBuilder.java @@ -17,10 +17,8 @@ package org.orekit.propagation.conversion; import org.hipparchus.CalculusFieldElement; -import org.hipparchus.Field; import org.hipparchus.ode.AbstractFieldIntegrator; import org.orekit.orbits.FieldOrbit; -import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; /** @@ -44,9 +42,6 @@ public AbstractFieldIntegratorBuilder() { // nothing to do } - /** {@inheritDoc} */ - public abstract AbstractFieldIntegrator buildIntegrator(Field field, Orbit orbit, OrbitType orbitType); - /** {@inheritDoc} */ public AbstractFieldIntegrator buildIntegrator(final FieldOrbit orbit, final OrbitType orbitType) { return buildIntegrator(orbit.getA().getField(), orbit.toOrbit(), orbitType); diff --git a/src/main/java/org/orekit/propagation/conversion/AbstractLimitedVariableStepFieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/AbstractLimitedVariableStepFieldIntegratorBuilder.java index 72cf2e5251..22c1b70537 100644 --- a/src/main/java/org/orekit/propagation/conversion/AbstractLimitedVariableStepFieldIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/AbstractLimitedVariableStepFieldIntegratorBuilder.java @@ -46,4 +46,20 @@ public abstract class AbstractLimitedVariableStepFieldIntegratorBuilder * The template orbit is used as a model to {@link @@ -150,6 +155,43 @@ protected AbstractPropagatorBuilder(final Orbit templateOrbit, final double positionScale, final boolean addDriverForCentralAttraction, final AttitudeProvider attitudeProvider) { + this(templateOrbit, positionAngleType, positionScale, addDriverForCentralAttraction, attitudeProvider, + Propagator.DEFAULT_MASS); + } + + /** Build a new instance. + *

      + * The template orbit is used as a model to {@link + * #createInitialOrbit() create initial orbit}. It defines the + * inertial frame, the central attraction coefficient, the orbit type, and is also + * used together with the {@code positionScale} to convert from the {@link + * ParameterDriver#setNormalizedValue(double) normalized} parameters used by the + * callers of this builder to the real orbital parameters. + *

      + *

      + * By default, all the {@link #getOrbitalParametersDrivers() orbital parameters drivers} + * are selected, which means that if the builder is used for orbit determination or + * propagator conversion, all orbital parameters will be estimated. If only a subset + * of the orbital parameters must be estimated, caller must retrieve the orbital + * parameters by calling {@link #getOrbitalParametersDrivers()} and then call + * {@link ParameterDriver#setSelected(boolean) setSelected(false)}. + *

      + * @param templateOrbit reference orbit from which real orbits will be built + * @param positionAngleType position angle type to use + * @param positionScale scaling factor used for orbital parameters normalization + * (typically set to the expected standard deviation of the position) + * @param addDriverForCentralAttraction if true, a {@link ParameterDriver} should + * be set up for central attraction coefficient + * @param attitudeProvider for the propagator. + * @param initialMass mass + * @since 12.2 + * @see #AbstractPropagatorBuilder(Orbit, PositionAngleType, double, boolean) + */ + protected AbstractPropagatorBuilder(final Orbit templateOrbit, + final PositionAngleType positionAngleType, + final double positionScale, + final boolean addDriverForCentralAttraction, + final AttitudeProvider attitudeProvider, final double initialMass) { this.initialOrbitDate = templateOrbit.getDate(); this.frame = templateOrbit.getFrame(); @@ -160,6 +202,7 @@ protected AbstractPropagatorBuilder(final Orbit templateOrbit, this.positionScale = positionScale; this.orbitalDrivers = orbitType.getDrivers(positionScale, templateOrbit, positionAngleType); this.attitudeProvider = attitudeProvider; + this.mass = initialMass; for (final DelegatingDriver driver : orbitalDrivers.getDrivers()) { driver.setSelected(true); } @@ -188,6 +231,22 @@ public void valueSpanMapChanged(final TimeSpanMap previousValueSpanMap, } + /** Get the mass. + * @return the mass + * @since 9.2 + */ + public double getMass() + { + return mass; + } + + /** Set the initial mass. + * @param mass the mass (kg) + */ + public void setMass(final double mass) { + this.mass = mass; + } + /** {@inheritDoc} */ public OrbitType getOrbitType() { return orbitType; @@ -218,6 +277,15 @@ public ParameterDriversList getPropagationParametersDrivers() { return propagationDrivers; } + @Override + public AbstractPropagatorBuilder clone() { + try { + return (AbstractPropagatorBuilder) super.clone(); + } catch (CloneNotSupportedException cnse) { + throw new OrekitException(OrekitMessages.PROPAGATOR_BUILDER_NOT_CLONEABLE); + } + } + /** * Get the attitude provider. * diff --git a/src/main/java/org/orekit/propagation/conversion/AbstractVariableStepFieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/AbstractVariableStepFieldIntegratorBuilder.java index ed6c7e1e5f..65b931a11e 100644 --- a/src/main/java/org/orekit/propagation/conversion/AbstractVariableStepFieldIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/AbstractVariableStepFieldIntegratorBuilder.java @@ -17,10 +17,9 @@ package org.orekit.propagation.conversion; import org.hipparchus.CalculusFieldElement; -import org.hipparchus.Field; -import org.hipparchus.ode.AbstractFieldIntegrator; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; +import org.orekit.propagation.numerical.NumericalPropagator; /** * Abstract class for integrator builder using variable step size. @@ -41,22 +40,50 @@ public abstract class AbstractVariableStepFieldIntegratorBuilder buildIntegrator(Field field, Orbit orbit, OrbitType orbitType); + /** + * Computes tolerances. + * @param orbit initial orbit + * @param orbitType orbit type for integration + * @return integrator tolerances + */ + protected double[][] getTolerances(final Orbit orbit, final OrbitType orbitType) { + if (Double.isNaN(dV)) { + return NumericalPropagator.tolerances(dP, orbit, orbitType); + } else { + return NumericalPropagator.tolerances(dP, dV, orbit, orbitType); + } + } } diff --git a/src/main/java/org/orekit/propagation/conversion/AbstractVariableStepIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/AbstractVariableStepIntegratorBuilder.java new file mode 100644 index 0000000000..9c1ab7dd46 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/AbstractVariableStepIntegratorBuilder.java @@ -0,0 +1,85 @@ +/* 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.propagation.conversion; + +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.propagation.numerical.NumericalPropagator; + +/** + * Abstract class for integrator builder using variable step size. + * + * @author Romain Serra + * @since 12.2 + */ +public abstract class AbstractVariableStepIntegratorBuilder implements ODEIntegratorBuilder { + + // CHECKSTYLE: stop VisibilityModifier check + /** Minimum step size (s). */ + protected final double minStep; + + /** Maximum step size (s). */ + protected final double maxStep; + + /** Position error (m). */ + protected final double dP; + + /** Velocity error (m/s). */ + protected final double dV; + // CHECKSTYLE: resume VisibilityModifier check + + /** + * Constructor. Should only use this constructor with {@link Orbit}. + * + * @param minStep minimum step size (s) + * @param maxStep maximum step size (s) + * @param dP position error (m) + */ + protected AbstractVariableStepIntegratorBuilder(final double minStep, final double maxStep, final double dP) { + this(minStep, maxStep, dP, Double.NaN); + } + + /** + * Constructor with expected velocity error. + * + * @param minStep minimum step size (s) + * @param maxStep maximum step size (s) + * @param dP position error (m) + * @param dV velocity error (m/s) + */ + protected AbstractVariableStepIntegratorBuilder(final double minStep, final double maxStep, final double dP, + final double dV) { + this.minStep = minStep; + this.maxStep = maxStep; + this.dP = dP; + this.dV = dV; + } + + /** + * Computes tolerances. + * @param orbit initial orbit + * @param orbitType orbit type for integration + * @return integrator tolerances + */ + protected double[][] getTolerances(final Orbit orbit, final OrbitType orbitType) { + if (Double.isNaN(dV)) { + return NumericalPropagator.tolerances(dP, orbit, orbitType); + } else { + return NumericalPropagator.tolerances(dP, dV, orbit, orbitType); + } + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/AdamsBashforthFieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/AdamsBashforthFieldIntegratorBuilder.java index c250d3e01d..0ec0d3ed0a 100644 --- a/src/main/java/org/orekit/propagation/conversion/AdamsBashforthFieldIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/AdamsBashforthFieldIntegratorBuilder.java @@ -36,7 +36,7 @@ public class AdamsBashforthFieldIntegratorBuilder { /** - * Build a new instance. + * Build a new instance. Should only use this constructor with {@link Orbit}. * * @param nSteps number of steps * @param minStep minimum step size (s) @@ -51,10 +51,28 @@ public AdamsBashforthFieldIntegratorBuilder(final int nSteps, final double minSt super(nSteps, minStep, maxStep, dP); } + /** + * Build a new instance. + * + * @param nSteps number of steps + * @param minStep minimum step size (s) + * @param maxStep maximum step size (s) + * @param dP position error (m) + * @param dV velocity error (m/s) + * + * @since 12.2 + * @see AdamsBashforthFieldIntegrator + * @see NumericalPropagator#tolerances(double, double, Orbit, OrbitType) + */ + public AdamsBashforthFieldIntegratorBuilder(final int nSteps, final double minStep, + final double maxStep, final double dP, final double dV) { + super(nSteps, minStep, maxStep, dP, dV); + } + /** {@inheritDoc} */ @Override public AbstractFieldIntegrator buildIntegrator(final Field field, final Orbit orbit, final OrbitType orbitType) { - final double[][] tol = NumericalPropagator.tolerances(dP, orbit, orbitType); + final double[][] tol = getTolerances(orbit, orbitType); return new AdamsBashforthFieldIntegrator<>(field, nSteps, minStep, maxStep, tol[0], tol[1]); } } diff --git a/src/main/java/org/orekit/propagation/conversion/AdamsBashforthIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/AdamsBashforthIntegratorBuilder.java index f9ffeb91b1..af45a9d3bf 100644 --- a/src/main/java/org/orekit/propagation/conversion/AdamsBashforthIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/AdamsBashforthIntegratorBuilder.java @@ -26,39 +26,46 @@ * @author Pascal Parraud * @since 6.0 */ -public class AdamsBashforthIntegratorBuilder implements ODEIntegratorBuilder { +public class AdamsBashforthIntegratorBuilder extends AbstractVariableStepIntegratorBuilder { /** Number of steps. */ private final int nSteps; - /** Minimum step size (s). */ - private final double minStep; - - /** Maximum step size (s). */ - private final double maxStep; - - /** Minimum step size (s). */ - private final double dP; + /** Build a new instance. Should only use this constructor with {@link Orbit}. + * @param nSteps number of steps + * @param minStep minimum step size (s) + * @param maxStep maximum step size (s) + * @param dP position error (m) + * @see AdamsBashforthIntegrator + * @see NumericalPropagator#tolerances(double, Orbit, OrbitType) + */ + public AdamsBashforthIntegratorBuilder(final int nSteps, final double minStep, + final double maxStep, final double dP) { + super(minStep, maxStep, dP); + this.nSteps = nSteps; + } /** Build a new instance. * @param nSteps number of steps * @param minStep minimum step size (s) * @param maxStep maximum step size (s) * @param dP position error (m) + * @param dV velocity error (m/s) + * + * @since 12.2 * @see AdamsBashforthIntegrator * @see NumericalPropagator#tolerances(double, Orbit, OrbitType) */ public AdamsBashforthIntegratorBuilder(final int nSteps, final double minStep, - final double maxStep, final double dP) { + final double maxStep, final double dP, final double dV) { + super(minStep, maxStep, dP, dV); this.nSteps = nSteps; - this.minStep = minStep; - this.maxStep = maxStep; - this.dP = dP; } /** {@inheritDoc} */ + @Override public AbstractIntegrator buildIntegrator(final Orbit orbit, final OrbitType orbitType) { - final double[][] tol = NumericalPropagator.tolerances(dP, orbit, orbitType); + final double[][] tol = getTolerances(orbit, orbitType); return new AdamsBashforthIntegrator(nSteps, minStep, maxStep, tol[0], tol[1]); } diff --git a/src/main/java/org/orekit/propagation/conversion/AdamsMoultonFieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/AdamsMoultonFieldIntegratorBuilder.java index e6e682c10c..fad14e4eec 100644 --- a/src/main/java/org/orekit/propagation/conversion/AdamsMoultonFieldIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/AdamsMoultonFieldIntegratorBuilder.java @@ -36,7 +36,7 @@ public class AdamsMoultonFieldIntegratorBuilder { /** - * Build a new instance. + * Build a new instance. Should only use this constructor with {@link Orbit}. * * @param nSteps number of steps * @param minStep minimum step size (s) @@ -51,10 +51,28 @@ public AdamsMoultonFieldIntegratorBuilder(final int nSteps, final double minStep super(nSteps, minStep, maxStep, dP); } + /** + * Build a new instance. + * + * @param nSteps number of steps + * @param minStep minimum step size (s) + * @param maxStep maximum step size (s) + * @param dP position error (m) + * @param dV velocity error (m/s) + * + * @since 12.2 + * @see AdamsMoultonFieldIntegrator + * @see NumericalPropagator#tolerances(double, double, Orbit, OrbitType) + */ + public AdamsMoultonFieldIntegratorBuilder(final int nSteps, final double minStep, + final double maxStep, final double dP, final double dV) { + super(nSteps, minStep, maxStep, dP, dV); + } + /** {@inheritDoc} */ @Override public AbstractFieldIntegrator buildIntegrator(final Field field, final Orbit orbit, final OrbitType orbitType) { - final double[][] tol = NumericalPropagator.tolerances(dP, orbit, orbitType); + final double[][] tol = getTolerances(orbit, orbitType); return new AdamsMoultonFieldIntegrator<>(field, nSteps, minStep, maxStep, tol[0], tol[1]); } } diff --git a/src/main/java/org/orekit/propagation/conversion/AdamsMoultonIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/AdamsMoultonIntegratorBuilder.java index 9f381d6329..4bb5edcf85 100644 --- a/src/main/java/org/orekit/propagation/conversion/AdamsMoultonIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/AdamsMoultonIntegratorBuilder.java @@ -26,39 +26,46 @@ * @author Pascal Parraud * @since 6.0 */ -public class AdamsMoultonIntegratorBuilder implements ODEIntegratorBuilder { +public class AdamsMoultonIntegratorBuilder extends AbstractVariableStepIntegratorBuilder { /** Number of steps. */ private final int nSteps; - /** Minimum step size (s). */ - private final double minStep; - - /** Maximum step size (s). */ - private final double maxStep; - - /** Minimum step size (s). */ - private final double dP; + /** Build a new instance. Should only use this constructor with {@link Orbit}. + * @param nSteps number of steps + * @param minStep minimum step size (s) + * @param maxStep maximum step size (s) + * @param dP position error (m) + * @see AdamsMoultonIntegrator + * @see NumericalPropagator#tolerances(double, Orbit, OrbitType) + */ + public AdamsMoultonIntegratorBuilder(final int nSteps, final double minStep, + final double maxStep, final double dP) { + super(minStep, maxStep, dP); + this.nSteps = nSteps; + } /** Build a new instance. * @param nSteps number of steps * @param minStep minimum step size (s) * @param maxStep maximum step size (s) * @param dP position error (m) + * @param dV velocity error (m/s) + * + * @since 12.2 * @see AdamsMoultonIntegrator * @see NumericalPropagator#tolerances(double, Orbit, OrbitType) */ public AdamsMoultonIntegratorBuilder(final int nSteps, final double minStep, - final double maxStep, final double dP) { + final double maxStep, final double dP, final double dV) { + super(minStep, maxStep, dP, dV); this.nSteps = nSteps; - this.minStep = minStep; - this.maxStep = maxStep; - this.dP = dP; } /** {@inheritDoc} */ + @Override public AbstractIntegrator buildIntegrator(final Orbit orbit, final OrbitType orbitType) { - final double[][] tol = NumericalPropagator.tolerances(dP, orbit, orbitType); + final double[][] tol = getTolerances(orbit, orbitType); return new AdamsMoultonIntegrator(nSteps, minStep, maxStep, tol[0], tol[1]); } diff --git a/src/main/java/org/orekit/propagation/conversion/BrouwerLyddanePropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/BrouwerLyddanePropagatorBuilder.java index fb1ea400f1..afd9e4e9d5 100644 --- a/src/main/java/org/orekit/propagation/conversion/BrouwerLyddanePropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/BrouwerLyddanePropagatorBuilder.java @@ -17,24 +17,19 @@ package org.orekit.propagation.conversion; import java.util.Collections; -import java.util.List; import org.hipparchus.util.FastMath; import org.orekit.attitudes.AttitudeProvider; import org.orekit.attitudes.FrameAlignedProvider; -import org.orekit.estimation.leastsquares.AbstractBatchLSModel; -import org.orekit.estimation.leastsquares.BatchLSModel; -import org.orekit.estimation.leastsquares.ModelObserver; -import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.forces.gravity.potential.GravityFieldFactory; import org.orekit.forces.gravity.potential.TideSystem; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.Propagator; import org.orekit.propagation.analytical.BrouwerLyddanePropagator; import org.orekit.propagation.analytical.tle.TLE; import org.orekit.utils.ParameterDriver; -import org.orekit.utils.ParameterDriversList; /** Builder for Brouwer-Lyddane propagator. *

      @@ -65,7 +60,7 @@ * @author Bryan Cazabonne * @since 11.1 */ -public class BrouwerLyddanePropagatorBuilder extends AbstractPropagatorBuilder { +public class BrouwerLyddanePropagatorBuilder extends AbstractAnalyticalPropagatorBuilder { /** Parameters scaling factor. *

      @@ -211,7 +206,8 @@ public BrouwerLyddanePropagatorBuilder(final Orbit templateOrbit, final double positionScale, final AttitudeProvider attitudeProvider, final double M2) { - super(overrideMu(templateOrbit, provider, positionAngleType), positionAngleType, positionScale, true, attitudeProvider); + super(overrideMu(templateOrbit, provider, positionAngleType), positionAngleType, positionScale, true, attitudeProvider, + Propagator.DEFAULT_MASS); this.provider = provider; // initialize M2 driver final ParameterDriver M2Driver = new ParameterDriver(BrouwerLyddanePropagator.M2_NAME, M2, SCALE, @@ -240,6 +236,7 @@ private static Orbit overrideMu(final Orbit templateOrbit, /** {@inheritDoc} */ @Override + @Deprecated public BrouwerLyddanePropagatorBuilder copy() { // Find M2 value @@ -252,8 +249,10 @@ public BrouwerLyddanePropagatorBuilder copy() { } } - return new BrouwerLyddanePropagatorBuilder(createInitialOrbit(), provider, getPositionAngleType(), + final BrouwerLyddanePropagatorBuilder builder = new BrouwerLyddanePropagatorBuilder(createInitialOrbit(), provider, getPositionAngleType(), getPositionScale(), getAttitudeProvider(), m2); + builder.setMass(getMass()); + return builder; } /** {@inheritDoc} */ @@ -273,21 +272,32 @@ public BrouwerLyddanePropagator buildPropagator(final double[] normalizedParamet } // Initialize propagator - final BrouwerLyddanePropagator propagator = new BrouwerLyddanePropagator(createInitialOrbit(), getAttitudeProvider(), provider, newM2); + final BrouwerLyddanePropagator propagator = new BrouwerLyddanePropagator(createInitialOrbit(), getAttitudeProvider(), getMass(), + provider, newM2); propagator.getParametersDrivers().get(0).setSelected(isSelected); + getImpulseManeuvers().forEach(propagator::addEventDetector); // Return return propagator; } - /** {@inheritDoc} */ - @Override - public AbstractBatchLSModel buildLeastSquaresModel(final PropagatorBuilder[] builders, - final List> measurements, - final ParameterDriversList estimatedMeasurementsParameters, - final ModelObserver observer) { - return new BatchLSModel(builders, measurements, estimatedMeasurementsParameters, observer); + /** + * Get the value of the M2 parameter. + *

      + * M2 represents the combination of all unmodeled secular along-track effects + * (e.g. drag). It is usually fitted during an orbit determination. + *

      + * @return the value of the M2 parameter + * @since 12.2 + */ + public double getM2Value() { + double m2 = 0.0; + for (final ParameterDriver driver : getPropagationParametersDrivers().getDrivers()) { + if (BrouwerLyddanePropagator.M2_NAME.equals(driver.getName())) { + m2 = driver.getValue(); + } + } + return m2; } - } diff --git a/src/main/java/org/orekit/propagation/conversion/DSSTPropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/DSSTPropagatorBuilder.java index fa665389ee..458667e06d 100644 --- a/src/main/java/org/orekit/propagation/conversion/DSSTPropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/DSSTPropagatorBuilder.java @@ -52,9 +52,6 @@ public class DSSTPropagatorBuilder extends AbstractPropagatorBuilder { /** Force models used during the extrapolation of the orbit. */ private final List forceModels; - /** Current mass for initial state (kg). */ - private double mass; - /** Type of the orbit used for the propagation.*/ private PropagationType propagationType; @@ -114,10 +111,9 @@ public DSSTPropagatorBuilder(final Orbit referenceOrbit, final PropagationType propagationType, final PropagationType stateType, final AttitudeProvider attitudeProvider) { - super(referenceOrbit, PositionAngleType.MEAN, positionScale, true, attitudeProvider); + super(referenceOrbit, PositionAngleType.MEAN, positionScale, true, attitudeProvider, Propagator.DEFAULT_MASS); this.builder = builder; this.forceModels = new ArrayList<>(); - this.mass = Propagator.DEFAULT_MASS; this.propagationType = propagationType; this.stateType = stateType; } @@ -139,6 +135,7 @@ public PropagationType getStateType() { /** Create a copy of a DSSTPropagatorBuilder object. * @return Copied version of the DSSTPropagatorBuilder */ + @Deprecated public DSSTPropagatorBuilder copy() { final DSSTPropagatorBuilder copyBuilder = new DSSTPropagatorBuilder(createInitialOrbit(), @@ -147,7 +144,7 @@ public DSSTPropagatorBuilder copy() { propagationType, stateType, getAttitudeProvider()); - copyBuilder.setMass(mass); + copyBuilder.setMass(getMass()); for (DSSTForceModel model : forceModels) { copyBuilder.addForceModel(model); } @@ -170,21 +167,6 @@ public List getAllForceModels() return Collections.unmodifiableList(forceModels); } - /** Get the mass. - * @return the mass - */ - public double getMass() - { - return mass; - } - - /** Set the initial mass. - * @param mass the mass (kg) - */ - public void setMass(final double mass) { - this.mass = mass; - } - /** Add a force model to the global perturbation model. *

      If this method is not called at all, the integrated orbit will follow * a Keplerian evolution only.

      @@ -230,7 +212,7 @@ public DSSTPropagator buildPropagator(final double[] normalizedParameters) { setParameters(normalizedParameters); final EquinoctialOrbit orbit = (EquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(createInitialOrbit()); final Attitude attitude = getAttitudeProvider().getAttitude(orbit, orbit.getDate(), getFrame()); - final SpacecraftState state = new SpacecraftState(orbit, attitude, mass); + final SpacecraftState state = new SpacecraftState(orbit, attitude, getMass()); final DSSTPropagator propagator = new DSSTPropagator( builder.buildIntegrator(orbit, OrbitType.EQUINOCTIAL), diff --git a/src/main/java/org/orekit/propagation/conversion/DormandPrince54FieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/DormandPrince54FieldIntegratorBuilder.java index 34177d8287..9b117cb2f2 100644 --- a/src/main/java/org/orekit/propagation/conversion/DormandPrince54FieldIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/DormandPrince54FieldIntegratorBuilder.java @@ -36,7 +36,7 @@ public class DormandPrince54FieldIntegratorBuilder { /** - * Build a new instance. + * Build a new instance. Should only use this constructor with {@link Orbit}. * * @param minStep minimum step size (s) * @param maxStep maximum step size (s) @@ -49,10 +49,27 @@ public DormandPrince54FieldIntegratorBuilder(final double minStep, final double super(minStep, maxStep, dP); } + /** + * Build a new instance. + * + * @param minStep minimum step size (s) + * @param maxStep maximum step size (s) + * @param dP position error (m) + * @param dV velocity error (m/s) + * + * @since 12.2 + * @see DormandPrince54FieldIntegrator + * @see NumericalPropagator#tolerances(double, double, Orbit, OrbitType) + */ + public DormandPrince54FieldIntegratorBuilder(final double minStep, final double maxStep, final double dP, + final double dV) { + super(minStep, maxStep, dP, dV); + } + /** {@inheritDoc} */ @Override public AbstractFieldIntegrator buildIntegrator(final Field field, final Orbit orbit, final OrbitType orbitType) { - final double[][] tol = NumericalPropagator.tolerances(dP, orbit, orbitType); + final double[][] tol = getTolerances(orbit, orbitType); return new DormandPrince54FieldIntegrator<>(field, minStep, maxStep, tol[0], tol[1]); } } diff --git a/src/main/java/org/orekit/propagation/conversion/DormandPrince54IntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/DormandPrince54IntegratorBuilder.java index a16c7c1783..d9e84f6aee 100644 --- a/src/main/java/org/orekit/propagation/conversion/DormandPrince54IntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/DormandPrince54IntegratorBuilder.java @@ -26,18 +26,9 @@ * @author Pascal Parraud * @since 6.0 */ -public class DormandPrince54IntegratorBuilder implements ODEIntegratorBuilder { +public class DormandPrince54IntegratorBuilder extends AbstractVariableStepIntegratorBuilder { - /** Minimum step size (s). */ - private final double minStep; - - /** Maximum step size (s). */ - private final double maxStep; - - /** Minimum step size (s). */ - private final double dP; - - /** Build a new instance. + /** Build a new instance. Should only use this constructor with {@link Orbit}. * @param minStep minimum step size (s) * @param maxStep maximum step size (s) * @param dP position error (m) @@ -45,14 +36,27 @@ public class DormandPrince54IntegratorBuilder implements ODEIntegratorBuilder { * @see NumericalPropagator#tolerances(double, Orbit, OrbitType) */ public DormandPrince54IntegratorBuilder(final double minStep, final double maxStep, final double dP) { - this.minStep = minStep; - this.maxStep = maxStep; - this.dP = dP; + super(minStep, maxStep, dP); + } + + /** Build a new instance. + * @param minStep minimum step size (s) + * @param maxStep maximum step size (s) + * @param dP position error (m) + * @param dV velocity error (m/s) + * + * @since 12.2 + * @see DormandPrince54Integrator + * @see NumericalPropagator#tolerances(double, double, Orbit, OrbitType) + */ + public DormandPrince54IntegratorBuilder(final double minStep, final double maxStep, final double dP, + final double dV) { + super(minStep, maxStep, dP, dV); } /** {@inheritDoc} */ public AbstractIntegrator buildIntegrator(final Orbit orbit, final OrbitType orbitType) { - final double[][] tol = NumericalPropagator.tolerances(dP, orbit, orbitType); + final double[][] tol = getTolerances(orbit, orbitType); return new DormandPrince54Integrator(minStep, maxStep, tol[0], tol[1]); } diff --git a/src/main/java/org/orekit/propagation/conversion/DormandPrince853FieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/DormandPrince853FieldIntegratorBuilder.java index 25d65137fa..9fba6703f8 100644 --- a/src/main/java/org/orekit/propagation/conversion/DormandPrince853FieldIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/DormandPrince853FieldIntegratorBuilder.java @@ -36,7 +36,7 @@ public class DormandPrince853FieldIntegratorBuilder { /** - * Build a new instance. + * Build a new instance. Should use this constructor only with {@link Orbit} * * @param minStep minimum step size (s) * @param maxStep maximum step size (s) @@ -49,10 +49,27 @@ public DormandPrince853FieldIntegratorBuilder(final double minStep, final double super(minStep, maxStep, dP); } + /** + * Build a new instance. + * + * @param minStep minimum step size (s) + * @param maxStep maximum step size (s) + * @param dP position error (m) + * @param dV velocity error (m/s) + * + * @since 12.2 + * @see DormandPrince853FieldIntegrator + * @see NumericalPropagator#tolerances(double, double, Orbit, OrbitType) + */ + public DormandPrince853FieldIntegratorBuilder(final double minStep, final double maxStep, final double dP, + final double dV) { + super(minStep, maxStep, dP, dV); + } + /** {@inheritDoc} */ @Override public AbstractFieldIntegrator buildIntegrator(final Field field, final Orbit orbit, final OrbitType orbitType) { - final double[][] tol = NumericalPropagator.tolerances(dP, orbit, orbitType); + final double[][] tol = getTolerances(orbit, orbitType); return new DormandPrince853FieldIntegrator<>(field, minStep, maxStep, tol[0], tol[1]); } } diff --git a/src/main/java/org/orekit/propagation/conversion/DormandPrince853IntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/DormandPrince853IntegratorBuilder.java index c840060d87..de3ba8e331 100644 --- a/src/main/java/org/orekit/propagation/conversion/DormandPrince853IntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/DormandPrince853IntegratorBuilder.java @@ -26,18 +26,9 @@ * @author Pascal Parraud * @since 6.0 */ -public class DormandPrince853IntegratorBuilder implements ODEIntegratorBuilder { +public class DormandPrince853IntegratorBuilder extends AbstractVariableStepIntegratorBuilder { - /** Minimum step size (s). */ - private final double minStep; - - /** Maximum step size (s). */ - private final double maxStep; - - /** Position error (m). */ - private final double dP; - - /** Build a new instance. + /** Build a new instance. Should only use this constructor with {@link Orbit}. * @param minStep minimum step size (s) * @param maxStep maximum step size (s) * @param dP position error (m) @@ -45,14 +36,28 @@ public class DormandPrince853IntegratorBuilder implements ODEIntegratorBuilder { * @see NumericalPropagator#tolerances(double, Orbit, OrbitType) */ public DormandPrince853IntegratorBuilder(final double minStep, final double maxStep, final double dP) { - this.minStep = minStep; - this.maxStep = maxStep; - this.dP = dP; + super(minStep, maxStep, dP); + } + + /** Build a new instance. + * @param minStep minimum step size (s) + * @param maxStep maximum step size (s) + * @param dP position error (m) + * @param dV velocity error (m/s) + * + * @since 12.2 + * @see DormandPrince853Integrator + * @see NumericalPropagator#tolerances(double, double, Orbit, OrbitType) + */ + public DormandPrince853IntegratorBuilder(final double minStep, final double maxStep, final double dP, + final double dV) { + super(minStep, maxStep, dP, dV); } /** {@inheritDoc} */ + @Override public AbstractIntegrator buildIntegrator(final Orbit orbit, final OrbitType orbitType) { - final double[][] tol = NumericalPropagator.tolerances(dP, orbit, orbitType); + final double[][] tol = getTolerances(orbit, orbitType); return new DormandPrince853Integrator(minStep, maxStep, tol[0], tol[1]); } diff --git a/src/main/java/org/orekit/propagation/conversion/EcksteinHechlerPropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/EcksteinHechlerPropagatorBuilder.java index 0b150f99d0..e8e1ef8757 100644 --- a/src/main/java/org/orekit/propagation/conversion/EcksteinHechlerPropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/EcksteinHechlerPropagatorBuilder.java @@ -16,14 +16,8 @@ */ package org.orekit.propagation.conversion; -import java.util.List; - import org.orekit.attitudes.AttitudeProvider; import org.orekit.attitudes.FrameAlignedProvider; -import org.orekit.estimation.leastsquares.AbstractBatchLSModel; -import org.orekit.estimation.leastsquares.BatchLSModel; -import org.orekit.estimation.leastsquares.ModelObserver; -import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.forces.gravity.potential.GravityFieldFactory; import org.orekit.forces.gravity.potential.TideSystem; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; @@ -32,13 +26,12 @@ import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.Propagator; import org.orekit.propagation.analytical.EcksteinHechlerPropagator; -import org.orekit.utils.ParameterDriversList; /** Builder for Eckstein-Hechler propagator. * @author Pascal Parraud * @since 6.0 */ -public class EcksteinHechlerPropagatorBuilder extends AbstractPropagatorBuilder { +public class EcksteinHechlerPropagatorBuilder extends AbstractAnalyticalPropagatorBuilder { /** Provider for un-normalized coefficients. */ private final UnnormalizedSphericalHarmonicsProvider provider; @@ -98,7 +91,7 @@ public EcksteinHechlerPropagatorBuilder(final Orbit templateOrbit, final double positionScale, final AttitudeProvider attitudeProvider) { super(overrideMu(templateOrbit, provider, positionAngleType), positionAngleType, - positionScale, true, attitudeProvider); + positionScale, true, attitudeProvider, Propagator.DEFAULT_MASS); this.provider = provider; } @@ -202,23 +195,19 @@ private static Orbit overrideMu(final Orbit templateOrbit, /** {@inheritDoc} */ public Propagator buildPropagator(final double[] normalizedParameters) { setParameters(normalizedParameters); - return new EcksteinHechlerPropagator(createInitialOrbit(), getAttitudeProvider(), - provider); - } - - /** {@inheritDoc} */ - @Override - public AbstractBatchLSModel buildLeastSquaresModel(final PropagatorBuilder[] builders, - final List> measurements, - final ParameterDriversList estimatedMeasurementsParameters, - final ModelObserver observer) { - return new BatchLSModel(builders, measurements, estimatedMeasurementsParameters, observer); + final EcksteinHechlerPropagator propagator = new EcksteinHechlerPropagator(createInitialOrbit(), getAttitudeProvider(), + getMass(), provider); + getImpulseManeuvers().forEach(propagator::addEventDetector); + return propagator; } /** {@inheritDoc} */ @Override + @Deprecated public EcksteinHechlerPropagatorBuilder copy() { - return new EcksteinHechlerPropagatorBuilder(createInitialOrbit(), provider, getPositionAngleType(), + final EcksteinHechlerPropagatorBuilder builder = new EcksteinHechlerPropagatorBuilder(createInitialOrbit(), provider, getPositionAngleType(), getPositionScale(), getAttitudeProvider()); + builder.setMass(getMass()); + return builder; } } diff --git a/src/main/java/org/orekit/propagation/conversion/EphemerisPropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/EphemerisPropagatorBuilder.java index 3d7e64f9db..e71a8d4236 100644 --- a/src/main/java/org/orekit/propagation/conversion/EphemerisPropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/EphemerisPropagatorBuilder.java @@ -159,6 +159,7 @@ public EphemerisPropagatorBuilder(final List states, /** {@inheritDoc} */ @Override + @Deprecated public EphemerisPropagatorBuilder copy() { return new EphemerisPropagatorBuilder(states, stateInterpolator, covariances, covarianceInterpolator, provider); } diff --git a/src/main/java/org/orekit/propagation/conversion/FieldODEIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/FieldODEIntegratorBuilder.java index bbfa932e36..ab2b10fdca 100644 --- a/src/main/java/org/orekit/propagation/conversion/FieldODEIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/FieldODEIntegratorBuilder.java @@ -19,9 +19,12 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; import org.hipparchus.ode.AbstractFieldIntegrator; +import org.orekit.orbits.FieldCartesianOrbit; import org.orekit.orbits.FieldOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; +import org.orekit.utils.FieldAbsolutePVCoordinates; +import org.orekit.utils.TimeStampedFieldPVCoordinates; /** * This interface is the top-level abstraction to build first order integrators for propagators conversion. @@ -56,4 +59,18 @@ AbstractFieldIntegrator buildIntegrator(Field field, */ AbstractFieldIntegrator buildIntegrator(FieldOrbit orbit, OrbitType orbitType); + + /** + * Build a first order integrator. Non-orbit version. + * + * @param fieldAbsolutePVCoordinates absolute position-velocity + * + * @return a first order integrator ready to use + */ + default AbstractFieldIntegrator buildIntegrator(final FieldAbsolutePVCoordinates fieldAbsolutePVCoordinates) { + final TimeStampedFieldPVCoordinates fieldPVCoordinates = fieldAbsolutePVCoordinates.getPVCoordinates(); + final FieldCartesianOrbit fieldOrbit = new FieldCartesianOrbit<>(fieldPVCoordinates, fieldAbsolutePVCoordinates.getFrame(), + fieldAbsolutePVCoordinates.getDate().getField().getOne()); + return buildIntegrator(fieldOrbit, OrbitType.CARTESIAN); + } } diff --git a/src/main/java/org/orekit/propagation/conversion/GraggBulirschStoerIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/GraggBulirschStoerIntegratorBuilder.java index c89c2845ed..8c7f9110c6 100644 --- a/src/main/java/org/orekit/propagation/conversion/GraggBulirschStoerIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/GraggBulirschStoerIntegratorBuilder.java @@ -26,18 +26,9 @@ * @author Pascal Parraud * @since 6.0 */ -public class GraggBulirschStoerIntegratorBuilder implements ODEIntegratorBuilder { +public class GraggBulirschStoerIntegratorBuilder extends AbstractVariableStepIntegratorBuilder { - /** Minimum step size (s). */ - private final double minStep; - - /** Maximum step size (s). */ - private final double maxStep; - - /** Minimum step size (s). */ - private final double dP; - - /** Build a new instance. + /** Build a new instance. Should only use this constructor with {@link Orbit}. * @param minStep minimum step size (s) * @param maxStep maximum step size (s) * @param dP position error (m) @@ -45,14 +36,28 @@ public class GraggBulirschStoerIntegratorBuilder implements ODEIntegratorBuilder * @see NumericalPropagator#tolerances(double, Orbit, OrbitType) */ public GraggBulirschStoerIntegratorBuilder(final double minStep, final double maxStep, final double dP) { - this.minStep = minStep; - this.maxStep = maxStep; - this.dP = dP; + super(minStep, maxStep, dP); + } + + /** Build a new instance. + * @param minStep minimum step size (s) + * @param maxStep maximum step size (s) + * @param dP position error (m) + * @param dV velocity error (m/s) + * + * @since 12.2 + * @see GraggBulirschStoerIntegrator + * @see NumericalPropagator#tolerances(double, double, Orbit, OrbitType) + */ + public GraggBulirschStoerIntegratorBuilder(final double minStep, final double maxStep, final double dP, + final double dV) { + super(minStep, maxStep, dP, dV); } /** {@inheritDoc} */ + @Override public AbstractIntegrator buildIntegrator(final Orbit orbit, final OrbitType orbitType) { - final double[][] tol = NumericalPropagator.tolerances(dP, orbit, orbitType); + final double[][] tol = getTolerances(orbit, orbitType); return new GraggBulirschStoerIntegrator(minStep, maxStep, tol[0], tol[1]); } diff --git a/src/main/java/org/orekit/propagation/conversion/HighamHall54FieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/HighamHall54FieldIntegratorBuilder.java index e100e9adc3..7dcfd59076 100644 --- a/src/main/java/org/orekit/propagation/conversion/HighamHall54FieldIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/HighamHall54FieldIntegratorBuilder.java @@ -36,7 +36,7 @@ public class HighamHall54FieldIntegratorBuilder { /** - * Build a new instance. + * Build a new instance. Should only use this constructor with {@link Orbit}. * * @param minStep minimum step size (s) * @param maxStep maximum step size (s) @@ -49,10 +49,27 @@ public HighamHall54FieldIntegratorBuilder(final double minStep, final double max super(minStep, maxStep, dP); } + /** + * Build a new instance. + * + * @param minStep minimum step size (s) + * @param maxStep maximum step size (s) + * @param dP position error (m) + * @param dV velocity error (m/s) + * + * @since 12.2 + * @see HighamHall54FieldIntegrator + * @see NumericalPropagator#tolerances(double, double, Orbit, OrbitType) + */ + public HighamHall54FieldIntegratorBuilder(final double minStep, final double maxStep, final double dP, + final double dV) { + super(minStep, maxStep, dP, dV); + } + /** {@inheritDoc} */ @Override public AbstractFieldIntegrator buildIntegrator(final Field field, final Orbit orbit, final OrbitType orbitType) { - final double[][] tol = NumericalPropagator.tolerances(dP, orbit, orbitType); + final double[][] tol = getTolerances(orbit, orbitType); return new HighamHall54FieldIntegrator<>(field, minStep, maxStep, tol[0], tol[1]); } } diff --git a/src/main/java/org/orekit/propagation/conversion/HighamHall54IntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/HighamHall54IntegratorBuilder.java index 2f1ae5b0e6..02bba7a807 100644 --- a/src/main/java/org/orekit/propagation/conversion/HighamHall54IntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/HighamHall54IntegratorBuilder.java @@ -26,18 +26,9 @@ * @author Pascal Parraud * @since 6.0 */ -public class HighamHall54IntegratorBuilder implements ODEIntegratorBuilder { +public class HighamHall54IntegratorBuilder extends AbstractVariableStepIntegratorBuilder { - /** Minimum step size (s). */ - private final double minStep; - - /** Maximum step size (s). */ - private final double maxStep; - - /** Minimum step size (s). */ - private final double dP; - - /** Build a new instance. + /** Build a new instance. Should only use this constructor with {@link Orbit}. * @param minStep minimum step size (s) * @param maxStep maximum step size (s) * @param dP position error (m) @@ -45,14 +36,28 @@ public class HighamHall54IntegratorBuilder implements ODEIntegratorBuilder { * @see NumericalPropagator#tolerances(double, Orbit, OrbitType) */ public HighamHall54IntegratorBuilder(final double minStep, final double maxStep, final double dP) { - this.minStep = minStep; - this.maxStep = maxStep; - this.dP = dP; + super(minStep, maxStep, dP); + } + + /** Build a new instance. + * @param minStep minimum step size (s) + * @param maxStep maximum step size (s) + * @param dP position error (m) + * @param dV velocity error (m/s) + * + * @since 12.2 + * @see HighamHall54Integrator + * @see NumericalPropagator#tolerances(double, double, Orbit, OrbitType) + */ + public HighamHall54IntegratorBuilder(final double minStep, final double maxStep, final double dP, + final double dV) { + super(minStep, maxStep, dP, dV); } /** {@inheritDoc} */ + @Override public AbstractIntegrator buildIntegrator(final Orbit orbit, final OrbitType orbitType) { - final double[][] tol = NumericalPropagator.tolerances(dP, orbit, orbitType); + final double[][] tol = getTolerances(orbit, orbitType); return new HighamHall54Integrator(minStep, maxStep, tol[0], tol[1]); } diff --git a/src/main/java/org/orekit/propagation/conversion/KeplerianPropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/KeplerianPropagatorBuilder.java index 73302f9163..2ad6a23800 100644 --- a/src/main/java/org/orekit/propagation/conversion/KeplerianPropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/KeplerianPropagatorBuilder.java @@ -16,25 +16,19 @@ */ package org.orekit.propagation.conversion; -import java.util.List; - import org.orekit.attitudes.AttitudeProvider; import org.orekit.attitudes.FrameAlignedProvider; -import org.orekit.estimation.leastsquares.AbstractBatchLSModel; -import org.orekit.estimation.leastsquares.BatchLSModel; -import org.orekit.estimation.leastsquares.ModelObserver; -import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.orbits.Orbit; import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.Propagator; import org.orekit.propagation.analytical.KeplerianPropagator; -import org.orekit.utils.ParameterDriversList; + /** Builder for Keplerian propagator. * @author Pascal Parraud * @since 6.0 */ -public class KeplerianPropagatorBuilder extends AbstractPropagatorBuilder { +public class KeplerianPropagatorBuilder extends AbstractAnalyticalPropagatorBuilder { /** Build a new instance. *

      @@ -80,29 +74,24 @@ public KeplerianPropagatorBuilder(final Orbit templateOrbit, final PositionAngleType positionAngleType, final double positionScale, final AttitudeProvider attitudeProvider) { - super(templateOrbit, positionAngleType, positionScale, true, attitudeProvider); + super(templateOrbit, positionAngleType, positionScale, true, attitudeProvider, Propagator.DEFAULT_MASS); } /** {@inheritDoc} */ @Override + @Deprecated public KeplerianPropagatorBuilder copy() { - return new KeplerianPropagatorBuilder(createInitialOrbit(), getPositionAngleType(), + final KeplerianPropagatorBuilder builder = new KeplerianPropagatorBuilder(createInitialOrbit(), getPositionAngleType(), getPositionScale(), getAttitudeProvider()); + builder.setMass(getMass()); + return builder; } /** {@inheritDoc} */ public Propagator buildPropagator(final double[] normalizedParameters) { setParameters(normalizedParameters); - return new KeplerianPropagator(createInitialOrbit(), getAttitudeProvider()); + final KeplerianPropagator propagator = new KeplerianPropagator(createInitialOrbit(), getAttitudeProvider(), getMu(), getMass()); + getImpulseManeuvers().forEach(propagator::addEventDetector); + return propagator; } - - /** {@inheritDoc} */ - @Override - public AbstractBatchLSModel buildLeastSquaresModel(final PropagatorBuilder[] builders, - final List> measurements, - final ParameterDriversList estimatedMeasurementsParameters, - final ModelObserver observer) { - return new BatchLSModel(builders, measurements, estimatedMeasurementsParameters, observer); - } - } diff --git a/src/main/java/org/orekit/propagation/conversion/NumericalPropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/NumericalPropagatorBuilder.java index dfb2515775..33dcdc02dd 100644 --- a/src/main/java/org/orekit/propagation/conversion/NumericalPropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/NumericalPropagatorBuilder.java @@ -28,6 +28,7 @@ import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.forces.ForceModel; import org.orekit.forces.gravity.NewtonianAttraction; +import org.orekit.forces.maneuvers.ImpulseManeuver; import org.orekit.orbits.Orbit; import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.Propagator; @@ -49,8 +50,8 @@ public class NumericalPropagatorBuilder extends AbstractPropagatorBuilder { /** Force models used during the extrapolation of the orbit. */ private final List forceModels; - /** Current mass for initial state (kg). */ - private double mass; + /** Impulse maneuvers. */ + private final List impulseManeuvers; /** Build a new instance. *

      @@ -102,15 +103,33 @@ public NumericalPropagatorBuilder(final Orbit referenceOrbit, final PositionAngleType positionAngleType, final double positionScale, final AttitudeProvider attitudeProvider) { - super(referenceOrbit, positionAngleType, positionScale, true, attitudeProvider); + super(referenceOrbit, positionAngleType, positionScale, true, attitudeProvider, Propagator.DEFAULT_MASS); this.builder = builder; - this.forceModels = new ArrayList(); - this.mass = Propagator.DEFAULT_MASS; + this.forceModels = new ArrayList<>(); + this.impulseManeuvers = new ArrayList<>(); + } + + /** + * Add impulse maneuver. + * @param impulseManeuver impulse maneuver + * @since 12.2 + */ + public void addImpulseManeuver(final ImpulseManeuver impulseManeuver) { + impulseManeuvers.add(impulseManeuver); + } + + /** + * Remove all impulse maneuvers. + * @since 12.2 + */ + public void clearImpulseManeuvers() { + impulseManeuvers.clear(); } /** Create a copy of a NumericalPropagatorBuilder object. * @return Copied version of the NumericalPropagatorBuilder */ + @Deprecated public NumericalPropagatorBuilder copy() { final NumericalPropagatorBuilder copyBuilder = new NumericalPropagatorBuilder(createInitialOrbit(), @@ -118,10 +137,11 @@ public NumericalPropagatorBuilder copy() { getPositionAngleType(), getPositionScale(), getAttitudeProvider()); - copyBuilder.setMass(mass); + copyBuilder.setMass(getMass()); for (ForceModel model : forceModels) { copyBuilder.addForceModel(model); } + impulseManeuvers.forEach(copyBuilder::addImpulseManeuver); return copyBuilder; } @@ -173,22 +193,6 @@ public void addForceModel(final ForceModel model) { addSupportedParameters(model.getParametersDrivers()); } - /** Get the mass. - * @return the mass - * @since 9.2 - */ - public double getMass() - { - return mass; - } - - /** Set the initial mass. - * @param mass the mass (kg) - */ - public void setMass(final double mass) { - this.mass = mass; - } - /** {@inheritDoc} */ public NumericalPropagator buildPropagator(final double[] normalizedParameters) { @@ -196,7 +200,7 @@ public NumericalPropagator buildPropagator(final double[] normalizedParameters) final Orbit orbit = createInitialOrbit(); final Attitude attitude = getAttitudeProvider().getAttitude(orbit, orbit.getDate(), getFrame()); - final SpacecraftState state = new SpacecraftState(orbit, attitude, mass); + final SpacecraftState state = new SpacecraftState(orbit, attitude, getMass()); final NumericalPropagator propagator = new NumericalPropagator( builder.buildIntegrator(orbit, getOrbitType()), @@ -212,6 +216,7 @@ public NumericalPropagator buildPropagator(final double[] normalizedParameters) for (ForceModel model : forceModels) { propagator.addForceModel(model); } + impulseManeuvers.forEach(propagator::addEventDetector); propagator.resetInitialState(state); diff --git a/src/main/java/org/orekit/propagation/conversion/ODEIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/ODEIntegratorBuilder.java index 5110ec57cc..97867f3be9 100644 --- a/src/main/java/org/orekit/propagation/conversion/ODEIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/ODEIntegratorBuilder.java @@ -17,8 +17,10 @@ package org.orekit.propagation.conversion; import org.hipparchus.ode.AbstractIntegrator; +import org.orekit.orbits.CartesianOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; +import org.orekit.utils.AbsolutePVCoordinates; /** This interface is the top-level abstraction to build first order integrators for propagators conversion. * @author Pascal Parraud @@ -33,4 +35,15 @@ public interface ODEIntegratorBuilder { */ AbstractIntegrator buildIntegrator(Orbit orbit, OrbitType orbitType); + /** + * Build a first order integrator. Non-orbit version. + * @param absolutePVCoordinates absolute position-velocity vector + * @return a first order integrator ready to use + */ + default AbstractIntegrator buildIntegrator(final AbsolutePVCoordinates absolutePVCoordinates) { + final double arbitraryMu = 1.; + final CartesianOrbit cartesianOrbit = new CartesianOrbit(absolutePVCoordinates.getPVCoordinates(), + absolutePVCoordinates.getFrame(), arbitraryMu); + return buildIntegrator(cartesianOrbit, OrbitType.CARTESIAN); + } } diff --git a/src/main/java/org/orekit/propagation/conversion/PropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/PropagatorBuilder.java index ee51c8d98e..36f05019ab 100644 --- a/src/main/java/org/orekit/propagation/conversion/PropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/PropagatorBuilder.java @@ -16,8 +16,6 @@ */ package org.orekit.propagation.conversion; -import java.util.List; - import org.orekit.estimation.leastsquares.AbstractBatchLSModel; import org.orekit.estimation.leastsquares.ModelObserver; import org.orekit.estimation.measurements.ObservedMeasurement; @@ -29,15 +27,19 @@ import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriversList; +import java.util.List; + /** This interface is the top-level abstraction to build propagators for conversion. * @author Pascal Parraud * @since 6.0 */ -public interface PropagatorBuilder { +public interface PropagatorBuilder extends Cloneable { /** Create a new instance identical to this one. * @return new instance identical to this one + * @deprecated as of 12.2, replaced by {@link Object#clone()} */ + @Deprecated PropagatorBuilder copy(); /** Build a propagator. diff --git a/src/main/java/org/orekit/propagation/conversion/TLEPropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/TLEPropagatorBuilder.java index 5ca289a0ef..68a433623e 100644 --- a/src/main/java/org/orekit/propagation/conversion/TLEPropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/TLEPropagatorBuilder.java @@ -16,15 +16,10 @@ */ package org.orekit.propagation.conversion; -import java.util.List; - import org.orekit.annotation.DefaultDataContext; +import org.orekit.attitudes.AttitudeProvider; import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.data.DataContext; -import org.orekit.estimation.leastsquares.AbstractBatchLSModel; -import org.orekit.estimation.leastsquares.BatchLSModel; -import org.orekit.estimation.leastsquares.ModelObserver; -import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.frames.Frame; import org.orekit.orbits.Orbit; import org.orekit.orbits.PositionAngleType; @@ -34,14 +29,15 @@ import org.orekit.propagation.analytical.tle.TLEPropagator; import org.orekit.propagation.analytical.tle.generation.TleGenerationAlgorithm; import org.orekit.utils.ParameterDriver; -import org.orekit.utils.ParameterDriversList; + +import java.util.List; /** Builder for TLEPropagator. * @author Pascal Parraud * @author Thomas Paulet * @since 6.0 */ -public class TLEPropagatorBuilder extends AbstractPropagatorBuilder { +public class TLEPropagatorBuilder extends AbstractAnalyticalPropagatorBuilder { /** Data context used to access frames and time scales. */ private final DataContext dataContext; @@ -69,6 +65,7 @@ public class TLEPropagatorBuilder extends AbstractPropagatorBuilder { * @param generationAlgorithm TLE generation algorithm * @since 12.0 * @see #TLEPropagatorBuilder(TLE, PositionAngleType, double, DataContext, TleGenerationAlgorithm) + * @see #TLEPropagatorBuilder(TLE, PositionAngleType, double, DataContext, TleGenerationAlgorithm, AttitudeProvider) */ @DefaultDataContext public TLEPropagatorBuilder(final TLE templateTLE, final PositionAngleType positionAngleType, @@ -93,12 +90,37 @@ public TLEPropagatorBuilder(final TLE templateTLE, final PositionAngleType posit * @param dataContext used to access frames and time scales. * @param generationAlgorithm TLE generation algorithm * @since 12.0 + * @see #TLEPropagatorBuilder(TLE, PositionAngleType, double, DataContext, TleGenerationAlgorithm, AttitudeProvider) */ public TLEPropagatorBuilder(final TLE templateTLE, final PositionAngleType positionAngleType, final double positionScale, final DataContext dataContext, final TleGenerationAlgorithm generationAlgorithm) { - super(TLEPropagator.selectExtrapolator(templateTLE, dataContext.getFrames().getTEME()).getInitialState().getOrbit(), - positionAngleType, positionScale, false, FrameAlignedProvider.of(dataContext.getFrames().getTEME())); + this(templateTLE, positionAngleType, positionScale, dataContext, generationAlgorithm, FrameAlignedProvider.of(dataContext.getFrames().getTEME())); + } + + /** Build a new instance. + *

      + * The template TLE is used as a model to {@link + * #createInitialOrbit() create initial orbit}. It defines the + * inertial frame, the central attraction coefficient, orbit type, satellite number, + * classification, .... and is also used together with the {@code positionScale} to + * convert from the {@link ParameterDriver#setNormalizedValue(double) normalized} + * parameters used by the callers of this builder to the real orbital parameters. + *

      + * @param templateTLE reference TLE from which real orbits will be built + * @param positionAngleType position angle type to use + * @param positionScale scaling factor used for orbital parameters normalization + * (typically set to the expected standard deviation of the position) + * @param dataContext used to access frames and time scales. + * @param generationAlgorithm TLE generation algorithm + * @param attitudeProvider attitude law to use + * @since 12.2 + */ + public TLEPropagatorBuilder(final TLE templateTLE, final PositionAngleType positionAngleType, + final double positionScale, final DataContext dataContext, + final TleGenerationAlgorithm generationAlgorithm, final AttitudeProvider attitudeProvider) { + super(TLEPropagator.selectExtrapolator(templateTLE, dataContext.getFrames().getTEME(), attitudeProvider).getInitialState().getOrbit(), + positionAngleType, positionScale, false, attitudeProvider, Propagator.DEFAULT_MASS); // Supported parameters: Bstar addSupportedParameters(templateTLE.getParametersDrivers()); @@ -110,9 +132,12 @@ public TLEPropagatorBuilder(final TLE templateTLE, final PositionAngleType posit /** {@inheritDoc} */ @Override + @Deprecated public TLEPropagatorBuilder copy() { - return new TLEPropagatorBuilder(templateTLE, getPositionAngleType(), getPositionScale(), - dataContext, generationAlgorithm); + final TLEPropagatorBuilder builder = new TLEPropagatorBuilder(templateTLE, getPositionAngleType(), getPositionScale(), + dataContext, generationAlgorithm, getAttitudeProvider()); + builder.setMass(getMass()); + return builder; } /** {@inheritDoc} */ @@ -135,8 +160,9 @@ public TLEPropagator buildPropagator(final double[] normalizedParameters) { } // propagator - return TLEPropagator.selectExtrapolator(tle, getAttitudeProvider(), Propagator.DEFAULT_MASS, teme); - + final TLEPropagator propagator = TLEPropagator.selectExtrapolator(tle, getAttitudeProvider(), getMass(), teme); + getImpulseManeuvers().forEach(propagator::addEventDetector); + return propagator; } /** Getter for the template TLE. @@ -145,14 +171,4 @@ public TLEPropagator buildPropagator(final double[] normalizedParameters) { public TLE getTemplateTLE() { return templateTLE; } - - /** {@inheritDoc} */ - @Override - public AbstractBatchLSModel buildLeastSquaresModel(final PropagatorBuilder[] builders, - final List> measurements, - final ParameterDriversList estimatedMeasurementsParameters, - final ModelObserver observer) { - return new BatchLSModel(builders, measurements, estimatedMeasurementsParameters, observer); - } - } diff --git a/src/main/java/org/orekit/propagation/events/AbstractDetector.java b/src/main/java/org/orekit/propagation/events/AbstractDetector.java index b01e35f808..0a4c0e9a5a 100644 --- a/src/main/java/org/orekit/propagation/events/AbstractDetector.java +++ b/src/main/java/org/orekit/propagation/events/AbstractDetector.java @@ -30,22 +30,16 @@ public abstract class AbstractDetector> implements EventDetector { /** Default maximum checking interval (s). */ - public static final double DEFAULT_MAXCHECK = 600; + public static final double DEFAULT_MAXCHECK = EventDetectionSettings.DEFAULT_MAXCHECK; /** Default convergence threshold (s). */ - public static final double DEFAULT_THRESHOLD = 1.e-6; + public static final double DEFAULT_THRESHOLD = EventDetectionSettings.DEFAULT_THRESHOLD; /** Default maximum number of iterations in the event time search. */ - public static final int DEFAULT_MAX_ITER = 100; + public static final int DEFAULT_MAX_ITER = EventDetectionSettings.DEFAULT_MAX_ITER; - /** Max check interval. */ - private final AdaptableInterval maxCheck; - - /** Convergence threshold. */ - private final double threshold; - - /** Maximum number of iterations in the event time search. */ - private final int maxIter; + /** Detection settings. */ + private final EventDetectionSettings eventDetectionSettings; /** Default handler for event overrides. */ private final EventHandler handler; @@ -61,7 +55,7 @@ public abstract class AbstractDetector> implements */ protected AbstractDetector(final double maxCheck, final double threshold, final int maxIter, final EventHandler handler) { - this(AdaptableInterval.of(maxCheck), threshold, maxIter, handler); + this(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); } /** Build a new instance. @@ -70,13 +64,22 @@ protected AbstractDetector(final double maxCheck, final double threshold, final * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences * @since 12.0 + * @deprecated as of 12.2 */ + @Deprecated protected AbstractDetector(final AdaptableInterval maxCheck, final double threshold, final int maxIter, final EventHandler handler) { - checkStrictlyPositive(threshold); - this.maxCheck = maxCheck; - this.threshold = threshold; - this.maxIter = maxIter; + this(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); + } + + /** Build a new instance. + * @param detectionSettings event detection settings + * @param handler event handler to call at event occurrences + * @since 12.2 + */ + protected AbstractDetector(final EventDetectionSettings detectionSettings, final EventHandler handler) { + checkStrictlyPositive(detectionSettings.getThreshold()); + this.eventDetectionSettings = detectionSettings; this.handler = handler; this.forward = true; } @@ -99,6 +102,7 @@ private void checkStrictlyPositive(final double value) throws OrekitException { * handler. If a subclass overrides this method it should call {@code * super.init(s0, t)}. */ + @Override public void init(final SpacecraftState s0, final AbsoluteDate t) { forward = t.durationFrom(s0.getDate()) >= 0.0; @@ -106,25 +110,28 @@ public void init(final SpacecraftState s0, } /** {@inheritDoc} */ - public abstract double g(SpacecraftState s); + @Override + public EventDetectionSettings getDetectionSettings() { + return eventDetectionSettings; + } /** {@inheritDoc} */ public AdaptableInterval getMaxCheckInterval() { - return maxCheck; + return getDetectionSettings().getMaxCheckInterval(); } /** {@inheritDoc} */ public int getMaxIterationCount() { - return maxIter; + return getDetectionSettings().getMaxIterationCount(); } /** {@inheritDoc} */ public double getThreshold() { - return threshold; + return getDetectionSettings().getThreshold(); } /** - * Setup the maximum checking interval. + * Set up the maximum checking interval. *

      * This will override a maximum checking interval if it has been configured previously. *

      @@ -137,7 +144,7 @@ public T withMaxCheck(final double newMaxCheck) { } /** - * Setup the maximum checking interval. + * Set up the maximum checking interval. *

      * This will override a maximum checking interval if it has been configured previously. *

      @@ -146,11 +153,11 @@ public T withMaxCheck(final double newMaxCheck) { * @since 12.0 */ public T withMaxCheck(final AdaptableInterval newMaxCheck) { - return create(newMaxCheck, getThreshold(), getMaxIterationCount(), getHandler()); + return create(new EventDetectionSettings(newMaxCheck, getThreshold(), getMaxIterationCount()), getHandler()); } /** - * Setup the maximum number of iterations in the event time search. + * Set up the maximum number of iterations in the event time search. *

      * This will override a number of iterations if it has been configured previously. *

      @@ -159,11 +166,11 @@ public T withMaxCheck(final AdaptableInterval newMaxCheck) { * @since 6.1 */ public T withMaxIter(final int newMaxIter) { - return create(getMaxCheckInterval(), getThreshold(), newMaxIter, getHandler()); + return create(new EventDetectionSettings(getMaxCheckInterval(), getThreshold(), newMaxIter), getHandler()); } /** - * Setup the convergence threshold. + * Set up the convergence threshold. *

      * This will override a convergence threshold if it has been configured previously. *

      @@ -172,11 +179,25 @@ public T withMaxIter(final int newMaxIter) { * @since 6.1 */ public T withThreshold(final double newThreshold) { - return create(getMaxCheckInterval(), newThreshold, getMaxIterationCount(), getHandler()); + return create(new EventDetectionSettings(getMaxCheckInterval(), newThreshold, getMaxIterationCount()), getHandler()); } /** - * Setup the event handler to call at event occurrences. + * Set up the event detection settings. + *

      + * This will override settings previously configured. + *

      + * @param newSettings new event detection settings + * @return a new detector with updated configuration (the instance is not changed) + * @since 12.2 + */ + public T withDetectionSettings(final EventDetectionSettings newSettings) { + return create(new EventDetectionSettings(newSettings.getMaxCheckInterval(), newSettings.getThreshold(), newSettings.getMaxIterationCount()), + getHandler()); + } + + /** + * Set up the event handler to call at event occurrences. *

      * This will override a handler if it has been configured previously. *

      @@ -185,7 +206,7 @@ public T withThreshold(final double newThreshold) { * @since 6.1 */ public T withHandler(final EventHandler newHandler) { - return create(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), newHandler); + return create(getDetectionSettings(), newHandler); } /** {@inheritDoc} */ @@ -200,9 +221,22 @@ public EventHandler getHandler() { * @param newMaxIter maximum number of iterations in the event time search * @param newHandler event handler to call at event occurrences * @return a new instance of the appropriate sub-type + * @deprecated as of 12.2. Will be removed in 13.0 and only the other signature shall remain */ - protected abstract T create(AdaptableInterval newMaxCheck, double newThreshold, - int newMaxIter, EventHandler newHandler); + @Deprecated + protected abstract T create(AdaptableInterval newMaxCheck, double newThreshold, int newMaxIter, + EventHandler newHandler); + + /** Build a new instance. + * @param detectionSettings detection settings + * @param newHandler event handler to call at event occurrences + * @return a new instance of the appropriate sub-type + * @since 12.2 + */ + protected T create(final EventDetectionSettings detectionSettings, final EventHandler newHandler) { + return create(detectionSettings.getMaxCheckInterval(), detectionSettings.getThreshold(), detectionSettings.getMaxIterationCount(), + newHandler); + } /** Check if the current propagation is forward or backward. * @return true if the current propagation is forward diff --git a/src/main/java/org/orekit/propagation/events/AdapterDetector.java b/src/main/java/org/orekit/propagation/events/AdapterDetector.java index 8d52e0e803..26a5b6ab92 100644 --- a/src/main/java/org/orekit/propagation/events/AdapterDetector.java +++ b/src/main/java/org/orekit/propagation/events/AdapterDetector.java @@ -85,4 +85,15 @@ public EventHandler getHandler() { return detector.getHandler(); } + /** {@inheritDoc} */ + @Override + public void finish(final SpacecraftState state) { + detector.finish(state); + } + + /** {@inheritDoc} */ + @Override + public EventDetectionSettings getDetectionSettings() { + return detector.getDetectionSettings(); + } } diff --git a/src/main/java/org/orekit/propagation/events/AlignmentDetector.java b/src/main/java/org/orekit/propagation/events/AlignmentDetector.java index 6b5943e904..58d68437e9 100644 --- a/src/main/java/org/orekit/propagation/events/AlignmentDetector.java +++ b/src/main/java/org/orekit/propagation/events/AlignmentDetector.java @@ -110,7 +110,7 @@ protected AlignmentDetector(final AdaptableInterval maxCheck, final double thres final int maxIter, final EventHandler handler, final PVCoordinatesProvider body, final double alignAngle) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); final SinCos sc = FastMath.sinCos(alignAngle); this.body = body; this.alignAngle = alignAngle; diff --git a/src/main/java/org/orekit/propagation/events/AltitudeDetector.java b/src/main/java/org/orekit/propagation/events/AltitudeDetector.java index 2139489f7c..5634cd589b 100644 --- a/src/main/java/org/orekit/propagation/events/AltitudeDetector.java +++ b/src/main/java/org/orekit/propagation/events/AltitudeDetector.java @@ -107,7 +107,7 @@ protected AltitudeDetector(final AdaptableInterval maxCheck, final double thresh final int maxIter, final EventHandler handler, final double altitude, final BodyShape bodyShape) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.altitude = altitude; this.bodyShape = bodyShape; } diff --git a/src/main/java/org/orekit/propagation/events/AngularSeparationDetector.java b/src/main/java/org/orekit/propagation/events/AngularSeparationDetector.java index 229df646ad..eef09f8135 100644 --- a/src/main/java/org/orekit/propagation/events/AngularSeparationDetector.java +++ b/src/main/java/org/orekit/propagation/events/AngularSeparationDetector.java @@ -83,7 +83,7 @@ protected AngularSeparationDetector(final AdaptableInterval maxCheck, final doub final PVCoordinatesProvider beacon, final PVCoordinatesProvider observer, final double proximityAngle) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.beacon = beacon; this.observer = observer; this.proximityAngle = proximityAngle; diff --git a/src/main/java/org/orekit/propagation/events/AngularSeparationFromSatelliteDetector.java b/src/main/java/org/orekit/propagation/events/AngularSeparationFromSatelliteDetector.java index fd0469eace..0eed9637c2 100644 --- a/src/main/java/org/orekit/propagation/events/AngularSeparationFromSatelliteDetector.java +++ b/src/main/java/org/orekit/propagation/events/AngularSeparationFromSatelliteDetector.java @@ -83,7 +83,7 @@ protected AngularSeparationFromSatelliteDetector(final AdaptableInterval maxChec final PVCoordinatesProvider primaryObject, final PVCoordinatesProvider secondaryObject, final double proximityAngle) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.primaryObject = primaryObject; this.secondaryObject = secondaryObject; this.proximityAngle = proximityAngle; diff --git a/src/main/java/org/orekit/propagation/events/ApsideDetector.java b/src/main/java/org/orekit/propagation/events/ApsideDetector.java index 03046fb12e..00b0049198 100644 --- a/src/main/java/org/orekit/propagation/events/ApsideDetector.java +++ b/src/main/java/org/orekit/propagation/events/ApsideDetector.java @@ -82,7 +82,7 @@ public ApsideDetector(final double threshold, final Orbit orbit) { */ public ApsideDetector(final AdaptableInterval maxCheck, final double threshold, final int maxIter, final EventHandler handler) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); } /** {@inheritDoc} */ diff --git a/src/main/java/org/orekit/propagation/events/BetaAngleDetector.java b/src/main/java/org/orekit/propagation/events/BetaAngleDetector.java index a2b30793c2..6dfd320568 100644 --- a/src/main/java/org/orekit/propagation/events/BetaAngleDetector.java +++ b/src/main/java/org/orekit/propagation/events/BetaAngleDetector.java @@ -88,7 +88,7 @@ protected BetaAngleDetector(final AdaptableInterval maxCheck, final double thres final int maxIter, final EventHandler handler, final double betaAngleThreshold, final PVCoordinatesProvider celestialBodyProvider, final Frame inertialFrame) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.betaAngleThreshold = betaAngleThreshold; this.celestialBodyProvider = celestialBodyProvider; this.inertialFrame = inertialFrame; diff --git a/src/main/java/org/orekit/propagation/events/BooleanDetector.java b/src/main/java/org/orekit/propagation/events/BooleanDetector.java index 40c0b968d5..05965244c4 100644 --- a/src/main/java/org/orekit/propagation/events/BooleanDetector.java +++ b/src/main/java/org/orekit/propagation/events/BooleanDetector.java @@ -82,7 +82,7 @@ protected BooleanDetector(final List detectors, final double newThreshold, final int newMaxIter, final EventHandler newHandler) { - super(newMaxCheck, newThreshold, newMaxIter, newHandler); + super(new EventDetectionSettings(newMaxCheck, newThreshold, newMaxIter), newHandler); this.detectors = detectors; this.operator = operator; } diff --git a/src/main/java/org/orekit/propagation/events/CylindricalShadowEclipseDetector.java b/src/main/java/org/orekit/propagation/events/CylindricalShadowEclipseDetector.java index bf2f8f25bc..a0f3b981cd 100644 --- a/src/main/java/org/orekit/propagation/events/CylindricalShadowEclipseDetector.java +++ b/src/main/java/org/orekit/propagation/events/CylindricalShadowEclipseDetector.java @@ -41,6 +41,23 @@ public class CylindricalShadowEclipseDetector extends AbstractDetector implements Time * @since 12.0 */ public DateDetector(final TimeStamped... dates) { - this(AdaptableInterval.of(DEFAULT_MAX_CHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnEvent(), DEFAULT_MIN_GAP, dates); + this(new EventDetectionSettings(AdaptableInterval.of(DEFAULT_MAX_CHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER), new StopOnEvent(), DEFAULT_MIN_GAP, dates); } /** Protected constructor with full parameters. @@ -99,13 +99,32 @@ public DateDetector(final TimeStamped... dates) { * @param minGap minimum gap between added dates (s) * @param dates list of event dates * @since 12.0 + * @deprecated as of 12.2 */ + @Deprecated protected DateDetector(final AdaptableInterval maxCheck, final double threshold, final int maxIter, final EventHandler handler, final double minGap, final TimeStamped... dates) { - super(maxCheck, threshold, maxIter, handler); + this(new EventDetectionSettings(maxCheck, threshold, maxIter), handler, minGap, dates); + } + + /** Protected constructor with full parameters. + *

      + * This constructor is not public as users are expected to use the builder + * API with the various {@code withXxx()} methods to set up the instance + * in a readable manner without using a huge amount of parameters. + *

      + * @param detectionSettings detection settings + * @param handler event handler to call at event occurrences + * @param minGap minimum gap between added dates (s) + * @param dates list of event dates + * @since 12.2 + */ + protected DateDetector(final EventDetectionSettings detectionSettings, + final EventHandler handler, final double minGap, final TimeStamped... dates) { + super(detectionSettings, handler); this.currentIndex = -1; this.gDate = null; - this.eventDateList = new ArrayList(dates.length); + this.eventDateList = new ArrayList<>(dates.length); for (final TimeStamped ts : dates) { addEventDate(ts.getDate()); } @@ -119,8 +138,7 @@ protected DateDetector(final AdaptableInterval maxCheck, final double threshold, * @since 12.0 */ public DateDetector withMinGap(final double newMinGap) { - return new DateDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), - getHandler(), newMinGap, + return new DateDetector(getDetectionSettings(), getHandler(), newMinGap, eventDateList.toArray(new EventDate[eventDateList.size()])); } @@ -128,7 +146,7 @@ public DateDetector withMinGap(final double newMinGap) { @Override protected DateDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, final int newMaxIter, final EventHandler newHandler) { - return new DateDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, minGap, + return new DateDetector(new EventDetectionSettings(newMaxCheck, newThreshold, newMaxIter), newHandler, minGap, eventDateList.toArray(new EventDate[eventDateList.size()])); } diff --git a/src/main/java/org/orekit/propagation/events/EclipseDetector.java b/src/main/java/org/orekit/propagation/events/EclipseDetector.java index 61d8a00d69..dbb9d4db0d 100644 --- a/src/main/java/org/orekit/propagation/events/EclipseDetector.java +++ b/src/main/java/org/orekit/propagation/events/EclipseDetector.java @@ -83,7 +83,7 @@ public EclipseDetector(final ExtendedPVCoordinatesProvider occulted, final doub * @since 12.0 */ public EclipseDetector(final OccultationEngine occultationEngine) { - this(AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + this(new EventDetectionSettings(AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER), new StopOnIncreasing(), occultationEngine, 0.0, true); } @@ -102,11 +102,31 @@ public EclipseDetector(final OccultationEngine occultationEngine) { * @param margin to apply to eclipse angle (rad) * @param totalEclipse umbra (true) or penumbra (false) detection flag * @since 12.0 + * @deprecated as of 12.2 */ + @Deprecated protected EclipseDetector(final AdaptableInterval maxCheck, final double threshold, final int maxIter, final EventHandler handler, final OccultationEngine occultationEngine, final double margin, final boolean totalEclipse) { - super(maxCheck, threshold, maxIter, handler); + this(new EventDetectionSettings(maxCheck, threshold, maxIter), handler, occultationEngine, margin, totalEclipse); + } + + /** Protected constructor with full parameters. + *

      + * This constructor is not public as users are expected to use the builder + * API with the various {@code withXxx()} methods to set up the instance + * in a readable manner without using a huge amount of parameters. + *

      + * @param detectionSettings detection settings + * @param handler event handler to call at event occurrences + * @param occultationEngine occultation engine + * @param margin to apply to eclipse angle (rad) + * @param totalEclipse umbra (true) or penumbra (false) detection flag + * @since 12.2 + */ + protected EclipseDetector(final EventDetectionSettings detectionSettings, final EventHandler handler, + final OccultationEngine occultationEngine, final double margin, final boolean totalEclipse) { + super(detectionSettings, handler); this.occultationEngine = occultationEngine; this.margin = margin; this.totalEclipse = totalEclipse; @@ -130,8 +150,7 @@ protected EclipseDetector create(final AdaptableInterval newMaxCheck, final doub * @since 6.1 */ public EclipseDetector withUmbra() { - return new EclipseDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(), - occultationEngine, margin, true); + return new EclipseDetector(getDetectionSettings(), getHandler(), occultationEngine, margin, true); } /** @@ -144,8 +163,7 @@ public EclipseDetector withUmbra() { * @since 6.1 */ public EclipseDetector withPenumbra() { - return new EclipseDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(), - occultationEngine, margin, false); + return new EclipseDetector(getDetectionSettings(), getHandler(), occultationEngine, margin, false); } /** @@ -159,8 +177,7 @@ public EclipseDetector withPenumbra() { * @since 12.0 */ public EclipseDetector withMargin(final double newMargin) { - return new EclipseDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(), - occultationEngine, newMargin, totalEclipse); + return new EclipseDetector(getDetectionSettings(), getHandler(), occultationEngine, newMargin, totalEclipse); } /** Get the angular margin used for eclipse detection. diff --git a/src/main/java/org/orekit/propagation/events/ElevationDetector.java b/src/main/java/org/orekit/propagation/events/ElevationDetector.java index fdee7fd043..8985afb3e8 100644 --- a/src/main/java/org/orekit/propagation/events/ElevationDetector.java +++ b/src/main/java/org/orekit/propagation/events/ElevationDetector.java @@ -121,7 +121,7 @@ protected ElevationDetector(final AdaptableInterval maxCheck, final double thres final double minElevation, final ElevationMask mask, final AtmosphericRefractionModel refractionModel, final TopocentricFrame topo) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.minElevation = minElevation; this.elevationMask = mask; this.refractionModel = refractionModel; diff --git a/src/main/java/org/orekit/propagation/events/ElevationExtremumDetector.java b/src/main/java/org/orekit/propagation/events/ElevationExtremumDetector.java index 45df8b362c..47b2e191c2 100644 --- a/src/main/java/org/orekit/propagation/events/ElevationExtremumDetector.java +++ b/src/main/java/org/orekit/propagation/events/ElevationExtremumDetector.java @@ -82,7 +82,7 @@ public ElevationExtremumDetector(final double maxCheck, final double threshold, protected ElevationExtremumDetector(final AdaptableInterval maxCheck, final double threshold, final int maxIter, final EventHandler handler, final TopocentricFrame topo) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.topo = topo; } diff --git a/src/main/java/org/orekit/propagation/events/EventDetectionSettings.java b/src/main/java/org/orekit/propagation/events/EventDetectionSettings.java new file mode 100644 index 0000000000..30c86a9971 --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/EventDetectionSettings.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.propagation.events; + +/** + * Class containing parameters for event detection. + * + * @author Romain Serra. + * @since 12.2 + * @see EventDetector + */ +public class EventDetectionSettings { + + /** Default maximum checking interval (s). */ + public static final double DEFAULT_MAXCHECK = 600; + + /** Default convergence threshold (s). */ + public static final double DEFAULT_THRESHOLD = 1.e-6; + + /** Default maximum number of iterations in the event time search. */ + public static final int DEFAULT_MAX_ITER = 100; + + /** Adaptable interval for maximum time without event evaluation. */ + private final AdaptableInterval maxCheckInterval; + + /** Detection threshold. */ + private final double threshold; + + /** Maximum iteration number when detecting event. */ + private final int maxIterationCount; + + /** + * Constructor. + * + * @param maxCheckInterval adaptable interval + * @param threshold detection threshold on time + * @param maxIterationCount maximum iteration number + */ + public EventDetectionSettings(final AdaptableInterval maxCheckInterval, final double threshold, + final int maxIterationCount) { + this.maxCheckInterval = maxCheckInterval; + this.maxIterationCount = maxIterationCount; + this.threshold = threshold; + } + + /** + * Constructor with maximum check as double. + * + * @param maxCheck constant maximum check for adaptable interval + * @param threshold detection threshold on time + * @param maxIterationCount maximum iteration number + */ + public EventDetectionSettings(final double maxCheck, final double threshold, final int maxIterationCount) { + this(AdaptableInterval.of(maxCheck), threshold, maxIterationCount); + } + + /** + * Getter for adaptable interval. + * @return adaptable interval + */ + public AdaptableInterval getMaxCheckInterval() { + return maxCheckInterval; + } + + /** + * Getter for threshold. + * @return threshold + */ + public double getThreshold() { + return threshold; + } + + /** + * Getter for max iter. + * @return max iter + */ + public int getMaxIterationCount() { + return maxIterationCount; + } + + /** + * Returns default settings for event detections. + * @return default settings + */ + public static EventDetectionSettings getDefaultEventDetectionSettings() { + return new EventDetectionSettings(DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER); + } +} diff --git a/src/main/java/org/orekit/propagation/events/EventDetector.java b/src/main/java/org/orekit/propagation/events/EventDetector.java index d888786d69..6987368355 100644 --- a/src/main/java/org/orekit/propagation/events/EventDetector.java +++ b/src/main/java/org/orekit/propagation/events/EventDetector.java @@ -109,4 +109,21 @@ default void init(SpacecraftState s0, AbsoluteDate t) { */ EventHandler getHandler(); + /** + * This method finalizes the event detector's job. + * @param state state at propagation end + * @since 12.2 + */ + default void finish(SpacecraftState state) { + getHandler().finish(state, this); + } + + /** + * Getter for the settings. + * @return detection settings + * @since 12.2 + */ + default EventDetectionSettings getDetectionSettings() { + return new EventDetectionSettings(getMaxCheckInterval(), getThreshold(), getMaxIterationCount()); + } } diff --git a/src/main/java/org/orekit/propagation/events/EventDetectorsProvider.java b/src/main/java/org/orekit/propagation/events/EventDetectorsProvider.java index 98ca03aab8..f94c6be36f 100644 --- a/src/main/java/org/orekit/propagation/events/EventDetectorsProvider.java +++ b/src/main/java/org/orekit/propagation/events/EventDetectorsProvider.java @@ -23,9 +23,10 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; -import org.hipparchus.ode.events.Action; import org.hipparchus.util.FastMath; import org.orekit.forces.ForceModel; +import org.orekit.propagation.events.handlers.FieldResetDerivativesOnEvent; +import org.orekit.propagation.events.handlers.ResetDerivativesOnEvent; import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; @@ -110,9 +111,7 @@ default Stream getEventDetectors(List parameterD withMaxCheck(0.5 * shortestDuration). withMinGap(0.5 * shortestDuration). withThreshold(DATATION_ACCURACY). - withHandler((state, d, increasing) -> { - return Action.RESET_DERIVATIVES; - }); + withHandler(new ResetDerivativesOnEvent()); return Stream.of(datesDetector); } } @@ -163,9 +162,7 @@ default > Stream> getFie withMaxCheck(0.5 * shortestDuration). withMinGap(0.5 * shortestDuration). withThreshold(field.getZero().newInstance(DATATION_ACCURACY)). - withHandler(( state, d, increasing) -> { - return Action.RESET_DERIVATIVES; - }); + withHandler(new FieldResetDerivativesOnEvent<>()); // Add all transitions' dates to the date detector for (int i = 0; i < transitionDates.size(); i++) { datesDetector.addEventDate(new FieldAbsoluteDate<>(field, transitionDates.get(i))); diff --git a/src/main/java/org/orekit/propagation/events/EventEnablingPredicateFilter.java b/src/main/java/org/orekit/propagation/events/EventEnablingPredicateFilter.java index c7e09c9793..5432e651b9 100644 --- a/src/main/java/org/orekit/propagation/events/EventEnablingPredicateFilter.java +++ b/src/main/java/org/orekit/propagation/events/EventEnablingPredicateFilter.java @@ -111,7 +111,7 @@ protected EventEnablingPredicateFilter(final AdaptableInterval maxCheck, final d final int maxIter, final EventHandler handler, final EventDetector rawDetector, final EnablingPredicate enabler) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.rawDetector = rawDetector; this.enabler = enabler; this.transformers = new Transformer[HISTORY_SIZE]; @@ -136,6 +136,7 @@ public EventDetector getDetector() { } /** {@inheritDoc} */ + @Override public void init(final SpacecraftState s0, final AbsoluteDate t) { super.init(s0, t); diff --git a/src/main/java/org/orekit/propagation/events/EventShifter.java b/src/main/java/org/orekit/propagation/events/EventShifter.java index 25f1df8d73..b060683cb7 100644 --- a/src/main/java/org/orekit/propagation/events/EventShifter.java +++ b/src/main/java/org/orekit/propagation/events/EventShifter.java @@ -96,7 +96,7 @@ protected EventShifter(final AdaptableInterval maxCheck, final double threshold, final int maxIter, final EventHandler handler, final EventDetector detector, final boolean useShiftedStates, final double increasingTimeShift, final double decreasingTimeShift) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.detector = detector; this.useShiftedStates = useShiftedStates; this.increasingOffset = -increasingTimeShift; diff --git a/src/main/java/org/orekit/propagation/events/EventSlopeFilter.java b/src/main/java/org/orekit/propagation/events/EventSlopeFilter.java index 36e8da6e0d..82b499c166 100644 --- a/src/main/java/org/orekit/propagation/events/EventSlopeFilter.java +++ b/src/main/java/org/orekit/propagation/events/EventSlopeFilter.java @@ -112,7 +112,7 @@ public EventSlopeFilter(final T rawDetector, final FilterType filter) { protected EventSlopeFilter(final AdaptableInterval maxCheck, final double threshold, final int maxIter, final EventHandler handler, final T rawDetector, final FilterType filter) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.rawDetector = rawDetector; this.filter = filter; this.transformers = new Transformer[HISTORY_SIZE]; @@ -123,7 +123,7 @@ protected EventSlopeFilter(final AdaptableInterval maxCheck, final double thresh @Override protected EventSlopeFilter create(final AdaptableInterval newMaxCheck, final double newThreshold, final int newMaxIter, final EventHandler newHandler) { - return new EventSlopeFilter(newMaxCheck, newThreshold, newMaxIter, newHandler, rawDetector, filter); + return new EventSlopeFilter<>(newMaxCheck, newThreshold, newMaxIter, newHandler, rawDetector, filter); } /** diff --git a/src/main/java/org/orekit/propagation/events/EventState.java b/src/main/java/org/orekit/propagation/events/EventState.java index 09f8f9796f..9b3b5882f1 100644 --- a/src/main/java/org/orekit/propagation/events/EventState.java +++ b/src/main/java/org/orekit/propagation/events/EventState.java @@ -617,6 +617,15 @@ private void check(final boolean condition) throws MathRuntimeException { } } + /** + * This method finalizes the event detector's job. + * @param state state at propagation end + * @since 12.2 + */ + public void finish(final SpacecraftState state) { + detector.finish(state); + } + /** * Class to hold the data related to an event occurrence that is needed to decide how * to modify integration. diff --git a/src/main/java/org/orekit/propagation/events/EventsLogger.java b/src/main/java/org/orekit/propagation/events/EventsLogger.java index 3c83cc2402..35c58afd88 100644 --- a/src/main/java/org/orekit/propagation/events/EventsLogger.java +++ b/src/main/java/org/orekit/propagation/events/EventsLogger.java @@ -55,7 +55,7 @@ public class EventsLogger { *

      */ public EventsLogger() { - log = new ArrayList(); + log = new ArrayList<>(); } /** Monitor an event detector. @@ -99,7 +99,7 @@ public void clearLoggedEvents() { * @return an immutable copy of the logged events */ public List getLoggedEvents() { - return new ArrayList(log); + return new ArrayList<>(log); } /** Class for logged events entries. */ @@ -167,9 +167,7 @@ private class LoggingWrapper extends AbstractDetector { * @param detector events detector to wrap */ LoggingWrapper(final EventDetector detector) { - this(detector.getMaxCheckInterval(), detector.getThreshold(), - detector.getMaxIterationCount(), null, - detector); + this(detector.getDetectionSettings(), null, detector); } /** Private constructor with full parameters. @@ -178,17 +176,14 @@ private class LoggingWrapper extends AbstractDetector { * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

      - * @param maxCheck maximum checking interval - * @param threshold convergence threshold (s) - * @param maxIter maximum number of iterations in the event time search + * @param detectionSettings detection settings * @param handler event handler to call at event occurrences * @param detector events detector to wrap * @since 6.1 */ - private LoggingWrapper(final AdaptableInterval maxCheck, final double threshold, - final int maxIter, final EventHandler handler, + private LoggingWrapper(final EventDetectionSettings detectionSettings, final EventHandler handler, final EventDetector detector) { - super(maxCheck, threshold, maxIter, handler); + super(detectionSettings, handler); this.detector = detector; } @@ -196,7 +191,7 @@ private LoggingWrapper(final AdaptableInterval maxCheck, final double threshold, @Override protected LoggingWrapper create(final AdaptableInterval newMaxCheck, final double newThreshold, final int newMaxIter, final EventHandler newHandler) { - return new LoggingWrapper(newMaxCheck, newThreshold, newMaxIter, newHandler, detector); + return new LoggingWrapper(new EventDetectionSettings(newMaxCheck, newThreshold, newMaxIter), newHandler, detector); } /** Log an event. @@ -208,6 +203,7 @@ public void logEvent(final SpacecraftState state, final boolean increasing) { } /** {@inheritDoc} */ + @Override public void init(final SpacecraftState s0, final AbsoluteDate t) { super.init(s0, t); @@ -220,6 +216,13 @@ public double g(final SpacecraftState s) { } /** {@inheritDoc} */ + @Override + public void finish(final SpacecraftState state) { + detector.finish(state); + } + + /** {@inheritDoc} */ + @Override public EventHandler getHandler() { final EventHandler handler = detector.getHandler(); diff --git a/src/main/java/org/orekit/propagation/events/ExtremumApproachDetector.java b/src/main/java/org/orekit/propagation/events/ExtremumApproachDetector.java index 6f28fe63a4..110564a8d9 100644 --- a/src/main/java/org/orekit/propagation/events/ExtremumApproachDetector.java +++ b/src/main/java/org/orekit/propagation/events/ExtremumApproachDetector.java @@ -89,7 +89,7 @@ public class ExtremumApproachDetector extends AbstractDetector + * This constructor is to be used if the user wants to change the default behavior of the detector. + *

      + * + * @param detectionSettings Detection settings. + * @param handler Event handler to call at event occurrences. + * @param secondaryPVProvider PVCoordinates provider of the other object with which we want to find out the extremum + * approach. + * @see EventHandler + * @since 12.2 + */ + protected ExtremumApproachDetector(final EventDetectionSettings detectionSettings, + final EventHandler handler, final PVCoordinatesProvider secondaryPVProvider) { + super(detectionSettings, handler); this.secondaryPVProvider = secondaryPVProvider; } diff --git a/src/main/java/org/orekit/propagation/events/FieldAbstractDetector.java b/src/main/java/org/orekit/propagation/events/FieldAbstractDetector.java index 3ca0de65d7..ea3833ddaf 100644 --- a/src/main/java/org/orekit/propagation/events/FieldAbstractDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldAbstractDetector.java @@ -33,22 +33,16 @@ public abstract class FieldAbstractDetector { /** Default maximum checking interval (s). */ - public static final double DEFAULT_MAXCHECK = 600; + public static final double DEFAULT_MAXCHECK = FieldEventDetectionSettings.DEFAULT_MAXCHECK; /** Default convergence threshold (s). */ - public static final double DEFAULT_THRESHOLD = 1.e-6; + public static final double DEFAULT_THRESHOLD = FieldEventDetectionSettings.DEFAULT_THRESHOLD; - /** Default cmaximum number of iterations in the event time search. */ - public static final int DEFAULT_MAX_ITER = 100; + /** Default maximum number of iterations in the event time search. */ + public static final int DEFAULT_MAX_ITER = FieldEventDetectionSettings.DEFAULT_MAX_ITER; - /** Max check interval. */ - private final FieldAdaptableInterval maxCheck; - - /** Convergence threshold. */ - private final T threshold; - - /** Maximum number of iterations in the event time search. */ - private final int maxIter; + /** Detection settings. */ + private final FieldEventDetectionSettings eventDetectionSettings; /** Default handler for event overrides. */ private final FieldEventHandler handler; @@ -61,13 +55,23 @@ public abstract class FieldAbstractDetector maxCheck, final T threshold, final int maxIter, final FieldEventHandler handler) { - checkStrictlyPositive(threshold.getReal()); - this.maxCheck = maxCheck; - this.threshold = threshold; - this.maxIter = maxIter; + this(new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler); + } + + /** Build a new instance. + * @param detectionSettings event detection settings + * @param handler event handler to call at event occurrences + * @since 12.2 + */ + protected FieldAbstractDetector(final FieldEventDetectionSettings detectionSettings, + final FieldEventHandler handler) { + checkStrictlyPositive(detectionSettings.getThreshold().getReal()); + this.eventDetectionSettings = detectionSettings; this.handler = handler; this.forward = true; } @@ -84,6 +88,7 @@ private void checkStrictlyPositive(final double value) throws OrekitException { } /** {@inheritDoc} */ + @Override public void init(final FieldSpacecraftState s0, final FieldAbsoluteDate t) { forward = t.durationFrom(s0.getDate()).getReal() >= 0.0; @@ -91,25 +96,28 @@ public void init(final FieldSpacecraftState s0, } /** {@inheritDoc} */ - public abstract T g(FieldSpacecraftState s); + @Override + public FieldEventDetectionSettings getDetectionSettings() { + return eventDetectionSettings; + } /** {@inheritDoc} */ public FieldAdaptableInterval getMaxCheckInterval() { - return maxCheck; + return getDetectionSettings().getMaxCheckInterval(); } /** {@inheritDoc} */ public int getMaxIterationCount() { - return maxIter; + return getDetectionSettings().getMaxIterationCount(); } /** {@inheritDoc} */ public T getThreshold() { - return threshold; + return getDetectionSettings().getThreshold(); } /** - * Setup the maximum checking interval. + * Set up the maximum checking interval. *

      * This will override a maximum checking interval if it has been configured previously. *

      @@ -122,7 +130,7 @@ public D withMaxCheck(final double newMaxCheck) { } /** - * Setup the maximum checking interval. + * Set up the maximum checking interval. *

      * This will override a maximum checking interval if it has been configured previously. *

      @@ -131,11 +139,11 @@ public D withMaxCheck(final double newMaxCheck) { * @since 12.0 */ public D withMaxCheck(final FieldAdaptableInterval newMaxCheck) { - return create(newMaxCheck, getThreshold(), getMaxIterationCount(), getHandler()); + return create(new FieldEventDetectionSettings<>(newMaxCheck, getThreshold(), getMaxIterationCount()), getHandler()); } /** - * Setup the maximum number of iterations in the event time search. + * Set up the maximum number of iterations in the event time search. *

      * This will override a number of iterations if it has been configured previously. *

      @@ -144,11 +152,11 @@ public D withMaxCheck(final FieldAdaptableInterval newMaxCheck) { * @since 6.1 */ public D withMaxIter(final int newMaxIter) { - return create(getMaxCheckInterval(), getThreshold(), newMaxIter, getHandler()); + return create(new FieldEventDetectionSettings<>(getMaxCheckInterval(), getThreshold(), newMaxIter), getHandler()); } /** - * Setup the convergence threshold. + * Set up the convergence threshold. *

      * This will override a convergence threshold if it has been configured previously. *

      @@ -157,11 +165,24 @@ public D withMaxIter(final int newMaxIter) { * @since 6.1 */ public D withThreshold(final T newThreshold) { - return create(getMaxCheckInterval(), newThreshold, getMaxIterationCount(), getHandler()); + return create(new FieldEventDetectionSettings<>(getMaxCheckInterval(), newThreshold, getMaxIterationCount()), getHandler()); + } + + /** + * Set up the event detection settings. + *

      + * This will override settings previously configured. + *

      + * @param newSettings new event detection settings + * @return a new detector with updated configuration (the instance is not changed) + * @since 12.2 + */ + public D withDetectionSettings(final FieldEventDetectionSettings newSettings) { + return create(newSettings, getHandler()); } /** - * Setup the event handler to call at event occurrences. + * Set up the event handler to call at event occurrences. *

      * This will override a handler if it has been configured previously. *

      @@ -170,7 +191,7 @@ public D withThreshold(final T newThreshold) { * @since 6.1 */ public D withHandler(final FieldEventHandler newHandler) { - return create(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), newHandler); + return create(getDetectionSettings(), newHandler); } /** {@inheritDoc} */ @@ -184,10 +205,23 @@ public FieldEventHandler getHandler() { * @param newMaxIter maximum number of iterations in the event time search * @param newHandler event handler to call at event occurrences * @return a new instance of the appropriate sub-type + * @deprecated as of 12.2 */ + @Deprecated protected abstract D create(FieldAdaptableInterval newMaxCheck, T newThreshold, int newMaxIter, FieldEventHandler newHandler); + /** Build a new instance. + * @param detectionSettings detection settings + * @param newHandler event handler to call at event occurrences + * @return a new instance of the appropriate sub-type + * @since 12.2 + */ + protected D create(final FieldEventDetectionSettings detectionSettings, final FieldEventHandler newHandler) { + return create(detectionSettings.getMaxCheckInterval(), detectionSettings.getThreshold(), + detectionSettings.getMaxIterationCount(), newHandler); + } + /** Check if the current propagation is forward or backward. * @return true if the current propagation is forward * @since 7.2 diff --git a/src/main/java/org/orekit/propagation/events/FieldAdapterDetector.java b/src/main/java/org/orekit/propagation/events/FieldAdapterDetector.java index 2679af9d11..92b0f1c929 100644 --- a/src/main/java/org/orekit/propagation/events/FieldAdapterDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldAdapterDetector.java @@ -87,4 +87,15 @@ public FieldEventHandler getHandler() { return detector.getHandler(); } + /** {@inheritDoc} */ + @Override + public void finish(final FieldSpacecraftState state) { + detector.finish(state); + } + + /** {@inheritDoc} */ + @Override + public FieldEventDetectionSettings getDetectionSettings() { + return detector.getDetectionSettings(); + } } diff --git a/src/main/java/org/orekit/propagation/events/FieldAltitudeDetector.java b/src/main/java/org/orekit/propagation/events/FieldAltitudeDetector.java index 08523ca909..b9b961b3ab 100644 --- a/src/main/java/org/orekit/propagation/events/FieldAltitudeDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldAltitudeDetector.java @@ -112,7 +112,7 @@ protected FieldAltitudeDetector(final FieldAdaptableInterval maxCheck, final final int maxIter, final FieldEventHandler handler, final T altitude, final BodyShape bodyShape) { - super(maxCheck, threshold, maxIter, handler); + super(new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler); this.altitude = altitude; this.bodyShape = bodyShape; } diff --git a/src/main/java/org/orekit/propagation/events/FieldApsideDetector.java b/src/main/java/org/orekit/propagation/events/FieldApsideDetector.java index 858f6e6513..66b6e8f204 100644 --- a/src/main/java/org/orekit/propagation/events/FieldApsideDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldApsideDetector.java @@ -49,8 +49,8 @@ public class FieldApsideDetector> extends Fiel * @since 12.1 */ public FieldApsideDetector(final T keplerianPeriod) { - super(FieldAdaptableInterval.of(keplerianPeriod.divide(3).getReal()), keplerianPeriod.multiply(1e-13), - DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>()); + super(new FieldEventDetectionSettings<>(keplerianPeriod.divide(3).getReal(), keplerianPeriod.multiply(1e-13), + DEFAULT_MAX_ITER), new FieldStopOnIncreasing<>()); } /** Build a new instance. @@ -70,8 +70,8 @@ public FieldApsideDetector(final FieldOrbit orbit) { * @param orbit initial orbit */ public FieldApsideDetector(final T threshold, final FieldOrbit orbit) { - super(FieldAdaptableInterval.of(orbit.getKeplerianPeriod().divide(3).getReal()), threshold, - DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>()); + super(new FieldEventDetectionSettings<>(orbit.getKeplerianPeriod().divide(3).getReal(), threshold, + DEFAULT_MAX_ITER), new FieldStopOnIncreasing<>()); } /** Protected constructor with full parameters. @@ -85,7 +85,7 @@ public FieldApsideDetector(final T threshold, final FieldOrbit orbit) { */ public FieldApsideDetector(final FieldAdaptableInterval maxCheck, final T threshold, final int maxIter, final FieldEventHandler handler) { - super(maxCheck, threshold, maxIter, handler); + super(new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler); } /** {@inheritDoc} */ diff --git a/src/main/java/org/orekit/propagation/events/FieldBetaAngleDetector.java b/src/main/java/org/orekit/propagation/events/FieldBetaAngleDetector.java index 29210aa352..d3672f31f6 100644 --- a/src/main/java/org/orekit/propagation/events/FieldBetaAngleDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldBetaAngleDetector.java @@ -95,7 +95,7 @@ protected FieldBetaAngleDetector(final FieldAdaptableInterval maxCheck, final final int maxIter, final FieldEventHandler handler, final T betaAngleThreshold, final FieldPVCoordinatesProvider celestialBodyProvider, final Frame inertialFrame) { - super(maxCheck, threshold, maxIter, handler); + super(new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler); this.betaAngleThreshold = betaAngleThreshold; this.celestialBodyProvider = celestialBodyProvider; this.inertialFrame = inertialFrame; diff --git a/src/main/java/org/orekit/propagation/events/FieldBooleanDetector.java b/src/main/java/org/orekit/propagation/events/FieldBooleanDetector.java index 8ee9f9bc69..d1127c528e 100644 --- a/src/main/java/org/orekit/propagation/events/FieldBooleanDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldBooleanDetector.java @@ -17,6 +17,7 @@ package org.orekit.propagation.events; +import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -86,7 +87,7 @@ protected FieldBooleanDetector(final List> detectors, final T newThreshold, final int newMaxIter, final FieldEventHandler newHandler) { - super(newMaxCheck, newThreshold, newMaxIter, newHandler); + super(new FieldEventDetectionSettings<>(newMaxCheck, newThreshold, newMaxIter), newHandler); this.detectors = detectors; this.operator = operator; } @@ -311,12 +312,12 @@ public > T combine(final T g1, final T g2) { */ public abstract > T combine(T g1, T g2); - }; + } /** Comparator for field elements. * @param type of the field elements */ - private static class FieldComparator> implements Comparator { + private static class FieldComparator> implements Comparator, Serializable { public int compare(final T t1, final T t2) { return Double.compare(t1.getReal(), t2.getReal()); } diff --git a/src/main/java/org/orekit/propagation/events/FieldCylindricalShadowEclipseDetector.java b/src/main/java/org/orekit/propagation/events/FieldCylindricalShadowEclipseDetector.java index db206828c7..56bb3de261 100644 --- a/src/main/java/org/orekit/propagation/events/FieldCylindricalShadowEclipseDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldCylindricalShadowEclipseDetector.java @@ -32,7 +32,8 @@ * @author Romain Serra * @see FieldEclipseDetector * @see CylindricalShadowEclipseDetector - * @since 12.1 + * @since 12. + * */ public class FieldCylindricalShadowEclipseDetector> extends FieldAbstractDetector, T> { @@ -44,6 +45,23 @@ public class FieldCylindricalShadowEclipseDetector eventDetectionSettings, + final FieldEventHandler handler) { + super(eventDetectionSettings, handler); + this.sun = sun; + this.occultingBodyRadius = FastMath.abs(occultingBodyRadius); + } + /** * Constructor. * @param sun light source provider (infinitely distant) @@ -52,14 +70,14 @@ public class FieldCylindricalShadowEclipseDetector maxCheck, final T threshold, final int maxIter, final FieldEventHandler handler) { - super(maxCheck, threshold, maxIter, handler); - this.sun = sun; - this.occultingBodyRadius = FastMath.abs(occultingBodyRadius); + this(sun, occultingBodyRadius, new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler); } /** @@ -70,8 +88,8 @@ public FieldCylindricalShadowEclipseDetector(final ExtendedPositionProvider sun, */ public FieldCylindricalShadowEclipseDetector(final ExtendedPositionProvider sun, final T occultingBodyRadius, final FieldEventHandler handler) { - this(sun, occultingBodyRadius, FieldAdaptableInterval.of(DEFAULT_MAXCHECK), occultingBodyRadius.getField().getZero().newInstance(DEFAULT_THRESHOLD), - DEFAULT_MAX_ITER, handler); + this(sun, occultingBodyRadius, new FieldEventDetectionSettings<>(occultingBodyRadius.getField(), + EventDetectionSettings.getDefaultEventDetectionSettings()), handler); } /** @@ -100,6 +118,7 @@ public T g(final FieldSpacecraftState s) { @Override protected FieldCylindricalShadowEclipseDetector create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, final int newMaxIter, final FieldEventHandler newHandler) { - return new FieldCylindricalShadowEclipseDetector<>(sun, occultingBodyRadius, newMaxCheck, newThreshold, newMaxIter, newHandler); + return new FieldCylindricalShadowEclipseDetector<>(sun, occultingBodyRadius, + new FieldEventDetectionSettings<>(newMaxCheck, newThreshold, newMaxIter), newHandler); } } diff --git a/src/main/java/org/orekit/propagation/events/FieldDateDetector.java b/src/main/java/org/orekit/propagation/events/FieldDateDetector.java index 5e01b35ee0..7d7759e2b7 100644 --- a/src/main/java/org/orekit/propagation/events/FieldDateDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldDateDetector.java @@ -110,10 +110,10 @@ public FieldDateDetector(final Field field, final FieldTimeStamped... date protected FieldDateDetector(final FieldAdaptableInterval maxCheck, final T threshold, final int maxIter, final FieldEventHandler handler, final double minGap, final FieldTimeStamped... dates) { - super(maxCheck, threshold, maxIter, handler); + super(new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler); this.currentIndex = -1; this.gDate = null; - this.eventDateList = new ArrayList>(dates.length); + this.eventDateList = new ArrayList<>(dates.length); for (final FieldTimeStamped ts : dates) { addEventDate(ts.getDate()); } diff --git a/src/main/java/org/orekit/propagation/events/FieldEclipseDetector.java b/src/main/java/org/orekit/propagation/events/FieldEclipseDetector.java index 9aaf3bcd10..e7c1c431a1 100644 --- a/src/main/java/org/orekit/propagation/events/FieldEclipseDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldEclipseDetector.java @@ -98,7 +98,7 @@ public FieldEclipseDetector(final Field field, final OccultationEngine occult protected FieldEclipseDetector(final FieldAdaptableInterval maxCheck, final T threshold, final int maxIter, final FieldEventHandler handler, final OccultationEngine occultationEngine, final T margin, final boolean totalEclipse) { - super(maxCheck, threshold, maxIter, handler); + super(new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler); this.occultationEngine = occultationEngine; this.margin = margin; this.totalEclipse = totalEclipse; diff --git a/src/main/java/org/orekit/propagation/events/FieldElevationDetector.java b/src/main/java/org/orekit/propagation/events/FieldElevationDetector.java index f3f1bccba4..264c9e0240 100644 --- a/src/main/java/org/orekit/propagation/events/FieldElevationDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldElevationDetector.java @@ -97,27 +97,49 @@ public FieldElevationDetector(final T maxCheck, final T threshold, final Topocen * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

      - * @param maxCheck maximum checking interval - * @param threshold convergence threshold (s) - * @param maxIter maximum number of iterations in the event time search + * @param detectionSettings detection settings * @param handler event handler to call at event occurrences * @param minElevation minimum elevation in radians (rad) * @param mask reference to elevation mask * @param refractionModel reference to refraction model * @param topo reference to a topocentric model + * @since 12.2 */ - protected FieldElevationDetector(final FieldAdaptableInterval maxCheck, final T threshold, - final int maxIter, final FieldEventHandler handler, + protected FieldElevationDetector(final FieldEventDetectionSettings detectionSettings, final FieldEventHandler handler, final double minElevation, final ElevationMask mask, final AtmosphericRefractionModel refractionModel, - final TopocentricFrame topo) { - super(maxCheck, threshold, maxIter, handler); + final TopocentricFrame topo) { + super(detectionSettings, handler); this.minElevation = minElevation; this.elevationMask = mask; this.refractionModel = refractionModel; this.topo = topo; } + /** Protected constructor with full parameters. + *

      + * This constructor is not public as users are expected to use the builder + * API with the various {@code withXxx()} methods to set up the instance + * in a readable manner without using a huge amount of parameters. + *

      + * @param maxCheck maximum checking interval + * @param threshold convergence threshold (s) + * @param maxIter maximum number of iterations in the event time search + * @param handler event handler to call at event occurrences + * @param minElevation minimum elevation in radians (rad) + * @param mask reference to elevation mask + * @param refractionModel reference to refraction model + * @param topo reference to a topocentric model + * @deprecated as of 12.2 + */ + @Deprecated + protected FieldElevationDetector(final FieldAdaptableInterval maxCheck, final T threshold, + final int maxIter, final FieldEventHandler handler, + final double minElevation, final ElevationMask mask, + final AtmosphericRefractionModel refractionModel, final TopocentricFrame topo) { + this(new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler, minElevation, mask, refractionModel, topo); + } + /** {@inheritDoc} */ @Override protected FieldElevationDetector create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, diff --git a/src/main/java/org/orekit/propagation/events/FieldElevationExtremumDetector.java b/src/main/java/org/orekit/propagation/events/FieldElevationExtremumDetector.java index 2114551e3e..63662d348c 100644 --- a/src/main/java/org/orekit/propagation/events/FieldElevationExtremumDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldElevationExtremumDetector.java @@ -90,7 +90,7 @@ public FieldElevationExtremumDetector(final T maxCheck, final T threshold, protected FieldElevationExtremumDetector(final FieldAdaptableInterval maxCheck, final T threshold, final int maxIter, final FieldEventHandler handler, final TopocentricFrame topo) { - super(maxCheck, threshold, maxIter, handler); + super(new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler); this.topo = topo; } diff --git a/src/main/java/org/orekit/propagation/events/FieldEventDetectionSettings.java b/src/main/java/org/orekit/propagation/events/FieldEventDetectionSettings.java new file mode 100644 index 0000000000..275caaff6a --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/FieldEventDetectionSettings.java @@ -0,0 +1,119 @@ +/* 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.propagation.events; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.orekit.propagation.FieldSpacecraftState; + +/** + * Class containing parameters for event detection. + * + * @author Romain Serra. + * @since 12.2 + * @see EventDetectionSettings + * @see FieldEventDetector + */ +public class FieldEventDetectionSettings > { + + /** Default maximum checking interval (s). */ + public static final double DEFAULT_MAXCHECK = 600; + + /** Default convergence threshold (s). */ + public static final double DEFAULT_THRESHOLD = 1.e-6; + + /** Default maximum number of iterations in the event time search. */ + public static final int DEFAULT_MAX_ITER = 100; + + /** Adaptable interval for maximum time without event evaluation. */ + private final FieldAdaptableInterval maxCheckInterval; + + /** Detection threshold (s). */ + private final T threshold; + + /** Maximum iteration number when detecting event. */ + private final int maxIterationCount; + + /** + * Constructor. + * + * @param maxCheckInterval adaptable interval + * @param threshold detection threshold on time + * @param maxIterationCount maximum iteration number + */ + public FieldEventDetectionSettings(final FieldAdaptableInterval maxCheckInterval, final T threshold, + final int maxIterationCount) { + this.maxCheckInterval = maxCheckInterval; + this.maxIterationCount = maxIterationCount; + this.threshold = threshold; + } + + /** + * Constructor with maximum check as double. + * + * @param maxCheck constant maximum check for adaptable interval + * @param threshold detection threshold on time + * @param maxIterationCount maximum iteration number + */ + public FieldEventDetectionSettings(final double maxCheck, final T threshold, final int maxIterationCount) { + this(FieldAdaptableInterval.of(maxCheck), threshold, maxIterationCount); + } + + /** + * Constructor from non-Field settings. + * + * @param field field + * @param eventDetectionSettings non-Field detection settings + */ + public FieldEventDetectionSettings(final Field field, final EventDetectionSettings eventDetectionSettings) { + this(state -> eventDetectionSettings.getMaxCheckInterval().currentInterval(state.toSpacecraftState()), + field.getZero().newInstance(eventDetectionSettings.getThreshold()), eventDetectionSettings.getMaxIterationCount()); + } + + /** + * Getter for adaptable interval. + * @return adaptable interval + */ + public FieldAdaptableInterval getMaxCheckInterval() { + return maxCheckInterval; + } + + /** + * Getter for threshold. + * @return threshold + */ + public T getThreshold() { + return threshold; + } + + /** + * Getter for max iter. + * @return max iter + */ + public int getMaxIterationCount() { + return maxIterationCount; + } + + /** + * Create a non-Field equivalent object. + * @return event detection settings + */ + public EventDetectionSettings toEventDetectionSettings() { + return new EventDetectionSettings(state -> getMaxCheckInterval().currentInterval(new FieldSpacecraftState<>(getThreshold().getField(), state)), + getThreshold().getReal(), getMaxIterationCount()); + } +} diff --git a/src/main/java/org/orekit/propagation/events/FieldEventDetector.java b/src/main/java/org/orekit/propagation/events/FieldEventDetector.java index 8d15eeded6..c42ddedf24 100644 --- a/src/main/java/org/orekit/propagation/events/FieldEventDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldEventDetector.java @@ -112,4 +112,21 @@ default void init(FieldSpacecraftState s0, */ FieldEventHandler getHandler(); + /** + * This method finalizes the event detector's job. + * @param state state at propagation end + * @since 12.2 + */ + default void finish(FieldSpacecraftState state) { + getHandler().finish(state, this); + } + + /** + * Getter for the settings. + * @return detection settings + * @since 12.2 + */ + default FieldEventDetectionSettings getDetectionSettings() { + return new FieldEventDetectionSettings<>(getMaxCheckInterval(), getThreshold(), getMaxIterationCount()); + } } diff --git a/src/main/java/org/orekit/propagation/events/FieldEventEnablingPredicateFilter.java b/src/main/java/org/orekit/propagation/events/FieldEventEnablingPredicateFilter.java index 201d23386f..518c7cafc9 100644 --- a/src/main/java/org/orekit/propagation/events/FieldEventEnablingPredicateFilter.java +++ b/src/main/java/org/orekit/propagation/events/FieldEventEnablingPredicateFilter.java @@ -115,7 +115,7 @@ protected FieldEventEnablingPredicateFilter(final FieldAdaptableInterval maxC final int maxIter, final FieldEventHandler handler, final FieldEventDetector rawDetector, final FieldEnablingPredicate enabler) { - super(maxCheck, threshold, maxIter, handler); + super(new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler); this.rawDetector = rawDetector; this.enabler = enabler; this.transformers = new Transformer[HISTORY_SIZE]; diff --git a/src/main/java/org/orekit/propagation/events/FieldEventSlopeFilter.java b/src/main/java/org/orekit/propagation/events/FieldEventSlopeFilter.java index 8e5d9883c5..9aa82919fa 100644 --- a/src/main/java/org/orekit/propagation/events/FieldEventSlopeFilter.java +++ b/src/main/java/org/orekit/propagation/events/FieldEventSlopeFilter.java @@ -116,7 +116,7 @@ public FieldEventSlopeFilter(final D rawDetector, final FilterType filter) { protected FieldEventSlopeFilter(final FieldAdaptableInterval maxCheck, final T threshold, final int maxIter, final FieldEventHandler handler, final D rawDetector, final FilterType filter) { - super(maxCheck, threshold, maxIter, handler); + super(new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler); this.rawDetector = rawDetector; this.filter = filter; this.transformers = new Transformer[HISTORY_SIZE]; @@ -139,6 +139,7 @@ public D getDetector() { } /** {@inheritDoc} */ + @Override public void init(final FieldSpacecraftState s0, final FieldAbsoluteDate t) { super.init(s0, t); @@ -249,16 +250,16 @@ public T g(final FieldSpacecraftState s) { private static class LocalHandler, T extends CalculusFieldElement> implements FieldEventHandler { /** {@inheritDoc} */ + @SuppressWarnings("unchecked") public Action eventOccurred(final FieldSpacecraftState s, final FieldEventDetector detector, final boolean increasing) { - @SuppressWarnings("unchecked") final FieldEventSlopeFilter esf = (FieldEventSlopeFilter) detector; return esf.rawDetector.getHandler().eventOccurred(s, esf.rawDetector, esf.filter.getTriggeredIncreasing()); } /** {@inheritDoc} */ @Override + @SuppressWarnings("unchecked") public FieldSpacecraftState resetState(final FieldEventDetector detector, final FieldSpacecraftState oldState) { - @SuppressWarnings("unchecked") final FieldEventSlopeFilter esf = (FieldEventSlopeFilter) detector; return esf.rawDetector.getHandler().resetState(esf.rawDetector, oldState); } diff --git a/src/main/java/org/orekit/propagation/events/FieldEventState.java b/src/main/java/org/orekit/propagation/events/FieldEventState.java index 2386db1fcc..aa5eb21da1 100644 --- a/src/main/java/org/orekit/propagation/events/FieldEventState.java +++ b/src/main/java/org/orekit/propagation/events/FieldEventState.java @@ -624,6 +624,15 @@ private void check(final boolean condition) throws MathRuntimeException { } } + /** + * This method finalizes the event detector's job. + * @param state state at propagation end + * @since 12.2 + */ + public void finish(final FieldSpacecraftState state) { + detector.finish(state); + } + /** * Class to hold the data related to an event occurrence that is needed to decide how * to modify integration. diff --git a/src/main/java/org/orekit/propagation/events/FieldEventsLogger.java b/src/main/java/org/orekit/propagation/events/FieldEventsLogger.java index 415708bc3a..a68f755c80 100644 --- a/src/main/java/org/orekit/propagation/events/FieldEventsLogger.java +++ b/src/main/java/org/orekit/propagation/events/FieldEventsLogger.java @@ -59,7 +59,7 @@ public class FieldEventsLogger> { *

      */ public FieldEventsLogger() { - log = new ArrayList>(); + log = new ArrayList<>(); } /** Monitor an event detector. @@ -102,7 +102,7 @@ public void clearLoggedEvents() { * @return an immutable copy of the logged events */ public List> getLoggedEvents() { - return new ArrayList>(log); + return new ArrayList<>(log); } /** Class for logged events entries. @@ -166,9 +166,7 @@ private class FieldLoggingWrapper extends FieldAbstractDetector detector) { - this(detector.getMaxCheckInterval(), detector.getThreshold(), - detector.getMaxIterationCount(), null, - detector); + this(detector.getDetectionSettings(), null, detector); } /** Private constructor with full parameters. @@ -177,17 +175,14 @@ private class FieldLoggingWrapper extends FieldAbstractDetector - * @param maxCheck maximum checking interval - * @param threshold convergence threshold (s) - * @param maxIter maximum number of iterations in the event time search + * @param detectionSettings detection settings * @param handler event handler to call at event occurrences * @param detector events detector to wrap * @since 6.1 */ - private FieldLoggingWrapper(final FieldAdaptableInterval maxCheck, final T threshold, - final int maxIter, final FieldEventHandler handler, + private FieldLoggingWrapper(final FieldEventDetectionSettings detectionSettings, final FieldEventHandler handler, final FieldEventDetector detector) { - super(maxCheck, threshold, maxIter, handler); + super(detectionSettings, handler); this.detector = detector; } @@ -195,7 +190,7 @@ private FieldLoggingWrapper(final FieldAdaptableInterval maxCheck, final T th @Override protected FieldLoggingWrapper create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, final int newMaxIter, final FieldEventHandler newHandler) { - return new FieldLoggingWrapper(newMaxCheck, newThreshold, newMaxIter, newHandler, detector); + return new FieldLoggingWrapper(new FieldEventDetectionSettings<>(newMaxCheck, newThreshold, newMaxIter), newHandler, detector); } /** Log an event. @@ -207,6 +202,7 @@ public void logEvent(final FieldSpacecraftState state, final boolean increasi } /** {@inheritDoc} */ + @Override public void init(final FieldSpacecraftState s0, final FieldAbsoluteDate t) { super.init(s0, t); @@ -219,6 +215,13 @@ public T g(final FieldSpacecraftState s) { } /** {@inheritDoc} */ + @Override + public void finish(final FieldSpacecraftState state) { + detector.finish(state); + } + + /** {@inheritDoc} */ + @Override public FieldEventHandler getHandler() { final FieldEventHandler handler = detector.getHandler(); diff --git a/src/main/java/org/orekit/propagation/events/FieldExtremumApproachDetector.java b/src/main/java/org/orekit/propagation/events/FieldExtremumApproachDetector.java index 287b8fd938..8c8fe13b5b 100644 --- a/src/main/java/org/orekit/propagation/events/FieldExtremumApproachDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldExtremumApproachDetector.java @@ -163,7 +163,7 @@ protected FieldExtremumApproachDetector(final T maxCheck, final T threshold, fin protected FieldExtremumApproachDetector(final FieldAdaptableInterval maxCheck, final T threshold, final int maxIter, final FieldEventHandler handler, final FieldPVCoordinatesProvider secondaryPVProvider) { - super(maxCheck, threshold, maxIter, handler); + super(new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler); this.secondaryPVProvider = secondaryPVProvider; } diff --git a/src/main/java/org/orekit/propagation/events/FieldFunctionalDetector.java b/src/main/java/org/orekit/propagation/events/FieldFunctionalDetector.java index 537b7e7fa2..e1d64eccfd 100644 --- a/src/main/java/org/orekit/propagation/events/FieldFunctionalDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldFunctionalDetector.java @@ -76,7 +76,7 @@ protected FieldFunctionalDetector( final int maxIter, final FieldEventHandler handler, final Function, T> function) { - super(maxCheck, threshold, maxIter, handler); + super(new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler); this.function = function; } diff --git a/src/main/java/org/orekit/propagation/events/FieldLatitudeCrossingDetector.java b/src/main/java/org/orekit/propagation/events/FieldLatitudeCrossingDetector.java index 36d1e6f6c7..661a82dd7e 100644 --- a/src/main/java/org/orekit/propagation/events/FieldLatitudeCrossingDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldLatitudeCrossingDetector.java @@ -92,7 +92,7 @@ protected FieldLatitudeCrossingDetector( final FieldEventHandler handler, final OneAxisEllipsoid body, final double latitude) { - super(maxCheck, threshold, maxIter, handler); + super(new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler); this.body = body; this.latitude = latitude; } diff --git a/src/main/java/org/orekit/propagation/events/FieldLatitudeRangeCrossingDetector.java b/src/main/java/org/orekit/propagation/events/FieldLatitudeRangeCrossingDetector.java index fd85c8f3ae..a99e00002f 100644 --- a/src/main/java/org/orekit/propagation/events/FieldLatitudeRangeCrossingDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldLatitudeRangeCrossingDetector.java @@ -117,7 +117,7 @@ protected FieldLatitudeRangeCrossingDetector(final FieldAdaptableInterval max final OneAxisEllipsoid body, final double fromLatitude, final double toLatitude) { - super(maxCheck, threshold, maxIter, handler); + super(new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler); this.body = body; this.fromLatitude = fromLatitude; this.toLatitude = toLatitude; diff --git a/src/main/java/org/orekit/propagation/events/FieldLongitudeCrossingDetector.java b/src/main/java/org/orekit/propagation/events/FieldLongitudeCrossingDetector.java index 48da9a43b5..9473c18cf0 100644 --- a/src/main/java/org/orekit/propagation/events/FieldLongitudeCrossingDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldLongitudeCrossingDetector.java @@ -106,7 +106,7 @@ protected FieldLongitudeCrossingDetector( final OneAxisEllipsoid body, final double longitude) { - super(maxCheck, threshold, maxIter, handler); + super(new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler); this.body = body; this.longitude = longitude; @@ -116,7 +116,7 @@ protected FieldLongitudeCrossingDetector( new FieldContinueOnEvent<>()); final FieldEnablingPredicate predicate = (state, detector, g) -> FastMath.abs(g).getReal() < 0.5 * FastMath.PI; - this.filtering = new FieldEventEnablingPredicateFilter(raw, predicate); + this.filtering = new FieldEventEnablingPredicateFilter<>(raw, predicate); } @@ -154,6 +154,7 @@ public double getLongitude() { /** * {@inheritDoc} */ + @Override public void init(final FieldSpacecraftState s0, final FieldAbsoluteDate t) { filtering.init(s0, t); } @@ -201,7 +202,7 @@ protected FieldRawLongitudeCrossingDetector( final TT threshold, final int maxIter, final FieldEventHandler handler) { - super(maxCheck, threshold, maxIter, handler); + super(new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler); } /** diff --git a/src/main/java/org/orekit/propagation/events/FieldLongitudeRangeCrossingDetector.java b/src/main/java/org/orekit/propagation/events/FieldLongitudeRangeCrossingDetector.java index 79c76b4fc7..6f0a56388d 100644 --- a/src/main/java/org/orekit/propagation/events/FieldLongitudeRangeCrossingDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldLongitudeRangeCrossingDetector.java @@ -119,7 +119,7 @@ protected FieldLongitudeRangeCrossingDetector(final FieldAdaptableInterval ma final OneAxisEllipsoid body, final double fromLongitude, final double toLongitude) { - super(maxCheck, threshold, maxIter, handler); + super(new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler); this.body = body; this.fromLongitude = ensureLongitudePositiveContinuity(fromLongitude); this.toLongitude = ensureLongitudePositiveContinuity(toLongitude); @@ -134,7 +134,7 @@ protected FieldLongitudeRangeCrossingDetector create(final FieldAdaptableInte final T newThreshold, final int newMaxIter, final FieldEventHandler newHandler) { - return new FieldLongitudeRangeCrossingDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, + return new FieldLongitudeRangeCrossingDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler, body, fromLongitude, toLongitude); } diff --git a/src/main/java/org/orekit/propagation/events/FieldNegateDetector.java b/src/main/java/org/orekit/propagation/events/FieldNegateDetector.java index 5a17b21f32..155282a6a3 100644 --- a/src/main/java/org/orekit/propagation/events/FieldNegateDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldNegateDetector.java @@ -68,7 +68,7 @@ protected FieldNegateDetector(final FieldAdaptableInterval newMaxCheck, final int newMaxIter, final FieldEventHandler newHandler, final FieldEventDetector original) { - super(newMaxCheck, newThreshold, newMaxIter, newHandler); + super(new FieldEventDetectionSettings<>(newMaxCheck, newThreshold, newMaxIter), newHandler); this.original = original; } diff --git a/src/main/java/org/orekit/propagation/events/FieldNodeDetector.java b/src/main/java/org/orekit/propagation/events/FieldNodeDetector.java index 66d0c07e1d..e75e8fac97 100644 --- a/src/main/java/org/orekit/propagation/events/FieldNodeDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldNodeDetector.java @@ -96,7 +96,7 @@ public FieldNodeDetector(final T threshold, final FieldOrbit orbit, final Fra protected FieldNodeDetector(final FieldAdaptableInterval maxCheck, final T threshold, final int maxIter, final FieldEventHandler handler, final Frame frame) { - super(maxCheck, threshold, maxIter, handler); + super(new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler); this.frame = frame; } diff --git a/src/main/java/org/orekit/propagation/events/FieldOfViewDetector.java b/src/main/java/org/orekit/propagation/events/FieldOfViewDetector.java index 980c00ffe9..6b358b083d 100644 --- a/src/main/java/org/orekit/propagation/events/FieldOfViewDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldOfViewDetector.java @@ -105,7 +105,7 @@ protected FieldOfViewDetector(final AdaptableInterval maxCheck, final double thr final EventHandler handler, final PVCoordinatesProvider pvTarget, final double radiusTarget, final VisibilityTrigger trigger, final FieldOfView fov) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.targetPVProvider = pvTarget; this.radiusTarget = radiusTarget; this.trigger = trigger; diff --git a/src/main/java/org/orekit/propagation/events/FieldParameterDrivenDateIntervalDetector.java b/src/main/java/org/orekit/propagation/events/FieldParameterDrivenDateIntervalDetector.java index 74e8b30e5b..232321f298 100644 --- a/src/main/java/org/orekit/propagation/events/FieldParameterDrivenDateIntervalDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldParameterDrivenDateIntervalDetector.java @@ -126,7 +126,7 @@ protected FieldParameterDrivenDateIntervalDetector(final FieldAdaptableInterval< final FieldEventHandler handler, final DateDriver start, final DateDriver stop, final DateDriver median, final ParameterDriver duration) { - super(maxCheck, threshold, maxIter, handler); + super(new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler); this.start = start; this.stop = stop; this.median = median; diff --git a/src/main/java/org/orekit/propagation/events/FieldRelativeDistanceDetector.java b/src/main/java/org/orekit/propagation/events/FieldRelativeDistanceDetector.java index 62be3ae7d9..2201f5f51b 100644 --- a/src/main/java/org/orekit/propagation/events/FieldRelativeDistanceDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldRelativeDistanceDetector.java @@ -73,6 +73,27 @@ public FieldRelativeDistanceDetector(final FieldPVCoordinatesProvider seconda DEFAULT_MAX_ITER, new FieldStopOnEvent<>(), secondaryPVProvider, distanceThreshold); } + /** + * Constructor. + *

      + * This constructor is to be used if the user wants to change the default behavior of the detector. + *

      + * + * @param detectionSettings Detection settings. + * @param handler Event handler to call at event occurrences. + * @param secondaryPVProvider PVCoordinates provider of the other object defining relative distance. + * @param distanceThreshold Relative distance threshold for event detection + * @see FieldEventHandler + * @since 12.2 + */ + protected FieldRelativeDistanceDetector(final FieldEventDetectionSettings detectionSettings, + final FieldEventHandler handler, final FieldPVCoordinatesProvider secondaryPVProvider, + final T distanceThreshold) { + super(detectionSettings, handler); + this.secondaryPVProvider = secondaryPVProvider; + this.distanceThreshold = distanceThreshold; + } + /** * Constructor. *

      @@ -86,13 +107,13 @@ public FieldRelativeDistanceDetector(final FieldPVCoordinatesProvider seconda * @param secondaryPVProvider PVCoordinates provider of the other object defining relative distance. * @param distanceThreshold Relative distance threshold for event detection * @see FieldEventHandler + * @deprecated as of 12.2 */ + @Deprecated protected FieldRelativeDistanceDetector(final FieldAdaptableInterval maxCheck, final T threshold, final int maxIter, final FieldEventHandler handler, final FieldPVCoordinatesProvider secondaryPVProvider, final T distanceThreshold) { - super(maxCheck, threshold, maxIter, handler); - this.secondaryPVProvider = secondaryPVProvider; - this.distanceThreshold = distanceThreshold; + this(new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler, secondaryPVProvider, distanceThreshold); } /** diff --git a/src/main/java/org/orekit/propagation/events/FootprintOverlapDetector.java b/src/main/java/org/orekit/propagation/events/FootprintOverlapDetector.java index 9870175048..a833c9e63e 100644 --- a/src/main/java/org/orekit/propagation/events/FootprintOverlapDetector.java +++ b/src/main/java/org/orekit/propagation/events/FootprintOverlapDetector.java @@ -132,7 +132,7 @@ protected FootprintOverlapDetector(final AdaptableInterval maxCheck, final doubl final double samplingStep, final List sampledZone) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.fov = fov; this.body = body; this.samplingStep = samplingStep; diff --git a/src/main/java/org/orekit/propagation/events/FunctionalDetector.java b/src/main/java/org/orekit/propagation/events/FunctionalDetector.java index 3104706670..202d9e9570 100644 --- a/src/main/java/org/orekit/propagation/events/FunctionalDetector.java +++ b/src/main/java/org/orekit/propagation/events/FunctionalDetector.java @@ -67,7 +67,7 @@ protected FunctionalDetector(final AdaptableInterval maxCheck, final int maxIter, final EventHandler handler, final ToDoubleFunction function) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.function = function; } diff --git a/src/main/java/org/orekit/propagation/events/GeographicZoneDetector.java b/src/main/java/org/orekit/propagation/events/GeographicZoneDetector.java index e38d429337..633d031408 100644 --- a/src/main/java/org/orekit/propagation/events/GeographicZoneDetector.java +++ b/src/main/java/org/orekit/propagation/events/GeographicZoneDetector.java @@ -102,7 +102,7 @@ protected GeographicZoneDetector(final AdaptableInterval maxCheck, final double final SphericalPolygonsSet zone, final EnclosingBall cap, final double margin) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.body = body; this.zone = zone; this.cap = cap; diff --git a/src/main/java/org/orekit/propagation/events/GroundAtNightDetector.java b/src/main/java/org/orekit/propagation/events/GroundAtNightDetector.java index 4898482cb3..ceee7ab2fd 100644 --- a/src/main/java/org/orekit/propagation/events/GroundAtNightDetector.java +++ b/src/main/java/org/orekit/propagation/events/GroundAtNightDetector.java @@ -105,7 +105,7 @@ protected GroundAtNightDetector(final TopocentricFrame groundLocation, final PVC final double threshold, final int maxIter, final EventHandler handler) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.groundLocation = groundLocation; this.sun = sun; this.dawnDuskElevation = dawnDuskElevation; diff --git a/src/main/java/org/orekit/propagation/events/GroundFieldOfViewDetector.java b/src/main/java/org/orekit/propagation/events/GroundFieldOfViewDetector.java index c1434de1d8..39433e25d1 100644 --- a/src/main/java/org/orekit/propagation/events/GroundFieldOfViewDetector.java +++ b/src/main/java/org/orekit/propagation/events/GroundFieldOfViewDetector.java @@ -89,7 +89,7 @@ protected GroundFieldOfViewDetector(final AdaptableInterval maxCheck, final EventHandler handler, final Frame frame, final FieldOfView fov) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.frame = frame; this.fov = fov; } diff --git a/src/main/java/org/orekit/propagation/events/HaloXZPlaneCrossingDetector.java b/src/main/java/org/orekit/propagation/events/HaloXZPlaneCrossingDetector.java index 90b6d7fb58..27414fe4cf 100644 --- a/src/main/java/org/orekit/propagation/events/HaloXZPlaneCrossingDetector.java +++ b/src/main/java/org/orekit/propagation/events/HaloXZPlaneCrossingDetector.java @@ -51,7 +51,7 @@ public HaloXZPlaneCrossingDetector(final double maxCheck, final double threshold protected HaloXZPlaneCrossingDetector(final AdaptableInterval maxCheck, final double threshold, final int maxIter, final EventHandler handler) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); } /** {@inheritDoc} */ diff --git a/src/main/java/org/orekit/propagation/events/InterSatDirectViewDetector.java b/src/main/java/org/orekit/propagation/events/InterSatDirectViewDetector.java index 1c82e3418b..1e822bf988 100644 --- a/src/main/java/org/orekit/propagation/events/InterSatDirectViewDetector.java +++ b/src/main/java/org/orekit/propagation/events/InterSatDirectViewDetector.java @@ -106,7 +106,7 @@ protected InterSatDirectViewDetector(final OneAxisEllipsoid body, final double threshold, final int maxIter, final EventHandler handler) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.body = body; this.skimmingAltitude = skimmingAltitude; this.secondary = secondary; diff --git a/src/main/java/org/orekit/propagation/events/LatitudeCrossingDetector.java b/src/main/java/org/orekit/propagation/events/LatitudeCrossingDetector.java index cc2b0c468b..ea2dd3a4a7 100644 --- a/src/main/java/org/orekit/propagation/events/LatitudeCrossingDetector.java +++ b/src/main/java/org/orekit/propagation/events/LatitudeCrossingDetector.java @@ -75,7 +75,7 @@ public LatitudeCrossingDetector(final double maxCheck, final double threshold, protected LatitudeCrossingDetector(final AdaptableInterval maxCheck, final double threshold, final int maxIter, final EventHandler handler, final OneAxisEllipsoid body, final double latitude) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.body = body; this.latitude = latitude; } diff --git a/src/main/java/org/orekit/propagation/events/LatitudeExtremumDetector.java b/src/main/java/org/orekit/propagation/events/LatitudeExtremumDetector.java index 59f7d61550..4d1b13b98f 100644 --- a/src/main/java/org/orekit/propagation/events/LatitudeExtremumDetector.java +++ b/src/main/java/org/orekit/propagation/events/LatitudeExtremumDetector.java @@ -71,7 +71,7 @@ public LatitudeExtremumDetector(final double maxCheck, final double threshold, protected LatitudeExtremumDetector(final AdaptableInterval maxCheck, final double threshold, final int maxIter, final EventHandler handler, final OneAxisEllipsoid body) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.body = body; } diff --git a/src/main/java/org/orekit/propagation/events/LatitudeRangeCrossingDetector.java b/src/main/java/org/orekit/propagation/events/LatitudeRangeCrossingDetector.java index 4e2029d25c..52b7173c71 100644 --- a/src/main/java/org/orekit/propagation/events/LatitudeRangeCrossingDetector.java +++ b/src/main/java/org/orekit/propagation/events/LatitudeRangeCrossingDetector.java @@ -88,7 +88,7 @@ public LatitudeRangeCrossingDetector(final double maxCheck, final double thresho protected LatitudeRangeCrossingDetector(final AdaptableInterval maxCheck, final double threshold, final int maxIter, final EventHandler handler, final OneAxisEllipsoid body, final double fromLatitude, final double toLatitude) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.body = body; this.fromLatitude = fromLatitude; this.toLatitude = toLatitude; diff --git a/src/main/java/org/orekit/propagation/events/LongitudeCrossingDetector.java b/src/main/java/org/orekit/propagation/events/LongitudeCrossingDetector.java index 8f0e185ac1..265311b15c 100644 --- a/src/main/java/org/orekit/propagation/events/LongitudeCrossingDetector.java +++ b/src/main/java/org/orekit/propagation/events/LongitudeCrossingDetector.java @@ -83,7 +83,7 @@ protected LongitudeCrossingDetector(final AdaptableInterval maxCheck, final doub final int maxIter, final EventHandler handler, final OneAxisEllipsoid body, final double longitude) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.body = body; this.longitude = longitude; @@ -120,6 +120,7 @@ public double getLongitude() { } /** {@inheritDoc} */ + @Override public void init(final SpacecraftState s0, final AbsoluteDate t) { filtering.init(s0, t); } @@ -159,7 +160,7 @@ private class RawLongitudeCrossingDetector extends AbstractDetector { * @since 10.3 */ public NodeDetector(final Frame frame) { - this(AdaptableInterval.of(DEFAULT_MAX_CHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + this(new EventDetectionSettings(AdaptableInterval.of(DEFAULT_MAX_CHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER), new StopOnIncreasing(), frame); } @@ -94,8 +94,8 @@ public NodeDetector(final Orbit orbit, final Frame frame) { * {@link org.orekit.frames.FramesFactory#getITRF(org.orekit.utils.IERSConventions, boolean) ITRF}) */ public NodeDetector(final double threshold, final Orbit orbit, final Frame frame) { - this(AdaptableInterval.of(2 * estimateNodesTimeSeparation(orbit) / 3), threshold, - DEFAULT_MAX_ITER, new StopOnIncreasing(), + this(new EventDetectionSettings(AdaptableInterval.of(2 * estimateNodesTimeSeparation(orbit) / 3), threshold, + DEFAULT_MAX_ITER), new StopOnIncreasing(), frame); } @@ -113,11 +113,31 @@ DEFAULT_MAX_ITER, new StopOnIncreasing(), * values are {@link org.orekit.frames.FramesFactory#getEME2000() EME2000} or * {@link org.orekit.frames.FramesFactory#getITRF(org.orekit.utils.IERSConventions, boolean) ITRF}) * @since 6.1 + * @deprecated as of 12.2 */ + @Deprecated protected NodeDetector(final AdaptableInterval maxCheck, final double threshold, final int maxIter, final EventHandler handler, final Frame frame) { - super(maxCheck, threshold, maxIter, handler); + this(new EventDetectionSettings(maxCheck, threshold, maxIter), handler, frame); + } + + /** Protected constructor with full parameters. + *

      + * This constructor is not public as users are expected to use the builder + * API with the various {@code withXxx()} methods to set up the instance + * in a readable manner without using a huge amount of parameters. + *

      + * @param detectionSettings detection settings + * @param handler event handler to call at event occurrences + * @param frame frame in which the equator is defined (typical + * values are {@link org.orekit.frames.FramesFactory#getEME2000() EME2000} or + * {@link org.orekit.frames.FramesFactory#getITRF(org.orekit.utils.IERSConventions, boolean) ITRF}) + * @since 12.2 + */ + protected NodeDetector(final EventDetectionSettings detectionSettings, final EventHandler handler, + final Frame frame) { + super(detectionSettings, handler); this.frame = frame; } @@ -125,7 +145,7 @@ protected NodeDetector(final AdaptableInterval maxCheck, final double threshold, @Override protected NodeDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, final int newMaxIter, final EventHandler newHandler) { - return new NodeDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, frame); + return new NodeDetector(new EventDetectionSettings(newMaxCheck, newThreshold, newMaxIter), newHandler, frame); } /** Find time separation between nodes. diff --git a/src/main/java/org/orekit/propagation/events/ParameterDrivenDateIntervalDetector.java b/src/main/java/org/orekit/propagation/events/ParameterDrivenDateIntervalDetector.java index 436b0bb5be..21ea09e21a 100644 --- a/src/main/java/org/orekit/propagation/events/ParameterDrivenDateIntervalDetector.java +++ b/src/main/java/org/orekit/propagation/events/ParameterDrivenDateIntervalDetector.java @@ -125,7 +125,7 @@ protected ParameterDrivenDateIntervalDetector(final AdaptableInterval maxCheck, final EventHandler handler, final DateDriver start, final DateDriver stop, final DateDriver median, final ParameterDriver duration) { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.start = start; this.stop = stop; this.median = median; @@ -151,7 +151,7 @@ private void replaceBindingObserver(final ParameterDriver driver, final BindingO stream(). filter(observer -> observer instanceof ParameterDrivenDateIntervalDetector.BindingObserver). collect(Collectors.toList()); - original.forEach(observer -> driver.removeObserver(observer)); + original.forEach(driver::removeObserver); driver.addObserver(bindingObserver); diff --git a/src/main/java/org/orekit/propagation/events/PositionAngleDetector.java b/src/main/java/org/orekit/propagation/events/PositionAngleDetector.java index 8f515d2f56..b2a9c8efd3 100644 --- a/src/main/java/org/orekit/propagation/events/PositionAngleDetector.java +++ b/src/main/java/org/orekit/propagation/events/PositionAngleDetector.java @@ -118,7 +118,7 @@ protected PositionAngleDetector(final AdaptableInterval maxCheck, final double t final double angle) throws OrekitIllegalArgumentException { - super(maxCheck, threshold, maxIter, handler); + super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); this.orbitType = orbitType; this.positionAngleType = positionAngleType; @@ -177,6 +177,7 @@ public double getAngle() { } /** {@inheritDoc} */ + @Override public void init(final SpacecraftState s0, final AbsoluteDate t) { super.init(s0, t); offsetEstimators = new TimeSpanMap<>(new OffsetEstimator(s0.getOrbit(), +1.0)); diff --git a/src/main/java/org/orekit/propagation/events/RelativeDistanceDetector.java b/src/main/java/org/orekit/propagation/events/RelativeDistanceDetector.java index ca57777062..cbf146fb15 100644 --- a/src/main/java/org/orekit/propagation/events/RelativeDistanceDetector.java +++ b/src/main/java/org/orekit/propagation/events/RelativeDistanceDetector.java @@ -76,10 +76,31 @@ public class RelativeDistanceDetector extends AbstractDetector + * This constructor is to be used if the user wants to change the default behavior of the detector. + *

      + * + * @param detectionSettings Detection settings + * @param handler Event handler to call at event occurrences. + * @param secondaryPVProvider PVCoordinates provider of the other object defining relative distance. + * @param distanceThreshold Relative distance threshold for event detection + * @see EventHandler + * @since 12.2 + */ + protected RelativeDistanceDetector(final EventDetectionSettings detectionSettings, + final EventHandler handler, final PVCoordinatesProvider secondaryPVProvider, + final double distanceThreshold) { + super(detectionSettings, handler); + this.secondaryPVProvider = secondaryPVProvider; + this.distanceThreshold = distanceThreshold; + } + /** * Constructor. *

      @@ -93,13 +114,13 @@ public RelativeDistanceDetector(final PVCoordinatesProvider secondaryPVProvider, * @param secondaryPVProvider PVCoordinates provider of the other object defining relative distance. * @param distanceThreshold Relative distance threshold for event detection * @see EventHandler + * @deprecated as of 12.2 */ + @Deprecated protected RelativeDistanceDetector(final AdaptableInterval maxCheck, final double threshold, final int maxIter, final EventHandler handler, final PVCoordinatesProvider secondaryPVProvider, final double distanceThreshold) { - super(maxCheck, threshold, maxIter, handler); - this.secondaryPVProvider = secondaryPVProvider; - this.distanceThreshold = distanceThreshold; + this(new EventDetectionSettings(maxCheck, threshold, maxIter), handler, secondaryPVProvider, distanceThreshold); } /** diff --git a/src/main/java/org/orekit/propagation/events/handlers/EventHandler.java b/src/main/java/org/orekit/propagation/events/handlers/EventHandler.java index 083bf23aab..f4df340359 100644 --- a/src/main/java/org/orekit/propagation/events/handlers/EventHandler.java +++ b/src/main/java/org/orekit/propagation/events/handlers/EventHandler.java @@ -77,4 +77,18 @@ default SpacecraftState resetState(EventDetector detector, SpacecraftState oldSt return oldState; } + + /** + * This method finalizes the event handler's job. + *

      + * The default implementation does nothing + *

      + * @param finalState state at propagation end + * @param detector event detector related to the event handler + * @since 12.2 + */ + default void finish(final SpacecraftState finalState, final EventDetector detector) { + // nothing by default + } + } diff --git a/src/main/java/org/orekit/propagation/events/handlers/EventMultipleHandler.java b/src/main/java/org/orekit/propagation/events/handlers/EventMultipleHandler.java index fb53ad9d35..c21d8fcbf1 100644 --- a/src/main/java/org/orekit/propagation/events/handlers/EventMultipleHandler.java +++ b/src/main/java/org/orekit/propagation/events/handlers/EventMultipleHandler.java @@ -143,6 +143,13 @@ public SpacecraftState resetState(final EventDetector detector, final Spacecraft return newState; } + @Override + public void finish(final SpacecraftState finalState, final EventDetector detector) { + for (final EventHandler handler : handlers) { + handler.finish(finalState, detector); + } + } + /** Add one handler to the managed handlers list. * @param handler handler associated with D detector * @return this object diff --git a/src/main/java/org/orekit/propagation/events/handlers/FieldEventHandler.java b/src/main/java/org/orekit/propagation/events/handlers/FieldEventHandler.java index 1a47857d6b..31041de0f7 100644 --- a/src/main/java/org/orekit/propagation/events/handlers/FieldEventHandler.java +++ b/src/main/java/org/orekit/propagation/events/handlers/FieldEventHandler.java @@ -78,4 +78,19 @@ default void init(FieldSpacecraftState initialState, FieldAbsoluteDate tar default FieldSpacecraftState resetState(FieldEventDetector detector, FieldSpacecraftState oldState) { return oldState; } + + + /** + * This method finalize event handler at the end of a propagation. + *

      + * The default implementation does nothing + *

      + * @param finalState state at propagation end + * @param detector event detector related to the event handler + * @since 12.2 + */ + default void finish(final FieldSpacecraftState finalState, final FieldEventDetector detector) { + // nothing by default + } + } diff --git a/src/main/java/org/orekit/propagation/events/handlers/FieldRecallLastOccurrence.java b/src/main/java/org/orekit/propagation/events/handlers/FieldRecallLastOccurrence.java index 3ccc07dd45..5c4365341b 100644 --- a/src/main/java/org/orekit/propagation/events/handlers/FieldRecallLastOccurrence.java +++ b/src/main/java/org/orekit/propagation/events/handlers/FieldRecallLastOccurrence.java @@ -80,4 +80,10 @@ public FieldSpacecraftState resetState(final FieldEventDetector detector, final FieldSpacecraftState oldState) { return wrappedHandler.resetState(detector, oldState); } + + /** {@inheritDoc} */ + @Override + public void finish(final FieldSpacecraftState finalState, final FieldEventDetector detector) { + wrappedHandler.finish(finalState, detector); + } } diff --git a/src/main/java/org/orekit/propagation/events/handlers/FieldResetDerivativesOnEvent.java b/src/main/java/org/orekit/propagation/events/handlers/FieldResetDerivativesOnEvent.java new file mode 100644 index 0000000000..926287d1b2 --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/handlers/FieldResetDerivativesOnEvent.java @@ -0,0 +1,49 @@ +/* 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.propagation.events.handlers; + + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.ode.events.Action; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.FieldEventDetector; + + +/** + * Event handler which will always return {@link Action#RESET_DERIVATIVES continue} as a state. + * @author Romain Serra + * + * @param type of the field element + * @since 12.2 + */ +public class FieldResetDerivativesOnEvent> implements FieldEventHandler { + + /** + * Specific implementation of the eventOccurred interface. + * + * @param s SpaceCraft state to be used in the evaluation + * @param detector object with appropriate type that can be used in determining correct return state + * @param increasing with the event occurred in an "increasing" or "decreasing" slope direction + * @return {@link Action#RESET_DERIVATIVES stop} under all circumstances + */ + @Override + public Action eventOccurred(final FieldSpacecraftState s, final FieldEventDetector detector, final boolean increasing) { + return Action.RESET_DERIVATIVES; + } + +} diff --git a/src/main/java/org/orekit/propagation/events/handlers/RecallLastOccurrence.java b/src/main/java/org/orekit/propagation/events/handlers/RecallLastOccurrence.java index 7263d2397e..162861ff8c 100644 --- a/src/main/java/org/orekit/propagation/events/handlers/RecallLastOccurrence.java +++ b/src/main/java/org/orekit/propagation/events/handlers/RecallLastOccurrence.java @@ -71,4 +71,10 @@ public Action eventOccurred(final SpacecraftState s, final EventDetector detecto public SpacecraftState resetState(final EventDetector detector, final SpacecraftState oldState) { return wrappedHandler.resetState(detector, oldState); } + + /** {@inheritDoc} */ + @Override + public void finish(final SpacecraftState finalState, final EventDetector detector) { + wrappedHandler.finish(finalState, detector); + } } diff --git a/src/main/java/org/orekit/propagation/events/handlers/ResetDerivativesOnEvent.java b/src/main/java/org/orekit/propagation/events/handlers/ResetDerivativesOnEvent.java new file mode 100644 index 0000000000..d75f08267d --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/handlers/ResetDerivativesOnEvent.java @@ -0,0 +1,45 @@ +/* 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.propagation.events.handlers; + +import org.hipparchus.ode.events.Action; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.EventDetector; + +/** + * Event handler which will always return {@link Action#RESET_DERIVATIVES stop} as a state. + * + * @author Romain Serra + * + * @since 12.2 + */ +public class ResetDerivativesOnEvent implements EventHandler { + + /** + * Specific implementation of the eventOccurred interface. + * + * @param s SpaceCraft state to be used in the evaluation + * @param detector object with appropriate type that can be used in determining correct return state + * @param increasing with the event occurred in an "increasing" or "decreasing" slope direction + * @return {@link Action#RESET_DERIVATIVES stop} under all circumstances + */ + @Override + public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) { + return Action.RESET_DERIVATIVES; + } + +} diff --git a/src/main/java/org/orekit/propagation/integration/AbstractIntegratedPropagator.java b/src/main/java/org/orekit/propagation/integration/AbstractIntegratedPropagator.java index d8b176c9f6..70c6e31bf1 100644 --- a/src/main/java/org/orekit/propagation/integration/AbstractIntegratedPropagator.java +++ b/src/main/java/org/orekit/propagation/integration/AbstractIntegratedPropagator.java @@ -439,12 +439,28 @@ public SpacecraftState propagate(final AbsoluteDate tStart, final AbsoluteDate t // propagate from start date to end date with event detection final SpacecraftState finalState = integrateDynamics(tEnd, false); - return finalState; + // Finalize event detectors + getEventsDetectors().forEach(detector -> detector.finish(finalState)); + return finalState; } } + /** Reset initial state with a given propagation type. + * + *

      By default this method returns the same as {@link #resetInitialState(SpacecraftState)} + *

      Its purpose is mostly to be derived in DSSTPropagator + * + * @param state new initial state to consider + * @param stateType type of the new state (mean or osculating) + * @since 12.1.3 + */ + public void resetInitialState(final SpacecraftState state, final PropagationType stateType) { + // Default behavior, do not take propagation type into account + resetInitialState(state); + } + /** Set up State Transition Matrix and Jacobian matrix handling. * @since 11.1 */ @@ -505,7 +521,7 @@ private SpacecraftState integrateDynamics(final AbsoluteDate tEnd, final boolean finalState = updateAdditionalStatesAndDerivatives(finalState, mathFinalState); if (resetAtEnd || forceResetAtEnd) { - resetInitialState(finalState); + resetInitialState(finalState, propagationType); setStartDate(finalState.getDate()); } diff --git a/src/main/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagator.java b/src/main/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagator.java index b5ca913ae1..a64e6a73f3 100644 --- a/src/main/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagator.java +++ b/src/main/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagator.java @@ -444,12 +444,30 @@ public FieldSpacecraftState propagate(final FieldAbsoluteDate tStart, fina } // propagate from start date to end date with event detection - return integrateDynamics(tEnd); + final FieldSpacecraftState state = integrateDynamics(tEnd); + // Finalize event detectors + getEventsDetectors().forEach(detector -> detector.finish(state)); + + return state; } } + /** Reset initial state with a given propagation type. + * + *

      By default this method returns the same as method resetInitialState(FieldSpacecraftState) + *

      Its purpose is mostly to be derived in FieldDSSTPropagator + * + * @param state new initial state to consider + * @param stateType type of the new state (mean or osculating) + * @since 12.1.3 + */ + public void resetInitialState(final FieldSpacecraftState state, final PropagationType stateType) { + // Default behavior, do not take propagation type into account + resetInitialState(state); + } + /** Propagation with or without event detection. * @param tEnd target date to which orbit should be propagated * @return state at end of propagation @@ -500,7 +518,7 @@ private FieldSpacecraftState integrateDynamics(final FieldAbsoluteDate tEn finalState = updateAdditionalStatesAndDerivatives(finalState, mathFinalState); if (resetAtEnd) { - resetInitialState(finalState); + resetInitialState(finalState, propagationType); setStartDate(finalState.getDate()); } diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTHarvester.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTHarvester.java index 0c082d4ac6..fe4f24fa2c 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTHarvester.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTHarvester.java @@ -274,8 +274,11 @@ public void initializeFieldShortPeriodTerms(final SpacecraftState reference, type, dsParameters); // create a copy of the list to protect against inadvertent modification - fieldShortPeriodTerms.computeIfAbsent(forceModel, x -> new ArrayList<>()) - .addAll(terms); + final List> list; + synchronized (fieldShortPeriodTerms) { + list = fieldShortPeriodTerms.computeIfAbsent(forceModel, x -> new ArrayList<>()); + } + list.addAll(terms); } @@ -324,8 +327,10 @@ public void setReferenceState(final SpacecraftState reference) { final Gradient zero = dsState.getDate().getField().getZero(); final Gradient[] shortPeriod = new Gradient[6]; Arrays.fill(shortPeriod, zero); - final List> terms = fieldShortPeriodTerms - .computeIfAbsent(forceModel, x -> new ArrayList<>(0)); + final List> terms; + synchronized (fieldShortPeriodTerms) { + terms = fieldShortPeriodTerms.computeIfAbsent(forceModel, x -> new ArrayList<>(0)); + } for (final FieldShortPeriodTerms spt : terms) { final Gradient[] spVariation = spt.value(dsState.getOrbit()); for (int i = 0; i < spVariation .length; i++) { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTPropagator.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTPropagator.java index a031e60225..e769d8104b 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTPropagator.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTPropagator.java @@ -16,6 +16,14 @@ */ package org.orekit.propagation.semianalytical.dsst; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + import org.hipparchus.linear.RealMatrix; import org.hipparchus.ode.ODEIntegrator; import org.hipparchus.ode.ODEStateAndDerivative; @@ -28,7 +36,6 @@ import org.orekit.attitudes.AttitudeProvider; import org.orekit.data.DataContext; import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; import org.orekit.orbits.EquinoctialOrbit; @@ -59,14 +66,6 @@ import org.orekit.utils.TimeSpanMap; import org.orekit.utils.TimeSpanMap.Span; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - /** * This class propagates {@link org.orekit.orbits.Orbit orbits} using the DSST theory. *

      @@ -278,17 +277,7 @@ public void setInitialState(final SpacecraftState initialState) { */ public void setInitialState(final SpacecraftState initialState, final PropagationType stateType) { - switch (stateType) { - case MEAN: - initialIsOsculating = false; - break; - case OSCULATING: - initialIsOsculating = true; - break; - default: - throw new OrekitInternalError(null); - } - resetInitialState(initialState); + resetInitialState(initialState, stateType); } /** Reset the initial state. @@ -305,6 +294,20 @@ public void resetInitialState(final SpacecraftState state) { super.setStartDate(state.getDate()); } + /** {@inheritDoc}. + * + *

      Change parameter {@link #initialIsOsculating()} accordingly + * @since 12.1.3 + */ + @Override + public void resetInitialState(final SpacecraftState state, final PropagationType stateType) { + // Reset initial state + resetInitialState(state); + + // Change state of initial osculating, if needed + initialIsOsculating = stateType == PropagationType.OSCULATING; + } + /** Set the selected short periodic coefficients that must be stored as additional states. * @param selectedCoefficients short periodic coefficients that must be stored as additional states * (null means no coefficients are selected, empty set means all coefficients are selected) diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTPropagator.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTPropagator.java index 30eac9acc0..ad51495d81 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTPropagator.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTPropagator.java @@ -16,6 +16,14 @@ */ package org.orekit.propagation.semianalytical.dsst; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; import org.hipparchus.ode.FieldODEIntegrator; @@ -58,14 +66,6 @@ import org.orekit.utils.ParameterObserver; import org.orekit.utils.TimeSpanMap; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - /** * This class propagates {@link org.orekit.orbits.FieldOrbit orbits} using the DSST theory. *

      @@ -309,17 +309,7 @@ public void setInitialState(final FieldSpacecraftState initialState) { */ public void setInitialState(final FieldSpacecraftState initialState, final PropagationType stateType) { - switch (stateType) { - case MEAN: - initialIsOsculating = false; - break; - case OSCULATING: - initialIsOsculating = true; - break; - default: - throw new OrekitInternalError(null); - } - resetInitialState(initialState); + resetInitialState(initialState, stateType); } /** Reset the initial state. @@ -336,6 +326,20 @@ public void resetInitialState(final FieldSpacecraftState state) { super.setStartDate(state.getDate()); } + /** {@inheritDoc}. + * + *

      Change parameter {@link #initialIsOsculating()} accordingly + * @since 12.1.3 + */ + @Override + public void resetInitialState(final FieldSpacecraftState state, final PropagationType stateType) { + // Reset initial state + resetInitialState(state); + + // Change state of initial osculating, if needed + initialIsOsculating = stateType == PropagationType.OSCULATING; + } + /** Set the selected short periodic coefficients that must be stored as additional states. * @param selectedCoefficients short periodic coefficients that must be stored as additional states * (null means no coefficients are selected, empty set means all coefficients are selected) diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTGravityContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTGravityContext.java new file mode 100644 index 0000000000..0c36025a38 --- /dev/null +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTGravityContext.java @@ -0,0 +1,257 @@ +/* Copyright 2002-2024 CS GROUP + * 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.propagation.semianalytical.dsst.forces; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.frames.Frame; +import org.orekit.frames.StaticTransform; +import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements; + +/** + * This class is a container for the common parameters used in {@link DSSTTesseral} and {@link DSSTZonal}. + *

      + * It performs parameters initialization at each integration step for the Tesseral and Zonal contribution + * to the central body gravitational perturbation. + *

      + * @author Bryan Cazabonne + * @author Maxime Journot + * @since 12.2 + */ +public class DSSTGravityContext extends ForceModelContext { + + /** A = sqrt(μ * a). */ + private final double A; + + /** Χ = 1 / sqrt(1 - e²) = 1 / B. */ + private final double chi; + + /** Χ². */ + private final double chi2; + + // Common factors from equinoctial coefficients + /** 2 * a / A . */ + private final double ax2oA; + + /** 1 / (A * B) . */ + private final double ooAB; + + /** B / A . */ + private final double BoA; + + /** B / (A * (1 + B)) . */ + private final double BoABpo; + + /** C / (2 * A * B) . */ + private final double Co2AB; + + /** μ / a . */ + private final double muoa; + + /** R / a . */ + private final double roa; + + /** Keplerian mean motion. */ + private final double n; + + /** Direction cosine α. */ + private final double alpha; + + /** Direction cosine β. */ + private final double beta; + + /** Direction cosine γ. */ + private final double gamma; + + /** Transform from body-fixed frame to inertial frame. */ + private final StaticTransform bodyFixedToInertialTransform; + + /** + * Constructor. + * + * @param auxiliaryElements auxiliary elements related to the current orbit + * @param bodyFixedFrame rotating body frame + * @param provider provider for spherical harmonics + * @param parameters values of the force model parameters + */ + DSSTGravityContext(final AuxiliaryElements auxiliaryElements, + final Frame bodyFixedFrame, + final UnnormalizedSphericalHarmonicsProvider provider, + final double[] parameters) { + + super(auxiliaryElements); + + // µ + final double mu = parameters[0]; + + // Semi-major axis + final double a = auxiliaryElements.getSma(); + + // Keplerian Mean Motion + final double absA = FastMath.abs(a); + this.n = FastMath.sqrt(mu / absA) / absA; + + // A = sqrt(µ * |a|) + this.A = FastMath.sqrt(mu * absA); + + // Χ = 1 / B + final double B = auxiliaryElements.getB(); + this.chi = 1. / B; + this.chi2 = chi * chi; + + // Common factors from equinoctial coefficients + // 2 * a / A + this.ax2oA = 2. * a / A; + // B / A + this.BoA = B / A; + // 1 / AB + this.ooAB = 1. / (A * B); + // C / 2AB + this.Co2AB = auxiliaryElements.getC() * ooAB / 2.; + // B / (A * (1 + B)) + this.BoABpo = BoA / (1. + B); + // &mu / a + this.muoa = mu / a; + // R / a + this.roa = provider.getAe() / a; + + // If (centralBodyFrame == null), then centralBodyFrame = orbit frame (see DSSTZonal constructors for more on this). + final Frame internalBodyFixedFrame = bodyFixedFrame == null ? auxiliaryElements.getFrame() : bodyFixedFrame; + + // Transform from body-fixed frame (typically ITRF) to inertial frame + this.bodyFixedToInertialTransform = internalBodyFixedFrame. + getStaticTransformTo(auxiliaryElements.getFrame(), auxiliaryElements.getDate()); + + final Vector3D zB = bodyFixedToInertialTransform.transformVector(Vector3D.PLUS_K); + + // Direction cosines for central body [Eq. 2.1.9-(1)] + this.alpha = Vector3D.dotProduct(zB, auxiliaryElements.getVectorF()); + this.beta = Vector3D.dotProduct(zB, auxiliaryElements.getVectorG()); + this.gamma = Vector3D.dotProduct(zB, auxiliaryElements.getVectorW()); + } + + /** Getter for the a. + * @return the a + */ + public double getA() { + return A; + } + + /** Getter for the chi. + * @return the chi + */ + public double getChi() { + return chi; + } + + /** Getter for the chi2. + * @return the chi2 + */ + public double getChi2() { + return chi2; + } + + /** Getter for the ax2oA. + * @return the ax2oA + */ + public double getAx2oA() { + return ax2oA; + } + + /** ooAB = 1 / (A * B). + * @return the ooAB + */ + public double getOoAB() { + return ooAB; + } + + /** Get B / A. + * @return the boA + */ + public double getBoA() { + return BoA; + } + + /** Get BoABpo = B / A(1 + B). + * @return the boABpo + */ + public double getBoABpo() { + return BoABpo; + } + + /** Get Co2AB = C / 2AB. + * @return the co2AB + */ + public double getCo2AB() { + return Co2AB; + } + + /** Get μ / a. + * @return the muoa + */ + public double getMuoa() { + return muoa; + } + + /** Get roa = R / a. + * @return the roa + */ + public double getRoa() { + return roa; + } + + /** + * Get the Keplerian mean motion. + *

      + * The Keplerian mean motion is computed directly from semi major axis and + * central acceleration constant. + *

      + * @return Keplerian mean motion in radians per second + */ + public double getMeanMotion() { + return n; + } + + /** Get direction cosine α for central body. + * @return α + */ + public double getAlpha() { + return alpha; + } + + /** Get direction cosine β for central body. + * @return β + */ + public double getBeta() { + return beta; + } + + /** Get direction cosine γ for central body. + * @return γ + */ + public double getGamma() { + return gamma; + } + + /** Getter for the bodyFixedToInertialTransform. + * @return the bodyFixedToInertialTransform + */ + public StaticTransform getBodyFixedToInertialTransform() { + return bodyFixedToInertialTransform; + } +} diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTJ2SquaredClosedForm.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTJ2SquaredClosedForm.java index f40f2292e6..40863b4ccd 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTJ2SquaredClosedForm.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTJ2SquaredClosedForm.java @@ -16,9 +16,6 @@ */ package org.orekit.propagation.semianalytical.dsst.forces; -import java.util.Collections; -import java.util.List; - import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; import org.hipparchus.util.MathArrays; @@ -31,6 +28,9 @@ import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; import org.orekit.utils.ParameterDriver; +import java.util.Collections; +import java.util.List; + /** * Second order J2-squared force model. *

      @@ -137,8 +137,7 @@ public > T[] getMeanElementRate(final FieldSpa public List initializeShortPeriodTerms(final AuxiliaryElements auxiliaryElements, final PropagationType type, final double[] parameters) { - // Currently, there is no short periods for J2-squared closed-form - return Collections.emptyList(); + return j2SquaredModel.initializeShortPeriodTerms(auxiliaryElements, type, parameters); } /** {@inheritDoc}. */ @@ -146,8 +145,7 @@ public List initializeShortPeriodTerms(final AuxiliaryElements public > List> initializeShortPeriodTerms(final FieldAuxiliaryElements auxiliaryElements, final PropagationType type, final T[] parameters) { - // Currently, there is no short periods for J2-squared closed-form - return Collections.emptyList(); + return j2SquaredModel.initializeShortPeriodTerms(auxiliaryElements, type, parameters); } /** {@inheritDoc}. */ @@ -165,14 +163,14 @@ public void registerAttitudeProvider(final AttitudeProvider attitudeProvider) { /** {@inheritDoc}. */ @Override public void updateShortPeriodTerms(final double[] parameters, final SpacecraftState... meanStates) { - // Currently, there is no short periods for J2-squared closed-form + j2SquaredModel.updateShortPeriodTerms(parameters, meanStates); } /** {@inheritDoc}. */ @Override @SuppressWarnings("unchecked") public > void updateShortPeriodTerms(final T[] parameters, final FieldSpacecraftState... meanStates) { - // Currently, there is no short periods for J2-squared closed-form + j2SquaredModel.updateShortPeriodTerms(parameters, meanStates); } } diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseral.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseral.java index 8f3df8ddfc..e8e1f724f5 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseral.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseral.java @@ -16,6 +16,17 @@ */ package org.orekit.propagation.semianalytical.dsst.forces; +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.FieldGradient; @@ -58,17 +69,6 @@ import org.orekit.utils.ParameterDriver; import org.orekit.utils.TimeSpanMap; -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.SortedMap; -import java.util.TreeMap; - /** Tesseral contribution to the central body gravitational perturbation. *

      * Only resonant tesserals are considered. @@ -475,9 +475,9 @@ public double[] getMeanElementRate(final SpacecraftState spacecraftState, final UAnddU udu = new UAnddU(spacecraftState.getDate(), context, hansen); // Compute the cross derivative operator : - final double UAlphaGamma = auxiliaryElements.getAlpha() * udu.getdUdGa() - auxiliaryElements.getGamma() * udu.getdUdAl(); - final double UAlphaBeta = auxiliaryElements.getAlpha() * udu.getdUdBe() - auxiliaryElements.getBeta() * udu.getdUdAl(); - final double UBetaGamma = auxiliaryElements.getBeta() * udu.getdUdGa() - auxiliaryElements.getGamma() * udu.getdUdBe(); + final double UAlphaGamma = context.getAlpha() * udu.getdUdGa() - context.getGamma() * udu.getdUdAl(); + final double UAlphaBeta = context.getAlpha() * udu.getdUdBe() - context.getBeta() * udu.getdUdAl(); + final double UBetaGamma = context.getBeta() * udu.getdUdGa() - context.getGamma() * udu.getdUdBe(); final double Uhk = auxiliaryElements.getH() * udu.getdUdk() - auxiliaryElements.getK() * udu.getdUdh(); final double pUagmIqUbgoAB = (auxiliaryElements.getP() * UAlphaGamma - I * auxiliaryElements.getQ() * UBetaGamma) * context.getOoAB(); final double UhkmUabmdUdl = Uhk - UAlphaBeta - udu.getdUdl(); @@ -511,9 +511,9 @@ public > T[] getMeanElementRate(final FieldSpa final FieldUAnddU udu = new FieldUAnddU<>(spacecraftState.getDate(), context, fho); // Compute the cross derivative operator : - final T UAlphaGamma = udu.getdUdGa().multiply(auxiliaryElements.getAlpha()).subtract(udu.getdUdAl().multiply(auxiliaryElements.getGamma())); - final T UAlphaBeta = udu.getdUdBe().multiply(auxiliaryElements.getAlpha()).subtract(udu.getdUdAl().multiply(auxiliaryElements.getBeta())); - final T UBetaGamma = udu.getdUdGa().multiply(auxiliaryElements.getBeta()).subtract(udu.getdUdBe().multiply(auxiliaryElements.getGamma())); + final T UAlphaGamma = udu.getdUdGa().multiply(context.getAlpha()).subtract(udu.getdUdAl().multiply(context.getGamma())); + final T UAlphaBeta = udu.getdUdBe().multiply(context.getAlpha()).subtract(udu.getdUdAl().multiply(context.getBeta())); + final T UBetaGamma = udu.getdUdGa().multiply(context.getBeta()).subtract(udu.getdUdBe().multiply(context.getGamma())); final T Uhk = udu.getdUdk().multiply(auxiliaryElements.getH()).subtract(udu.getdUdh().multiply(auxiliaryElements.getK())); final T pUagmIqUbgoAB = (UAlphaGamma.multiply(auxiliaryElements.getP()).subtract(UBetaGamma.multiply(auxiliaryElements.getQ()).multiply(I))).multiply(context.getOoAB()); final T UhkmUabmdUdl = Uhk.subtract(UAlphaBeta).subtract(udu.getdUdl()); @@ -889,7 +889,7 @@ private double[][] computeNSum(final AbsoluteDate date, // Jacobi l-index from 2.7.1-(15) final int l = FastMath.min(n - m, n - FastMath.abs(s)); // Jacobi polynomial and derivative - final double[] jacobi = JacobiPolynomials.getValueAndDerivative(l, v, w, auxiliaryElements.getGamma()); + final double[] jacobi = JacobiPolynomials.getValueAndDerivative(l, v, w, context.getGamma()); // Geopotential coefficients final double cnm = harmonics.getUnnormalizedCnm(n, m); @@ -1042,7 +1042,7 @@ private > T[][] computeNSum(final FieldAbsolut final int l = FastMath.min(n - m, n - FastMath.abs(s)); // Jacobi polynomial and derivative final FieldGradient jacobi = - JacobiPolynomials.getValue(l, v, w, FieldGradient.variable(1, 0, auxiliaryElements.getGamma())); + JacobiPolynomials.getValue(l, v, w, FieldGradient.variable(1, 0, context.getGamma())); // Geopotential coefficients final T cnm = zero.newInstance(harmonics.getUnnormalizedCnm(n, m)); @@ -1195,10 +1195,10 @@ public void generateCoefficients(final AbsoluteDate date, final DSSTTesseralCont // Compute only if there is at least one non-resonant tesseral if (!nonResOrders.isEmpty() || maxDegreeTesseralSP < 0) { // Gmsj and Hmsj polynomials - ghMSJ = new GHmsjPolynomials(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta(), I); + ghMSJ = new GHmsjPolynomials(auxiliaryElements.getK(), auxiliaryElements.getH(), context.getAlpha(), context.getBeta(), I); // GAMMAmns function - gammaMNS = new GammaMnsFunction(maxDegree, auxiliaryElements.getGamma(), I); + gammaMNS = new GammaMnsFunction(maxDegree, context.getGamma(), I); final int maxRoaPower = FastMath.max(maxDegreeTesseralSP, maxDegreeMdailyTesseralSP); @@ -1299,28 +1299,29 @@ private void buildFourierCoefficients(final AbsoluteDate date, dRdGaSin += nSumSneg[6][1]; } } - dRdaCos *= -context.getMoa() / auxiliaryElements.getSma(); - dRdaSin *= -context.getMoa() / auxiliaryElements.getSma(); - dRdhCos *= context.getMoa(); - dRdhSin *= context.getMoa(); - dRdkCos *= context.getMoa(); - dRdkSin *= context.getMoa(); - dRdlCos *= context.getMoa(); - dRdlSin *= context.getMoa(); - dRdAlCos *= context.getMoa(); - dRdAlSin *= context.getMoa(); - dRdBeCos *= context.getMoa(); - dRdBeSin *= context.getMoa(); - dRdGaCos *= context.getMoa(); - dRdGaSin *= context.getMoa(); + final double muOnA = context.getMuoa(); + dRdaCos *= -muOnA / auxiliaryElements.getSma(); + dRdaSin *= -muOnA / auxiliaryElements.getSma(); + dRdhCos *= muOnA; + dRdhSin *= muOnA; + dRdkCos *= muOnA; + dRdkSin *= muOnA; + dRdlCos *= muOnA; + dRdlSin *= muOnA; + dRdAlCos *= muOnA; + dRdAlSin *= muOnA; + dRdBeCos *= muOnA; + dRdBeSin *= muOnA; + dRdGaCos *= muOnA; + dRdGaSin *= muOnA; // Compute the cross derivative operator : - final double RAlphaGammaCos = auxiliaryElements.getAlpha() * dRdGaCos - auxiliaryElements.getGamma() * dRdAlCos; - final double RAlphaGammaSin = auxiliaryElements.getAlpha() * dRdGaSin - auxiliaryElements.getGamma() * dRdAlSin; - final double RAlphaBetaCos = auxiliaryElements.getAlpha() * dRdBeCos - auxiliaryElements.getBeta() * dRdAlCos; - final double RAlphaBetaSin = auxiliaryElements.getAlpha() * dRdBeSin - auxiliaryElements.getBeta() * dRdAlSin; - final double RBetaGammaCos = auxiliaryElements.getBeta() * dRdGaCos - auxiliaryElements.getGamma() * dRdBeCos; - final double RBetaGammaSin = auxiliaryElements.getBeta() * dRdGaSin - auxiliaryElements.getGamma() * dRdBeSin; + final double RAlphaGammaCos = context.getAlpha() * dRdGaCos - context.getGamma() * dRdAlCos; + final double RAlphaGammaSin = context.getAlpha() * dRdGaSin - context.getGamma() * dRdAlSin; + final double RAlphaBetaCos = context.getAlpha() * dRdBeCos - context.getBeta() * dRdAlCos; + final double RAlphaBetaSin = context.getAlpha() * dRdBeSin - context.getBeta() * dRdAlSin; + final double RBetaGammaCos = context.getBeta() * dRdGaCos - context.getGamma() * dRdBeCos; + final double RBetaGammaSin = context.getBeta() * dRdGaSin - context.getGamma() * dRdBeSin; final double RhkCos = auxiliaryElements.getH() * dRdkCos - auxiliaryElements.getK() * dRdhCos; final double RhkSin = auxiliaryElements.getH() * dRdkSin - auxiliaryElements.getK() * dRdhSin; final double pRagmIqRbgoABCos = (auxiliaryElements.getP() * RAlphaGammaCos - I * auxiliaryElements.getQ() * RBetaGammaCos) * context.getOoAB(); @@ -1457,10 +1458,10 @@ public void generateCoefficients(final FieldAbsoluteDate date, // Compute only if there is at least one non-resonant tesseral if (!nonResOrders.isEmpty() || maxDegreeTesseralSP < 0) { // Gmsj and Hmsj polynomials - ghMSJ = new FieldGHmsjPolynomials<>(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta(), I, field); + ghMSJ = new FieldGHmsjPolynomials<>(auxiliaryElements.getK(), auxiliaryElements.getH(), context.getAlpha(), context.getBeta(), I, field); // GAMMAmns function - gammaMNS = new FieldGammaMnsFunction<>(maxDegree, auxiliaryElements.getGamma(), I, field); + gammaMNS = new FieldGammaMnsFunction<>(maxDegree, context.getGamma(), I, field); final int maxRoaPower = FastMath.max(maxDegreeTesseralSP, maxDegreeMdailyTesseralSP); @@ -1567,28 +1568,29 @@ private void buildFourierCoefficients(final FieldAbsoluteDate date, dRdGaSin = dRdGaSin.add(nSumSneg[6][1]); } } - dRdaCos = dRdaCos.multiply(context.getMoa().negate().divide(auxiliaryElements.getSma())); - dRdaSin = dRdaSin.multiply(context.getMoa().negate().divide(auxiliaryElements.getSma())); - dRdhCos = dRdhCos.multiply(context.getMoa()); - dRdhSin = dRdhSin.multiply(context.getMoa()); - dRdkCos = dRdkCos.multiply(context.getMoa()); - dRdkSin = dRdkSin.multiply(context.getMoa()); - dRdlCos = dRdlCos.multiply(context.getMoa()); - dRdlSin = dRdlSin.multiply(context.getMoa()); - dRdAlCos = dRdAlCos.multiply(context.getMoa()); - dRdAlSin = dRdAlSin.multiply(context.getMoa()); - dRdBeCos = dRdBeCos.multiply(context.getMoa()); - dRdBeSin = dRdBeSin.multiply(context.getMoa()); - dRdGaCos = dRdGaCos.multiply(context.getMoa()); - dRdGaSin = dRdGaSin.multiply(context.getMoa()); + final T muOnA = context.getMuoa(); + dRdaCos = dRdaCos.multiply(muOnA.negate().divide(auxiliaryElements.getSma())); + dRdaSin = dRdaSin.multiply(muOnA.negate().divide(auxiliaryElements.getSma())); + dRdhCos = dRdhCos.multiply(muOnA); + dRdhSin = dRdhSin.multiply(muOnA); + dRdkCos = dRdkCos.multiply(muOnA); + dRdkSin = dRdkSin.multiply(muOnA); + dRdlCos = dRdlCos.multiply(muOnA); + dRdlSin = dRdlSin.multiply(muOnA); + dRdAlCos = dRdAlCos.multiply(muOnA); + dRdAlSin = dRdAlSin.multiply(muOnA); + dRdBeCos = dRdBeCos.multiply(muOnA); + dRdBeSin = dRdBeSin.multiply(muOnA); + dRdGaCos = dRdGaCos.multiply(muOnA); + dRdGaSin = dRdGaSin.multiply(muOnA); // Compute the cross derivative operator : - final T RAlphaGammaCos = auxiliaryElements.getAlpha().multiply(dRdGaCos).subtract(auxiliaryElements.getGamma().multiply(dRdAlCos)); - final T RAlphaGammaSin = auxiliaryElements.getAlpha().multiply(dRdGaSin).subtract(auxiliaryElements.getGamma().multiply(dRdAlSin)); - final T RAlphaBetaCos = auxiliaryElements.getAlpha().multiply(dRdBeCos).subtract(auxiliaryElements.getBeta().multiply(dRdAlCos)); - final T RAlphaBetaSin = auxiliaryElements.getAlpha().multiply(dRdBeSin).subtract(auxiliaryElements.getBeta().multiply(dRdAlSin)); - final T RBetaGammaCos = auxiliaryElements.getBeta().multiply(dRdGaCos).subtract(auxiliaryElements.getGamma().multiply(dRdBeCos)); - final T RBetaGammaSin = auxiliaryElements.getBeta().multiply(dRdGaSin).subtract(auxiliaryElements.getGamma().multiply(dRdBeSin)); + final T RAlphaGammaCos = context.getAlpha().multiply(dRdGaCos).subtract(context.getGamma().multiply(dRdAlCos)); + final T RAlphaGammaSin = context.getAlpha().multiply(dRdGaSin).subtract(context.getGamma().multiply(dRdAlSin)); + final T RAlphaBetaCos = context.getAlpha().multiply(dRdBeCos).subtract(context.getBeta().multiply(dRdAlCos)); + final T RAlphaBetaSin = context.getAlpha().multiply(dRdBeSin).subtract(context.getBeta().multiply(dRdAlSin)); + final T RBetaGammaCos = context.getBeta().multiply(dRdGaCos).subtract(context.getGamma().multiply(dRdBeCos)); + final T RBetaGammaSin = context.getBeta().multiply(dRdGaSin).subtract(context.getGamma().multiply(dRdBeSin)); final T RhkCos = auxiliaryElements.getH().multiply(dRdkCos).subtract(auxiliaryElements.getK().multiply(dRdhCos)); final T RhkSin = auxiliaryElements.getH().multiply(dRdkSin).subtract(auxiliaryElements.getK().multiply(dRdhSin)); final T pRagmIqRbgoABCos = (auxiliaryElements.getP().multiply(RAlphaGammaCos).subtract(auxiliaryElements.getQ().multiply(RBetaGammaCos).multiply(I))).multiply(context.getOoAB()); @@ -2325,10 +2327,10 @@ private class UAnddU { // Compute only if there is at least one resonant tesseral if (!resOrders.isEmpty()) { // Gmsj and Hmsj polynomials - final GHmsjPolynomials ghMSJ = new GHmsjPolynomials(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta(), I); + final GHmsjPolynomials ghMSJ = new GHmsjPolynomials(auxiliaryElements.getK(), auxiliaryElements.getH(), context.getAlpha(), context.getBeta(), I); // GAMMAmns function - final GammaMnsFunction gammaMNS = new GammaMnsFunction(maxDegree, auxiliaryElements.getGamma(), I); + final GammaMnsFunction gammaMNS = new GammaMnsFunction(maxDegree, context.getGamma(), I); // R / a up to power degree final double[] roaPow = new double[maxDegree + 1]; @@ -2425,13 +2427,14 @@ private class UAnddU { dUdGa += cosPhi * dUdGaCos + sinPhi * dUdGaSin; } - this.dUda = dUda * (-context.getMoa() / auxiliaryElements.getSma()); - this.dUdh = dUdh * context.getMoa(); - this.dUdk = dUdk * context.getMoa(); - this.dUdl = dUdl * context.getMoa(); - this.dUdAl = dUdAl * context.getMoa(); - this.dUdBe = dUdBe * context.getMoa(); - this.dUdGa = dUdGa * context.getMoa(); + final double muOnA = context.getMuoa(); + this.dUda = dUda * (-muOnA / auxiliaryElements.getSma()); + this.dUdh = dUdh * muOnA; + this.dUdk = dUdk * muOnA; + this.dUdl = dUdl * muOnA; + this.dUdAl = dUdAl * muOnA; + this.dUdBe = dUdBe * muOnA; + this.dUdGa = dUdGa * muOnA; } } @@ -2550,10 +2553,10 @@ private class FieldUAnddU > { // Compute only if there is at least one resonant tesseral if (!resOrders.isEmpty()) { // Gmsj and Hmsj polynomials - final FieldGHmsjPolynomials ghMSJ = new FieldGHmsjPolynomials<>(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta(), I, field); + final FieldGHmsjPolynomials ghMSJ = new FieldGHmsjPolynomials<>(auxiliaryElements.getK(), auxiliaryElements.getH(), context.getAlpha(), context.getBeta(), I, field); // GAMMAmns function - final FieldGammaMnsFunction gammaMNS = new FieldGammaMnsFunction<>(maxDegree, auxiliaryElements.getGamma(), I, field); + final FieldGammaMnsFunction gammaMNS = new FieldGammaMnsFunction<>(maxDegree, context.getGamma(), I, field); // R / a up to power degree final T[] roaPow = MathArrays.buildArray(field, maxDegree + 1); @@ -2650,16 +2653,15 @@ private class FieldUAnddU > { dUdGa = dUdGa.add(dUdGaCos.multiply(cosPhi).add(dUdGaSin.multiply(sinPhi))); } - dUda = dUda.multiply(context.getMoa().divide(auxiliaryElements.getSma())).negate(); - dUdh = dUdh.multiply(context.getMoa()); - dUdk = dUdk.multiply(context.getMoa()); - dUdl = dUdl.multiply(context.getMoa()); - dUdAl = dUdAl.multiply(context.getMoa()); - dUdBe = dUdBe.multiply(context.getMoa()); - dUdGa = dUdGa.multiply(context.getMoa()); - + final T muOnA = context.getMuoa(); + dUda = dUda.multiply(muOnA.divide(auxiliaryElements.getSma())).negate(); + dUdh = dUdh.multiply(muOnA); + dUdk = dUdk.multiply(muOnA); + dUdl = dUdl.multiply(muOnA); + dUdAl = dUdAl.multiply(muOnA); + dUdBe = dUdBe.multiply(muOnA); + dUdGa = dUdGa.multiply(muOnA); } - } /** Return value of dU / da. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseralContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseralContext.java index 2f5f58aca1..3f1ab56434 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseralContext.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseralContext.java @@ -18,6 +18,7 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathUtils; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; import org.orekit.frames.Frame; import org.orekit.frames.StaticTransform; @@ -32,7 +33,7 @@ * @author Bryan Cazabonne * @since 10.0 */ -public class DSSTTesseralContext extends ForceModelContext { +public class DSSTTesseralContext extends DSSTGravityContext { /** Retrograde factor I. *

      @@ -49,47 +50,12 @@ public class DSSTTesseralContext extends ForceModelContext { */ private static final int I = 1; - /** A = sqrt(μ * a). */ - private double A; - - // Common factors for potential computation - /** Χ = 1 / sqrt(1 - e²) = 1 / B. */ - private double chi; - - /** Χ². */ - private double chi2; - /** Central body rotation angle θ. */ private double theta; - // Common factors from equinoctial coefficients - /** 2 * a / A . */ - private double ax2oA; - - /** 1 / (A * B) . */ - private double ooAB; - - /** B / A . */ - private double BoA; - - /** B / (A * (1 + B)) . */ - private double BoABpo; - - /** C / (2 * A * B) . */ - private double Co2AB; - - /** μ / a . */ - private double moa; - - /** R / a . */ - private double roa; - /** ecc². */ private double e2; - /** Keplerian mean motion. */ - private double n; - /** Keplerian period. */ private double period; @@ -113,55 +79,24 @@ public class DSSTTesseralContext extends ForceModelContext { final double bodyPeriod, final double[] parameters) { - super(auxiliaryElements); - - final double mu = parameters[0]; - - // Keplerian Mean Motion - final double absA = FastMath.abs(auxiliaryElements.getSma()); - n = FastMath.sqrt(mu / absA) / absA; + super(auxiliaryElements, centralBodyFrame, provider, parameters); // Keplerian period final double a = auxiliaryElements.getSma(); - period = (a < 0) ? Double.POSITIVE_INFINITY : 2.0 * FastMath.PI * a * FastMath.sqrt(a / mu); - - A = FastMath.sqrt(mu * auxiliaryElements.getSma()); + period = (a < 0) ? Double.POSITIVE_INFINITY : MathUtils.TWO_PI / getMeanMotion(); // Eccentricity square e2 = auxiliaryElements.getEcc() * auxiliaryElements.getEcc(); // Central body rotation angle from equation 2.7.1-(3)(4). - final StaticTransform t = centralBodyFrame.getStaticTransformTo( - auxiliaryElements.getFrame(), - auxiliaryElements.getDate()); + final StaticTransform t = getBodyFixedToInertialTransform(); final Vector3D xB = t.transformVector(Vector3D.PLUS_I); final Vector3D yB = t.transformVector(Vector3D.PLUS_J); theta = FastMath.atan2(-auxiliaryElements.getVectorF().dotProduct(yB) + I * auxiliaryElements.getVectorG().dotProduct(xB), auxiliaryElements.getVectorF().dotProduct(xB) + I * auxiliaryElements.getVectorG().dotProduct(yB)); - // Common factors from equinoctial coefficients - // 2 * a / A - ax2oA = 2. * auxiliaryElements.getSma() / A; - // B / A - BoA = auxiliaryElements.getB() / A; - // 1 / AB - ooAB = 1. / (A * auxiliaryElements.getB()); - // C / 2AB - Co2AB = auxiliaryElements.getC() * ooAB / 2.; - // B / (A * (1 + B)) - BoABpo = BoA / (1. + auxiliaryElements.getB()); - // &mu / a - moa = mu / auxiliaryElements.getSma(); - // R / a - roa = provider.getAe() / auxiliaryElements.getSma(); - - // Χ = 1 / B - chi = 1. / auxiliaryElements.getB(); - chi2 = chi * chi; - // Ratio of satellite to central body periods to define resonant terms ratio = period / bodyPeriod; - } /** Get ecc². @@ -179,76 +114,14 @@ public double getTheta() { return theta; } - /** - * Get ax2oA = 2 * a / A . - * @return ax2oA - */ - public double getAx2oA() { - return ax2oA; - } - - /** - * Get Χ = 1 / sqrt(1 - e²) = 1 / B. - * @return chi - */ - public double getChi() { - return chi; - } - - /** - * Get Χ². - * @return chi2 - */ - public double getChi2() { - return chi2; - } - - /** - * Get B / A. - * @return BoA - */ - public double getBoA() { - return BoA; - } - - /** - * Get ooAB = 1 / (A * B). - * @return ooAB - */ - public double getOoAB() { - return ooAB; - } - - /** - * Get Co2AB = C / 2AB. - * @return Co2AB - */ - public double getCo2AB() { - return Co2AB; - } - - /** - * Get BoABpo = B / A(1 + B). - * @return BoABpo - */ - public double getBoABpo() { - return BoABpo; - } - /** * Get μ / a . * @return moa + *@deprecated since 12.2 Use getMuoa() instead */ + @Deprecated public double getMoa() { - return moa; - } - - /** - * Get roa = R / a. - * @return roa - */ - public double getRoa() { - return roa; + return getMuoa(); } /** @@ -264,18 +137,6 @@ public double getOrbitPeriod() { return period; } - /** - * Get the Keplerian mean motion. - *

      - * The Keplerian mean motion is computed directly from semi major axis and - * central acceleration constant. - *

      - * @return Keplerian mean motion in radians per second - */ - public double getMeanMotion() { - return n; - } - /** * Get the ratio of satellite period to central body rotation period. * @return ratio diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonal.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonal.java index d888f628a3..27fccf723f 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonal.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonal.java @@ -16,6 +16,16 @@ */ package org.orekit.propagation.semianalytical.dsst.forces; +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; + import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; import org.hipparchus.exception.LocalizedCoreFormats; @@ -29,6 +39,7 @@ import org.orekit.errors.OrekitInternalError; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics; +import org.orekit.frames.Frame; import org.orekit.orbits.FieldOrbit; import org.orekit.orbits.Orbit; import org.orekit.propagation.FieldSpacecraftState; @@ -55,16 +66,6 @@ import org.orekit.utils.ParameterDriver; import org.orekit.utils.TimeSpanMap; -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.SortedMap; - /** Zonal contribution to the central body gravitational perturbation. * * @author Romain Di Costanzo @@ -111,6 +112,9 @@ public class DSSTZonal implements DSSTForceModel { /** Provider for spherical harmonics. */ private final UnnormalizedSphericalHarmonicsProvider provider; + /** Central body rotating frame (fixed with respect to body). */ + private final Frame bodyFixedFrame; + /** Maximal degree to consider for harmonics potential. */ private final int maxDegree; @@ -145,6 +149,37 @@ public class DSSTZonal implements DSSTForceModel { private Map, FieldHansenObjects> fieldHansen; /** Constructor with default reference values. + *

      + * When this constructor is used, maximum allowed values are used + * for the short periodic coefficients: + *

      + *
        + *
      • {@link #maxDegreeShortPeriodics} is set to {@code provider.getMaxDegree()}
      • + *
      • {@link #maxEccPowShortPeriodics} is set to {@code min(provider.getMaxDegree() - 1, 4)}. + * This parameter should not exceed 4 as higher values will exceed computer capacity
      • + *
      • {@link #maxFrequencyShortPeriodics} is set to {@code 2 * provider.getMaxDegree() + 1}
      • + *
      + * @param bodyFixedFrame rotating body frame + *

      If set, this frame will be used to compute the Earth pole direction and the zonal contribution. + * If not, the inertial frame from the propagator will be used.
      + * Ideally this frame should be always set to ITRF. + * However, using TOD is advised since it's generally a good trade-off between performance and precision. + * + * @param provider provider for spherical harmonics + * @since 12.1 + */ + public DSSTZonal(final Frame bodyFixedFrame, final UnnormalizedSphericalHarmonicsProvider provider) { + this(bodyFixedFrame, provider, provider.getMaxDegree(), FastMath.min(4, provider.getMaxDegree() - 1), 2 * provider.getMaxDegree() + 1); + } + + /** + * Constructor with bodyFixedFrame = orbit frame, and default reference values. + *

      + * Kept for compatibility with anterior versions. + *

      + * Setting bodyFixedFrame to null will lead to large errors if the orbit frame is far from Earth rotating frame (GCRF, EME2000...). + * The error gets smaller as the orbit frame gets closer to Earth rotating frame (MOD, then TOD). + * @see issue-1104 on the forge *

      * When this constructor is used, maximum allowed values are used * for the short periodic coefficients: @@ -159,10 +194,16 @@ public class DSSTZonal implements DSSTForceModel { * @since 10.1 */ public DSSTZonal(final UnnormalizedSphericalHarmonicsProvider provider) { - this(provider, provider.getMaxDegree(), FastMath.min(4, provider.getMaxDegree() - 1), 2 * provider.getMaxDegree() + 1); + this(null, provider); } - /** Simple constructor. + /** Constructor. + * @param bodyFixedFrame rotating body frame + *

      If set, this frame will be used to compute the Earth pole direction and the zonal contribution. + * If not, the inertial frame from the propagator will be used.
      + * Ideally this frame should be always set to ITRF. + * However, using TOD is advised since it's a good trade-off between performance and precision. + * * @param provider provider for spherical harmonics * @param maxDegreeShortPeriodics maximum degree to consider for short periodics zonal harmonics potential * (must be between 2 and {@code provider.getMaxDegree()}) @@ -171,9 +212,10 @@ public DSSTZonal(final UnnormalizedSphericalHarmonicsProvider provider) { * values will exceed computer capacity) * @param maxFrequencyShortPeriodics maximum frequency in true longitude for short periodic computations * (must be between 1 and {@code 2 * maxDegreeShortPeriodics + 1}) - * @since 7.2 + * @since 12.1 */ - public DSSTZonal(final UnnormalizedSphericalHarmonicsProvider provider, + public DSSTZonal(final Frame bodyFixedFrame, + final UnnormalizedSphericalHarmonicsProvider provider, final int maxDegreeShortPeriodics, final int maxEccPowShortPeriodics, final int maxFrequencyShortPeriodics) { @@ -182,6 +224,9 @@ public DSSTZonal(final UnnormalizedSphericalHarmonicsProvider provider, provider.getMu(), MU_SCALE, 0.0, Double.POSITIVE_INFINITY); + // Central body rotating frame + this.bodyFixedFrame = bodyFixedFrame; + // Vns coefficients this.Vns = CoefficientsFactory.computeVns(provider.getMaxDegree() + 1); @@ -202,7 +247,31 @@ public DSSTZonal(final UnnormalizedSphericalHarmonicsProvider provider, zonalFieldSPCoefs = new HashMap<>(); fieldHansen = new HashMap<>(); + } + /** + * Constructor with bodyFixedFrame = orbit frame. + *

      + * Added for retro-compatibility with anterior versions for initialization. + *

      + * Setting bodyFixedFrame to null will lead to large errors if the orbit frame is far from Earth rotating frame (GCRF, EME2000...). + * The error gets smaller as the orbit frame gets closer to Earth rotating frame (MOD, then TOD). + * @see issue-1104 on the forge + * @param provider provider for spherical harmonics + * @param maxDegreeShortPeriodics maximum degree to consider for short periodics zonal harmonics potential + * (must be between 2 and {@code provider.getMaxDegree()}) + * @param maxEccPowShortPeriodics maximum power of the eccentricity to be used in short periodic computations + * (must be between 0 and {@code maxDegreeShortPeriodics - 1}, but should typically not exceed 4 as higher + * values will exceed computer capacity) + * @param maxFrequencyShortPeriodics maximum frequency in true longitude for short periodic computations + * (must be between 1 and {@code 2 * maxDegreeShortPeriodics + 1}) + * @since 7.2 + */ + public DSSTZonal(final UnnormalizedSphericalHarmonicsProvider provider, + final int maxDegreeShortPeriodics, + final int maxEccPowShortPeriodics, + final int maxFrequencyShortPeriodics) { + this(null, provider, maxDegreeShortPeriodics, maxEccPowShortPeriodics, maxFrequencyShortPeriodics); } /** Check an index range. @@ -313,7 +382,7 @@ public > List> initia */ private void computeMeanElementsTruncations(final AuxiliaryElements auxiliaryElements, final double[] parameters) { - final DSSTZonalContext context = new DSSTZonalContext(auxiliaryElements, provider, parameters); + final DSSTZonalContext context = new DSSTZonalContext(auxiliaryElements, bodyFixedFrame, provider, parameters); //Compute the max eccentricity power for the mean element rate expansion if (maxDegree == 2) { maxEccPowMeanElements = 0; @@ -326,12 +395,12 @@ private void computeMeanElementsTruncations(final AuxiliaryElements auxiliaryEle double xmuran = parameters[0] / auxiliaryElements.getSma(); // Set a lower bound for eccentricity final double eo2 = FastMath.max(0.0025, auxiliaryElements.getEcc() / 2.); - final double x2o2 = context.getXX() / 2.; + final double x2o2 = context.getChi2() / 2.; final double[] eccPwr = new double[maxDegree + 1]; final double[] chiPwr = new double[maxDegree + 1]; final double[] hafPwr = new double[maxDegree + 1]; eccPwr[0] = 1.; - chiPwr[0] = context.getX(); + chiPwr[0] = context.getChi(); hafPwr[0] = 1.; for (int i = 1; i <= maxDegree; i++) { eccPwr[i] = eccPwr[i - 1] * eo2; @@ -369,13 +438,13 @@ private void computeMeanElementsTruncations(final AuxiliaryElements auxiliaryEle term = csnm * xmuran * (CombinatoricsUtils.factorialDouble(maxDeg - l) / (CombinatoricsUtils.factorialDouble(maxDeg - m))) * (CombinatoricsUtils.factorialDouble(maxDeg + l) / (CombinatoricsUtils.factorialDouble(nsld2) * CombinatoricsUtils.factorialDouble(nsld2 + l))) * - eccPwr[l] * UpperBounds.getDnl(context.getXX(), chiPwr[l], maxDeg, l) * - (UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, l, m, 1, I) + UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, l, m, -1, I)); + eccPwr[l] * UpperBounds.getDnl(context.getChi2(), chiPwr[l], maxDeg, l) * + (UpperBounds.getRnml(context.getGamma(), maxDeg, l, m, 1, I) + UpperBounds.getRnml(context.getGamma(), maxDeg, l, m, -1, I)); } else { term = csnm * xmuran * (CombinatoricsUtils.factorialDouble(maxDeg + m) / (CombinatoricsUtils.factorialDouble(nsld2) * CombinatoricsUtils.factorialDouble(nsld2 + l))) * - eccPwr[l] * hafPwr[m - l] * UpperBounds.getDnl(context.getXX(), chiPwr[l], maxDeg, l) * - (UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, m, l, 1, I) + UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, m, l, -1, I)); + eccPwr[l] * hafPwr[m - l] * UpperBounds.getDnl(context.getChi2(), chiPwr[l], maxDeg, l) * + (UpperBounds.getRnml(context.getGamma(), maxDeg, m, l, 1, I) + UpperBounds.getRnml(context.getGamma(), maxDeg, m, l, -1, I)); } // Is the current spherical harmonic term bigger than the truncation tolerance ? if (term >= TRUNCATION_TOLERANCE) { @@ -415,11 +484,11 @@ private void computeMeanElementsTruncations(final AuxiliaryElements auxiliaryEle * @param field field used by default */ private > void computeMeanElementsTruncations(final FieldAuxiliaryElements auxiliaryElements, - final T[] parameters, - final Field field) { + final T[] parameters, + final Field field) { final T zero = field.getZero(); - final FieldDSSTZonalContext context = new FieldDSSTZonalContext<>(auxiliaryElements, provider, parameters); + final FieldDSSTZonalContext context = new FieldDSSTZonalContext<>(auxiliaryElements, bodyFixedFrame, provider, parameters); //Compute the max eccentricity power for the mean element rate expansion if (maxDegree == 2) { maxEccPowMeanElements = 0; @@ -432,12 +501,12 @@ private > void computeMeanElementsTruncations( T xmuran = parameters[0].divide(auxiliaryElements.getSma()); // Set a lower bound for eccentricity final T eo2 = FastMath.max(zero.newInstance(0.0025), auxiliaryElements.getEcc().divide(2.)); - final T x2o2 = context.getXX().divide(2.); + final T x2o2 = context.getChi2().divide(2.); final T[] eccPwr = MathArrays.buildArray(field, maxDegree + 1); final T[] chiPwr = MathArrays.buildArray(field, maxDegree + 1); final T[] hafPwr = MathArrays.buildArray(field, maxDegree + 1); eccPwr[0] = zero.newInstance(1.); - chiPwr[0] = context.getX(); + chiPwr[0] = context.getChi(); hafPwr[0] = zero.newInstance(1.); for (int i = 1; i <= maxDegree; i++) { eccPwr[i] = eccPwr[i - 1].multiply(eo2); @@ -475,13 +544,13 @@ private > void computeMeanElementsTruncations( term = csnm.multiply(xmuran). multiply((CombinatoricsUtils.factorialDouble(maxDeg - l) / (CombinatoricsUtils.factorialDouble(maxDeg - m))) * (CombinatoricsUtils.factorialDouble(maxDeg + l) / (CombinatoricsUtils.factorialDouble(nsld2) * CombinatoricsUtils.factorialDouble(nsld2 + l)))). - multiply(eccPwr[l]).multiply(UpperBounds.getDnl(context.getXX(), chiPwr[l], maxDeg, l)). - multiply(UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, l, m, 1, I).add(UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, l, m, -1, I))); + multiply(eccPwr[l]).multiply(UpperBounds.getDnl(context.getChi2(), chiPwr[l], maxDeg, l)). + multiply(UpperBounds.getRnml(context.getGamma(), maxDeg, l, m, 1, I).add(UpperBounds.getRnml(context.getGamma(), maxDeg, l, m, -1, I))); } else { term = csnm.multiply(xmuran). multiply(CombinatoricsUtils.factorialDouble(maxDeg + m) / (CombinatoricsUtils.factorialDouble(nsld2) * CombinatoricsUtils.factorialDouble(nsld2 + l))). - multiply(eccPwr[l]).multiply(hafPwr[m - l]).multiply(UpperBounds.getDnl(context.getXX(), chiPwr[l], maxDeg, l)). - multiply(UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, m, l, 1, I).add(UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, m, l, -1, I))); + multiply(eccPwr[l]).multiply(hafPwr[m - l]).multiply(UpperBounds.getDnl(context.getChi2(), chiPwr[l], maxDeg, l)). + multiply(UpperBounds.getRnml(context.getGamma(), maxDeg, m, l, 1, I).add(UpperBounds.getRnml(context.getGamma(), maxDeg, m, l, -1, I))); } // Is the current spherical harmonic term bigger than the truncation tolerance ? if (term.getReal() >= TRUNCATION_TOLERANCE) { @@ -523,7 +592,7 @@ private > void computeMeanElementsTruncations( * @return new force model context */ private DSSTZonalContext initializeStep(final AuxiliaryElements auxiliaryElements, final double[] parameters) { - return new DSSTZonalContext(auxiliaryElements, provider, parameters); + return new DSSTZonalContext(auxiliaryElements, bodyFixedFrame, provider, parameters); } /** Performs initialization at each integration step for the current force model. @@ -537,7 +606,7 @@ private DSSTZonalContext initializeStep(final AuxiliaryElements auxiliaryElement */ private > FieldDSSTZonalContext initializeStep(final FieldAuxiliaryElements auxiliaryElements, final T[] parameters) { - return new FieldDSSTZonalContext<>(auxiliaryElements, provider, parameters); + return new FieldDSSTZonalContext<>(auxiliaryElements, bodyFixedFrame, provider, parameters); } /** {@inheritDoc} */ @@ -589,9 +658,9 @@ private double[] computeMeanElementRates(final DSSTZonalContext context, // Compute cross derivatives [Eq. 2.2-(8)] // U(alpha,gamma) = alpha * dU/dgamma - gamma * dU/dalpha - final double UAlphaGamma = auxiliaryElements.getAlpha() * udu.getdUdGa() - auxiliaryElements.getGamma() * udu.getdUdAl(); + final double UAlphaGamma = context.getAlpha() * udu.getdUdGa() - context.getGamma() * udu.getdUdAl(); // U(beta,gamma) = beta * dU/dgamma - gamma * dU/dbeta - final double UBetaGamma = auxiliaryElements.getBeta() * udu.getdUdGa() - auxiliaryElements.getGamma() * udu.getdUdBe(); + final double UBetaGamma = context.getBeta() * udu.getdUdGa() - context.getGamma() * udu.getdUdBe(); // Common factor final double pUAGmIqUBGoAB = (auxiliaryElements.getP() * UAlphaGamma - I * auxiliaryElements.getQ() * UBetaGamma) * context.getOoAB(); @@ -599,9 +668,9 @@ private double[] computeMeanElementRates(final DSSTZonalContext context, final double da = 0.; final double dh = context.getBoA() * udu.getdUdk() + auxiliaryElements.getK() * pUAGmIqUBGoAB; final double dk = -context.getBoA() * udu.getdUdh() - auxiliaryElements.getH() * pUAGmIqUBGoAB; - final double dp = context.getMCo2AB() * UBetaGamma; - final double dq = context.getMCo2AB() * UAlphaGamma * I; - final double dM = context.getM2aoA() * udu.getdUda() + context.getBoABpo() * (auxiliaryElements.getH() * udu.getdUdh() + auxiliaryElements.getK() * udu.getdUdk()) + pUAGmIqUBGoAB; + final double dp = -context.getCo2AB() * UBetaGamma; + final double dq = -context.getCo2AB() * UAlphaGamma * I; + final double dM = -context.getAx2oA() * udu.getdUda() + context.getBoABpo() * (auxiliaryElements.getH() * udu.getdUdh() + auxiliaryElements.getK() * udu.getdUdk()) + pUAGmIqUBGoAB; return new double[] {da, dk, dh, dq, dp, dM}; } @@ -625,9 +694,9 @@ private > T[] computeMeanElementRates(final Fi // Compute cross derivatives [Eq. 2.2-(8)] // U(alpha,gamma) = alpha * dU/dgamma - gamma * dU/dalpha - final T UAlphaGamma = udu.getdUdGa().multiply(auxiliaryElements.getAlpha()).subtract(udu.getdUdAl().multiply(auxiliaryElements.getGamma())); + final T UAlphaGamma = udu.getdUdGa().multiply(context.getAlpha()).subtract(udu.getdUdAl().multiply(context.getGamma())); // U(beta,gamma) = beta * dU/dgamma - gamma * dU/dbeta - final T UBetaGamma = udu.getdUdGa().multiply(auxiliaryElements.getBeta()).subtract(udu.getdUdBe().multiply(auxiliaryElements.getGamma())); + final T UBetaGamma = udu.getdUdGa().multiply(context.getBeta()).subtract(udu.getdUdBe().multiply(context.getGamma())); // Common factor final T pUAGmIqUBGoAB = (UAlphaGamma.multiply(auxiliaryElements.getP()).subtract(UBetaGamma.multiply(I).multiply(auxiliaryElements.getQ()))).multiply(context.getOoAB()); @@ -635,9 +704,9 @@ private > T[] computeMeanElementRates(final Fi final T da = field.getZero(); final T dh = udu.getdUdk().multiply(context.getBoA()).add(pUAGmIqUBGoAB.multiply(auxiliaryElements.getK())); final T dk = (udu.getdUdh().multiply(context.getBoA()).negate()).subtract(pUAGmIqUBGoAB.multiply(auxiliaryElements.getH())); - final T dp = UBetaGamma.multiply(context.getMCo2AB()); - final T dq = UAlphaGamma.multiply(context.getMCo2AB()).multiply(I); - final T dM = pUAGmIqUBGoAB.add(udu.getdUda().multiply(context.getM2aoA())).add((udu.getdUdh().multiply(auxiliaryElements.getH()).add(udu.getdUdk().multiply(auxiliaryElements.getK()))).multiply(context.getBoABpo())); + final T dp = UBetaGamma.multiply(context.getCo2AB().negate()); + final T dq = UAlphaGamma.multiply(context.getCo2AB().negate()).multiply(I); + final T dM = pUAGmIqUBGoAB.add(udu.getdUda().multiply(context.getAx2oA().negate())).add((udu.getdUdh().multiply(auxiliaryElements.getH()).add(udu.getdUdk().multiply(auxiliaryElements.getK()))).multiply(context.getBoABpo())); final T[] elements = MathArrays.buildArray(field, 6); elements[0] = da; @@ -1004,19 +1073,19 @@ private void computeCijSijCoefficients(final AbsoluteDate date, final Slot slot, if (isBetween(j, 1, 2 * nMax - 1) && j < cjsj.jMax) { // Compute cross derivatives // Cj(alpha,gamma) = alpha * dC/dgamma - gamma * dC/dalpha - final double CjAlphaGamma = auxiliaryElements.getAlpha() * cjsj.getdCjdGamma(j) - auxiliaryElements.getGamma() * cjsj.getdCjdAlpha(j); + final double CjAlphaGamma = context.getAlpha() * cjsj.getdCjdGamma(j) - context.getGamma() * cjsj.getdCjdAlpha(j); // Cj(alpha,beta) = alpha * dC/dbeta - beta * dC/dalpha - final double CjAlphaBeta = auxiliaryElements.getAlpha() * cjsj.getdCjdBeta(j) - auxiliaryElements.getBeta() * cjsj.getdCjdAlpha(j); + final double CjAlphaBeta = context.getAlpha() * cjsj.getdCjdBeta(j) - context.getBeta() * cjsj.getdCjdAlpha(j); // Cj(beta,gamma) = beta * dC/dgamma - gamma * dC/dbeta - final double CjBetaGamma = auxiliaryElements.getBeta() * cjsj.getdCjdGamma(j) - auxiliaryElements.getGamma() * cjsj.getdCjdBeta(j); + final double CjBetaGamma = context.getBeta() * cjsj.getdCjdGamma(j) - context.getGamma() * cjsj.getdCjdBeta(j); // Cj(h,k) = h * dC/dk - k * dC/dh final double CjHK = auxiliaryElements.getH() * cjsj.getdCjdK(j) - auxiliaryElements.getK() * cjsj.getdCjdH(j); // Sj(alpha,gamma) = alpha * dS/dgamma - gamma * dS/dalpha - final double SjAlphaGamma = auxiliaryElements.getAlpha() * cjsj.getdSjdGamma(j) - auxiliaryElements.getGamma() * cjsj.getdSjdAlpha(j); + final double SjAlphaGamma = context.getAlpha() * cjsj.getdSjdGamma(j) - context.getGamma() * cjsj.getdSjdAlpha(j); // Sj(alpha,beta) = alpha * dS/dbeta - beta * dS/dalpha - final double SjAlphaBeta = auxiliaryElements.getAlpha() * cjsj.getdSjdBeta(j) - auxiliaryElements.getBeta() * cjsj.getdSjdAlpha(j); + final double SjAlphaBeta = context.getAlpha() * cjsj.getdSjdBeta(j) - context.getBeta() * cjsj.getdSjdAlpha(j); // Sj(beta,gamma) = beta * dS/dgamma - gamma * dS/dbeta - final double SjBetaGamma = auxiliaryElements.getBeta() * cjsj.getdSjdGamma(j) - auxiliaryElements.getGamma() * cjsj.getdSjdBeta(j); + final double SjBetaGamma = context.getBeta() * cjsj.getdSjdGamma(j) - context.getGamma() * cjsj.getdSjdBeta(j); // Sj(h,k) = h * dS/dk - k * dS/dh final double SjHK = auxiliaryElements.getH() * cjsj.getdSjdK(j) - auxiliaryElements.getK() * cjsj.getdSjdH(j); @@ -1044,8 +1113,8 @@ private void computeCijSijCoefficients(final AbsoluteDate date, final Slot slot, //Coefficients for λ final double coef6 = auxiliaryElements.getH() * cjsj.getdCjdH(j) + auxiliaryElements.getK() * cjsj.getdCjdK(j); final double coef7 = auxiliaryElements.getH() * cjsj.getdSjdH(j) + auxiliaryElements.getK() * cjsj.getdSjdK(j); - currentCij[5] += context.getOON2A2() * (-2 * auxiliaryElements.getSma() * cjsj.getdCjdA(j) + coef6 / (context.getX() + 1) + context.getX() * coef2 - 3 * cjsj.getCj(j)); - currentSij[5] += context.getOON2A2() * (-2 * auxiliaryElements.getSma() * cjsj.getdSjdA(j) + coef7 / (context.getX() + 1) + context.getX() * coef3 - 3 * cjsj.getSj(j)); + currentCij[5] += context.getOON2A2() * (-2 * auxiliaryElements.getSma() * cjsj.getdCjdA(j) + coef6 / (context.getChi() + 1) + context.getChi() * coef2 - 3 * cjsj.getCj(j)); + currentSij[5] += context.getOON2A2() * (-2 * auxiliaryElements.getSma() * cjsj.getdSjdA(j) + coef7 / (context.getChi() + 1) + context.getChi() * coef3 - 3 * cjsj.getSj(j)); } for (int i = 0; i < 6; i++) { @@ -1266,19 +1335,19 @@ private > void computeCijSijCoefficients(final if (isBetween(j, 1, 2 * nMax - 1) && j < cjsj.jMax) { // Compute cross derivatives // Cj(alpha,gamma) = alpha * dC/dgamma - gamma * dC/dalpha - final T CjAlphaGamma = auxiliaryElements.getAlpha().multiply(cjsj.getdCjdGamma(j)).subtract(auxiliaryElements.getGamma().multiply(cjsj.getdCjdAlpha(j))); + final T CjAlphaGamma = context.getAlpha().multiply(cjsj.getdCjdGamma(j)).subtract(context.getGamma().multiply(cjsj.getdCjdAlpha(j))); // Cj(alpha,beta) = alpha * dC/dbeta - beta * dC/dalpha - final T CjAlphaBeta = auxiliaryElements.getAlpha().multiply(cjsj.getdCjdBeta(j)).subtract(auxiliaryElements.getBeta().multiply(cjsj.getdCjdAlpha(j))); + final T CjAlphaBeta = context.getAlpha().multiply(cjsj.getdCjdBeta(j)).subtract(context.getBeta().multiply(cjsj.getdCjdAlpha(j))); // Cj(beta,gamma) = beta * dC/dgamma - gamma * dC/dbeta - final T CjBetaGamma = auxiliaryElements.getBeta().multiply(cjsj.getdCjdGamma(j)).subtract(auxiliaryElements.getGamma().multiply(cjsj.getdCjdBeta(j))); + final T CjBetaGamma = context.getBeta().multiply(cjsj.getdCjdGamma(j)).subtract(context.getGamma().multiply(cjsj.getdCjdBeta(j))); // Cj(h,k) = h * dC/dk - k * dC/dh final T CjHK = auxiliaryElements.getH().multiply(cjsj.getdCjdK(j)).subtract(auxiliaryElements.getK().multiply(cjsj.getdCjdH(j))); // Sj(alpha,gamma) = alpha * dS/dgamma - gamma * dS/dalpha - final T SjAlphaGamma = auxiliaryElements.getAlpha().multiply(cjsj.getdSjdGamma(j)).subtract(auxiliaryElements.getGamma().multiply(cjsj.getdSjdAlpha(j))); + final T SjAlphaGamma = context.getAlpha().multiply(cjsj.getdSjdGamma(j)).subtract(context.getGamma().multiply(cjsj.getdSjdAlpha(j))); // Sj(alpha,beta) = alpha * dS/dbeta - beta * dS/dalpha - final T SjAlphaBeta = auxiliaryElements.getAlpha().multiply(cjsj.getdSjdBeta(j)).subtract(auxiliaryElements.getBeta().multiply(cjsj.getdSjdAlpha(j))); + final T SjAlphaBeta = context.getAlpha().multiply(cjsj.getdSjdBeta(j)).subtract(context.getBeta().multiply(cjsj.getdSjdAlpha(j))); // Sj(beta,gamma) = beta * dS/dgamma - gamma * dS/dbeta - final T SjBetaGamma = auxiliaryElements.getBeta().multiply(cjsj.getdSjdGamma(j)).subtract(auxiliaryElements.getGamma().multiply(cjsj.getdSjdBeta(j))); + final T SjBetaGamma = context.getBeta().multiply(cjsj.getdSjdGamma(j)).subtract(context.getGamma().multiply(cjsj.getdSjdBeta(j))); // Sj(h,k) = h * dS/dk - k * dS/dh final T SjHK = auxiliaryElements.getH().multiply(cjsj.getdSjdK(j)).subtract(auxiliaryElements.getK().multiply(cjsj.getdSjdH(j))); @@ -1306,8 +1375,8 @@ private > void computeCijSijCoefficients(final //Coefficients for λ final T coef6 = auxiliaryElements.getH().multiply(cjsj.getdCjdH(j)).add(auxiliaryElements.getK().multiply(cjsj.getdCjdK(j))); final T coef7 = auxiliaryElements.getH().multiply(cjsj.getdSjdH(j)).add(auxiliaryElements.getK().multiply(cjsj.getdSjdK(j))); - currentCij[5] = currentCij[5].add(context.getOON2A2().multiply(auxiliaryElements.getSma().multiply(-2.).multiply(cjsj.getdCjdA(j)).add(coef6.divide(context.getX().add(1.))).add(context.getX().multiply(coef2)).subtract(cjsj.getCj(j).multiply(3.)))); - currentSij[5] = currentSij[5].add(context.getOON2A2().multiply(auxiliaryElements.getSma().multiply(-2.).multiply(cjsj.getdSjdA(j)).add(coef7.divide(context.getX().add(1.))).add(context.getX().multiply(coef3)).subtract(cjsj.getSj(j).multiply(3.)))); + currentCij[5] = currentCij[5].add(context.getOON2A2().multiply(auxiliaryElements.getSma().multiply(-2.).multiply(cjsj.getdCjdA(j)).add(coef6.divide(context.getChi().add(1.))).add(context.getChi().multiply(coef2)).subtract(cjsj.getCj(j).multiply(3.)))); + currentSij[5] = currentSij[5].add(context.getOON2A2().multiply(auxiliaryElements.getSma().multiply(-2.).multiply(cjsj.getdSjdA(j)).add(coef7.divide(context.getChi().add(1.))).add(context.getChi().multiply(coef3)).subtract(cjsj.getSj(j).multiply(3.)))); } for (int i = 0; i < 6; i++) { @@ -1770,9 +1839,9 @@ private class FourierCjSjCoefficients { final AuxiliaryElements auxiliaryElements = context.getAuxiliaryElements(); - this.ghijCoef = new GHIJjsPolynomials(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta()); + this.ghijCoef = new GHIJjsPolynomials(auxiliaryElements.getK(), auxiliaryElements.getH(), context.getAlpha(), context.getBeta()); // Qns coefficients - final double[][] Qns = CoefficientsFactory.computeQns(auxiliaryElements.getGamma(), nMax, nMax); + final double[][] Qns = CoefficientsFactory.computeQns(context.getGamma(), nMax, nMax); this.lnsCoef = new LnsCoefficients(nMax, nMax, Qns, Vns, context.getRoa()); this.nMax = nMax; @@ -1780,8 +1849,8 @@ private class FourierCjSjCoefficients { this.jMax = jMax; // compute the common factors that depends on the mean elements - this.hXXX = auxiliaryElements.getH() * context.getXXX(); - this.kXXX = auxiliaryElements.getK() * context.getXXX(); + this.hXXX = auxiliaryElements.getH() * context.getChi3(); + this.kXXX = auxiliaryElements.getK() * context.getChi3(); this.cCoef = new double[7][jMax + 1]; this.sCoef = new double[7][jMax + 1]; @@ -1845,8 +1914,8 @@ private void generateCoefficients(final AbsoluteDate date, final double jn = -harmonics.getUnnormalizedCnm(n, 0); // K₀-n-1,s - final double kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final double dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final double kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getChi()); + final double dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getChi()); final double coef0 = d0smj * jn; final double coef1 = coef0 * lns; @@ -1904,8 +1973,8 @@ private void generateCoefficients(final AbsoluteDate date, final double jn = -harmonics.getUnnormalizedCnm(n, 0); // K₀-n-1,s - final double kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final double dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final double kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getChi()); + final double dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getChi()); final double coef0 = d0spj * jn; final double coef1 = coef0 * lns; @@ -1964,8 +2033,8 @@ private void generateCoefficients(final AbsoluteDate date, final double jn = -harmonics.getUnnormalizedCnm(n, 0); // K₀-n-1,s - final double kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final double dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final double kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getChi()); + final double dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getChi()); final double coef0 = d0smj * jn; final double coef1 = coef0 * lns; @@ -1999,8 +2068,8 @@ private void generateCoefficients(final AbsoluteDate date, //add first term // Jj final double jj = -harmonics.getUnnormalizedCnm(j, 0); - double kns = hansenObjects.getHansenObjects()[0].getValue(-j - 1, context.getX()); - double dkns = hansenObjects.getHansenObjects()[0].getDerivative(-j - 1, context.getX()); + double kns = hansenObjects.getHansenObjects()[0].getValue(-j - 1, context.getChi()); + double dkns = hansenObjects.getHansenObjects()[0].getDerivative(-j - 1, context.getChi()); double lns = lnsCoef.getLns(j, j); //dlns is 0 because n == s == j @@ -2052,8 +2121,8 @@ private void generateCoefficients(final AbsoluteDate date, // if s is odd, then the term is equal to zero due to the factor Vj,s-j if (s % 2 == 0) { // (2 - delta(0,j-s)) * Jj * K₀-j-1,s * Ljj-s - kns = hansenObjects.getHansenObjects()[s].getValue(-j - 1, context.getX()); - dkns = hansenObjects.getHansenObjects()[s].getDerivative(-j - 1, context.getX()); + kns = hansenObjects.getHansenObjects()[s].getValue(-j - 1, context.getChi()); + dkns = hansenObjects.getHansenObjects()[s].getDerivative(-j - 1, context.getChi()); lns = lnsCoef.getLns(j, jms); final double dlns = lnsCoef.getdLnsdGamma(j, jms); @@ -2135,8 +2204,8 @@ private void generateCoefficients(final AbsoluteDate date, final double jn = -harmonics.getUnnormalizedCnm(n, 0); // K₀-n-1,s - final double kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final double dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final double kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getChi()); + final double dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getChi()); final double coef0 = d0smj * jn; final double coef1 = coef0 * lns; @@ -2195,8 +2264,8 @@ private void generateCoefficients(final AbsoluteDate date, final double jn = -harmonics.getUnnormalizedCnm(n, 0); // K₀-n-1,s - final double kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final double dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final double kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getChi()); + final double dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getChi()); final double coef0 = d0smj * jn; final double coef1 = coef0 * lns; @@ -2259,8 +2328,8 @@ private void generateCoefficients(final AbsoluteDate date, // K₀-n-1,s - final double kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final double dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final double kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getChi()); + final double dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getChi()); final double coef0 = d0smj * jn; final double coef1 = coef0 * lns; @@ -2321,8 +2390,8 @@ private void generateCoefficients(final AbsoluteDate date, final double jn = -harmonics.getUnnormalizedCnm(n, 0); // K₀-n-1,s - final double kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final double dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final double kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getChi()); + final double dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getChi()); final double coef0 = d0smj * jn; final double coef1 = coef0 * lns; @@ -2584,9 +2653,9 @@ private class FieldFourierCjSjCoefficients > { final FieldAuxiliaryElements auxiliaryElements = context.getFieldAuxiliaryElements(); - this.ghijCoef = new FieldGHIJjsPolynomials<>(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta()); + this.ghijCoef = new FieldGHIJjsPolynomials<>(auxiliaryElements.getK(), auxiliaryElements.getH(), context.getAlpha(), context.getBeta()); // Qns coefficients - final T[][] Qns = CoefficientsFactory.computeQns(auxiliaryElements.getGamma(), nMax, nMax); + final T[][] Qns = CoefficientsFactory.computeQns(context.getGamma(), nMax, nMax); this.lnsCoef = new FieldLnsCoefficients<>(nMax, nMax, Qns, Vns, context.getRoa(), field); this.nMax = nMax; @@ -2594,8 +2663,8 @@ private class FieldFourierCjSjCoefficients > { this.jMax = jMax; // compute the common factors that depends on the mean elements - this.hXXX = auxiliaryElements.getH().multiply(context.getXXX()); - this.kXXX = auxiliaryElements.getK().multiply(context.getXXX()); + this.hXXX = auxiliaryElements.getH().multiply(context.getChi3()); + this.kXXX = auxiliaryElements.getK().multiply(context.getChi3()); this.cCoef = MathArrays.buildArray(field, 7, jMax + 1); this.sCoef = MathArrays.buildArray(field, 7, jMax + 1); @@ -2664,8 +2733,8 @@ private void generateCoefficients(final FieldAbsoluteDate date, final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0)); // K₀-n-1,s - final T kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final T dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final T kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getChi()); + final T dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getChi()); final T coef0 = jn.multiply(d0smj); final T coef1 = coef0.multiply(lns); @@ -2723,8 +2792,8 @@ private void generateCoefficients(final FieldAbsoluteDate date, final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0)); // K₀-n-1,s - final T kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final T dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final T kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getChi()); + final T dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getChi()); final T coef0 = jn.multiply(d0spj); final T coef1 = coef0.multiply(lns); @@ -2783,8 +2852,8 @@ private void generateCoefficients(final FieldAbsoluteDate date, final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0)); // K₀-n-1,s - final T kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final T dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final T kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getChi()); + final T dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getChi()); final T coef0 = jn.multiply(d0smj); final T coef1 = coef0.multiply(lns); @@ -2818,8 +2887,8 @@ private void generateCoefficients(final FieldAbsoluteDate date, //add first term // Jj final T jj = zero.subtract(harmonics.getUnnormalizedCnm(j, 0)); - T kns = hansenObjects.getHansenObjects()[0].getValue(-j - 1, context.getX()); - T dkns = hansenObjects.getHansenObjects()[0].getDerivative(-j - 1, context.getX()); + T kns = hansenObjects.getHansenObjects()[0].getValue(-j - 1, context.getChi()); + T dkns = hansenObjects.getHansenObjects()[0].getDerivative(-j - 1, context.getChi()); T lns = lnsCoef.getLns(j, j); //dlns is 0 because n == s == j @@ -2871,8 +2940,8 @@ private void generateCoefficients(final FieldAbsoluteDate date, // if s is odd, then the term is equal to zero due to the factor Vj,s-j if (s % 2 == 0) { // (2 - delta(0,j-s)) * Jj * K₀-j-1,s * Ljj-s - kns = hansenObjects.getHansenObjects()[s].getValue(-j - 1, context.getX()); - dkns = hansenObjects.getHansenObjects()[s].getDerivative(-j - 1, context.getX()); + kns = hansenObjects.getHansenObjects()[s].getValue(-j - 1, context.getChi()); + dkns = hansenObjects.getHansenObjects()[s].getDerivative(-j - 1, context.getChi()); lns = lnsCoef.getLns(j, jms); final T dlns = lnsCoef.getdLnsdGamma(j, jms); @@ -2954,8 +3023,8 @@ private void generateCoefficients(final FieldAbsoluteDate date, final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0)); // K₀-n-1,s - final T kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final T dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final T kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getChi()); + final T dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getChi()); final T coef0 = jn.multiply(d0smj); final T coef1 = coef0.multiply(lns); @@ -3014,8 +3083,8 @@ private void generateCoefficients(final FieldAbsoluteDate date, final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0)); // K₀-n-1,s - final T kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final T dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final T kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getChi()); + final T dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getChi()); final T coef0 = jn.multiply(d0smj); final T coef1 = coef0.multiply(lns); @@ -3078,8 +3147,8 @@ private void generateCoefficients(final FieldAbsoluteDate date, // K₀-n-1,s - final T kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final T dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final T kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getChi()); + final T dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getChi()); final T coef0 = jn.multiply(d0smj); final T coef1 = coef0.multiply(lns); @@ -3140,8 +3209,8 @@ private void generateCoefficients(final FieldAbsoluteDate date, final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0)); // K₀-n-1,s - final T kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final T dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final T kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getChi()); + final T dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getChi()); final T coef0 = jn.multiply(d0smj); final T coef1 = coef0.multiply(lns); @@ -3505,9 +3574,9 @@ private class UAnddU { U = 0.; // Gs and Hs coefficients - final double[][] GsHs = CoefficientsFactory.computeGsHs(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta(), maxEccPowMeanElements); + final double[][] GsHs = CoefficientsFactory.computeGsHs(auxiliaryElements.getK(), auxiliaryElements.getH(), context.getAlpha(), context.getBeta(), maxEccPowMeanElements); // Qns coefficients - final double[][] Qns = CoefficientsFactory.computeQns(auxiliaryElements.getGamma(), maxDegree, maxEccPowMeanElements); + final double[][] Qns = CoefficientsFactory.computeQns(context.getGamma(), maxDegree, maxEccPowMeanElements); final double[] roaPow = new double[maxDegree + 1]; roaPow[0] = 1.; @@ -3540,8 +3609,8 @@ private class UAnddU { final double sxgsm1 = s * GsHs[0][s - 1]; final double sxhsm1 = s * GsHs[1][s - 1]; // Then compute derivatives - dGsdh = auxiliaryElements.getBeta() * sxgsm1 - auxiliaryElements.getAlpha() * sxhsm1; - dGsdk = auxiliaryElements.getAlpha() * sxgsm1 + auxiliaryElements.getBeta() * sxhsm1; + dGsdh = context.getBeta() * sxgsm1 - context.getAlpha() * sxhsm1; + dGsdk = context.getAlpha() * sxgsm1 + context.getBeta() * sxhsm1; dGsdAl = auxiliaryElements.getK() * sxgsm1 - auxiliaryElements.getH() * sxhsm1; dGsdBe = auxiliaryElements.getH() * sxgsm1 + auxiliaryElements.getK() * sxhsm1; } @@ -3554,8 +3623,8 @@ private class UAnddU { if ((n - s) % 2 == 0) { //Extract data from previous computation : - final double kns = hansen.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final double dkns = hansen.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final double kns = hansen.getHansenObjects()[s].getValue(-n - 1, context.getChi()); + final double dkns = hansen.getHansenObjects()[s].getDerivative(-n - 1, context.getChi()); final double vns = Vns.get(new NSKey(n, s)); final double coef0 = d0s * roaPow[n] * vns * -harmonics.getUnnormalizedCnm(n, 0); @@ -3570,9 +3639,9 @@ private class UAnddU { // Compute dU / da : dUda += coef3 * (n + 1); // Compute dU / dEx - dUdk += coef1 * (kns * dGsdk + auxiliaryElements.getK() * context.getXXX() * gs * dkns); + dUdk += coef1 * (kns * dGsdk + auxiliaryElements.getK() * context.getChi3() * gs * dkns); // Compute dU / dEy - dUdh += coef1 * (kns * dGsdh + auxiliaryElements.getH() * context.getXXX() * gs * dkns); + dUdh += coef1 * (kns * dGsdh + auxiliaryElements.getH() * context.getChi3() * gs * dkns); // Compute dU / dAlpha dUdAl += coef2 * dGsdAl; // Compute dU / dBeta @@ -3699,9 +3768,11 @@ private class FieldUAnddU > { U = zero; // Gs and Hs coefficients - final T[][] GsHs = CoefficientsFactory.computeGsHs(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta(), maxEccPowMeanElements, field); + final T[][] GsHs = CoefficientsFactory.computeGsHs(auxiliaryElements.getK(), auxiliaryElements.getH(), + context.getAlpha(), context.getBeta(), + maxEccPowMeanElements, field); // Qns coefficients - final T[][] Qns = CoefficientsFactory.computeQns(auxiliaryElements.getGamma(), maxDegree, maxEccPowMeanElements); + final T[][] Qns = CoefficientsFactory.computeQns(context.getGamma(), maxDegree, maxEccPowMeanElements); final T[] roaPow = MathArrays.buildArray(field, maxDegree + 1); roaPow[0] = zero.newInstance(1.); @@ -3734,8 +3805,8 @@ private class FieldUAnddU > { final T sxgsm1 = GsHs[0][s - 1].multiply(s); final T sxhsm1 = GsHs[1][s - 1].multiply(s); // Then compute derivatives - dGsdh = sxgsm1.multiply(auxiliaryElements.getBeta()).subtract(sxhsm1.multiply(auxiliaryElements.getAlpha())); - dGsdk = sxgsm1.multiply(auxiliaryElements.getAlpha()).add(sxhsm1.multiply(auxiliaryElements.getBeta())); + dGsdh = sxgsm1.multiply(context.getBeta()).subtract(sxhsm1.multiply(context.getAlpha())); + dGsdk = sxgsm1.multiply(context.getAlpha()).add(sxhsm1.multiply(context.getBeta())); dGsdAl = sxgsm1.multiply(auxiliaryElements.getK()).subtract(sxhsm1.multiply(auxiliaryElements.getH())); dGsdBe = sxgsm1.multiply(auxiliaryElements.getH()).add(sxhsm1.multiply(auxiliaryElements.getK())); } @@ -3748,8 +3819,8 @@ private class FieldUAnddU > { if ((n - s) % 2 == 0) { //Extract data from previous computation : - final T kns = hansen.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final T dkns = hansen.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final T kns = hansen.getHansenObjects()[s].getValue(-n - 1, context.getChi()); + final T dkns = hansen.getHansenObjects()[s].getDerivative(-n - 1, context.getChi()); final double vns = Vns.get(new NSKey(n, s)); final T coef0 = d0s.multiply(roaPow[n]).multiply(vns).multiply(-harmonics.getUnnormalizedCnm(n, 0)); @@ -3764,9 +3835,9 @@ private class FieldUAnddU > { // Compute dU / da : dUda = dUda.add(coef3.multiply(n + 1)); // Compute dU / dEx - dUdk = dUdk.add(coef1.multiply(dGsdk.multiply(kns).add(auxiliaryElements.getK().multiply(context.getXXX()).multiply(dkns).multiply(gs)))); + dUdk = dUdk.add(coef1.multiply(dGsdk.multiply(kns).add(auxiliaryElements.getK().multiply(context.getChi3()).multiply(dkns).multiply(gs)))); // Compute dU / dEy - dUdh = dUdh.add(coef1.multiply(dGsdh.multiply(kns).add(auxiliaryElements.getH().multiply(context.getXXX()).multiply(dkns).multiply(gs)))); + dUdh = dUdh.add(coef1.multiply(dGsdh.multiply(kns).add(auxiliaryElements.getH().multiply(context.getChi3()).multiply(dkns).multiply(gs)))); // Compute dU / dAlpha dUdAl = dUdAl.add(coef2.multiply(dGsdAl)); // Compute dU / dBeta @@ -3860,7 +3931,7 @@ private class HansenObjects { * @param element element of the array to compute the init values */ public void computeHansenObjectsInitValues(final DSSTZonalContext context, final int element) { - hansenObjects[element].computeInitValues(context.getX()); + hansenObjects[element].computeInitValues(context.getChi()); } /** Get the Hansen Objects. @@ -3897,7 +3968,7 @@ private class FieldHansenObjects> { * @param element element of the array to compute the init values */ public void computeHansenObjectsInitValues(final FieldDSSTZonalContext context, final int element) { - hansenObjects[element].computeInitValues(context.getX()); + hansenObjects[element].computeInitValues(context.getChi()); } /** Get the Hansen Objects. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonalContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonalContext.java index 0cab4f62a6..9105ecb046 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonalContext.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonalContext.java @@ -16,8 +16,8 @@ */ package org.orekit.propagation.semianalytical.dsst.forces; -import org.hipparchus.util.FastMath; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.frames.Frame; import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements; /** @@ -29,34 +29,10 @@ * @author Bryan Cazabonne * @since 10.0 */ -public class DSSTZonalContext extends ForceModelContext { +public class DSSTZonalContext extends DSSTGravityContext { - // Common factors for potential computation - /** A = sqrt(μ * a). */ - private final double A; - /** Χ = 1 / sqrt(1 - e²) = 1 / B. */ - private double X; - /** Χ². */ - private double XX; - /** Χ³. */ - private double XXX; - /** 1 / (A * B) . */ - private double ooAB; - /** B / A . */ - private double BoA; - /** B / A(1 + B) . */ - private double BoABpo; - /** -C / (2 * A * B) . */ - private double mCo2AB; - /** -2 * a / A . */ - private double m2aoA; - /** μ / a . */ - private double muoa; - /** R / a . */ - private double roa; - - /** Keplerian mean motion. */ - private final double n; + /** Χ³ = 1 / B³. */ + private final double chi3; // Short period terms /** h * k. */ @@ -81,47 +57,42 @@ public class DSSTZonalContext extends ForceModelContext { private double BB; /** - * Simple constructor. + * Constructor with central body frame equals orbit frame. * * @param auxiliaryElements auxiliary elements related to the current orbit * @param provider provider for spherical harmonics * @param parameters values of the force model parameters + * @deprecated since 12.2 and issue 1104, should be removed in 13.0 */ + @Deprecated DSSTZonalContext(final AuxiliaryElements auxiliaryElements, final UnnormalizedSphericalHarmonicsProvider provider, - final double[] parameters) { - - super(auxiliaryElements); - - final double mu = parameters[0]; - - // Keplerian Mean Motion - final double absA = FastMath.abs(auxiliaryElements.getSma()); - n = FastMath.sqrt(mu / absA) / absA; + final double[] parameters) { + this(auxiliaryElements, auxiliaryElements.getFrame(), provider, parameters); + } - A = FastMath.sqrt(mu * auxiliaryElements.getSma()); + /** + * Constructor with central body frame potentially different than orbit frame. + * + * @param auxiliaryElements auxiliary elements related to the current orbit + * @param bodyFixedFrame rotating body frame + * @param provider provider for spherical harmonics + * @param parameters values of the force model parameters + * @since 12.2 + */ + DSSTZonalContext(final AuxiliaryElements auxiliaryElements, + final Frame bodyFixedFrame, + final UnnormalizedSphericalHarmonicsProvider provider, + final double[] parameters) { - // Χ = 1 / B - X = 1. / auxiliaryElements.getB(); - XX = X * X; - XXX = X * XX; + super(auxiliaryElements, bodyFixedFrame, provider, parameters); - // 1 / AB - ooAB = 1. / (A * auxiliaryElements.getB()); - // B / A - BoA = auxiliaryElements.getB() / A; - // -C / 2AB - mCo2AB = -auxiliaryElements.getC() * ooAB / 2.; - // B / A(1 + B) - BoABpo = BoA / (1. + auxiliaryElements.getB()); - // -2 * a / A - m2aoA = -2 * auxiliaryElements.getSma() / A; - // μ / a - muoa = mu / auxiliaryElements.getSma(); - // R / a - roa = provider.getAe() / auxiliaryElements.getSma(); + // Chi3 + final double chi = getChi(); + this.chi3 = chi * getChi2(); // Short period terms + // ----- // h * k. hk = auxiliaryElements.getH() * auxiliaryElements.getK(); @@ -130,98 +101,71 @@ public class DSSTZonalContext extends ForceModelContext { // (k² - h²) / 2. k2mh2o2 = k2mh2 / 2.; // 1 / (n² * a²) = 1 / (n * A) - oon2a2 = 1 / (A * n); + oon2a2 = 1 / (getA() * getMeanMotion()); // 1 / (n² * a) = a / (n * A) oon2a = auxiliaryElements.getSma() * oon2a2; // χ³ / (n² * a) - x3on2a = XXX * oon2a; + x3on2a = chi3 * oon2a; // χ / (n² * a²) - xon2a2 = X * oon2a2; + xon2a2 = getChi() * oon2a2; // (C * χ) / ( 2 * n² * a² ) cxo2n2a2 = xon2a2 * auxiliaryElements.getC() / 2; // (χ²) / (n² * a² * (χ + 1 ) ) - x2on2a2xp1 = xon2a2 * X / (X + 1); + x2on2a2xp1 = xon2a2 * chi / (chi + 1); // B * B BB = auxiliaryElements.getB() * auxiliaryElements.getB(); } /** Get Χ = 1 / sqrt(1 - e²) = 1 / B. * @return Χ + * @deprecated since 12.2 Use getChi() instead */ + @Deprecated public double getX() { - return X; + return getChi(); } /** Get Χ². * @return Χ². + * @deprecated since 12.2 Use getChi2() instead */ + @Deprecated public double getXX() { - return XX; + return getChi2(); + } + + /** Getter for the Χ³. + * @return the Χ³ + */ + public double getChi3() { + return chi3; } /** Get Χ³. * @return Χ³ + * @deprecated since 12.2 Use getChi3() instead */ + @Deprecated public double getXXX() { - return XXX; + return getChi3(); } /** Get m2aoA = -2 * a / A. * @return m2aoA + * @deprecated since 12.2 Use -getAx2oA()() instead */ + @Deprecated public double getM2aoA() { - return m2aoA; - } - - /** Get B / A. - * @return BoA - */ - public double getBoA() { - return BoA; - } - - /** Get ooAB = 1 / (A * B). - * @return ooAB - */ - public double getOoAB() { - return ooAB; + return -getAx2oA(); } /** Get mCo2AB = -C / 2AB. * @return mCo2AB + * @deprecated since 12.2 Use -getCo2AB()() instead */ + @Deprecated public double getMCo2AB() { - return mCo2AB; - } - - /** Get BoABpo = B / A(1 + B). - * @return BoABpo - */ - public double getBoABpo() { - return BoABpo; - } - - /** Get μ / a . - * @return muoa - */ - public double getMuoa() { - return muoa; - } - - /** Get roa = R / a. - * @return roa - */ - public double getRoa() { - return roa; - } - - /** Get the Keplerian mean motion. - *

      The Keplerian mean motion is computed directly from semi major axis - * and central acceleration constant.

      - * @return Keplerian mean motion in radians per second - */ - public double getMeanMotion() { - return n; + return -getCo2AB(); } /** Get h * k. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTGravityContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTGravityContext.java new file mode 100644 index 0000000000..0c470a41de --- /dev/null +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTGravityContext.java @@ -0,0 +1,257 @@ +/* Copyright 2002-2024 CS GROUP + * 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.propagation.semianalytical.dsst.forces; + +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.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.frames.FieldStaticTransform; +import org.orekit.frames.Frame; +import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; + +/** + * This class is a container for the common parameters used in {@link DSSTTesseral} and {@link DSSTZonal}. + *

      + * It performs parameters initialization at each integration step for the Tesseral and Zonal contribution + * to the central body gravitational perturbation. + *

      + * @author Bryan Cazabonne + * @author Maxime Journot + * @since 12.2 + */ +public class FieldDSSTGravityContext> extends FieldForceModelContext { + + /** A = sqrt(μ * a). */ + private final T A; + + /** Χ = 1 / sqrt(1 - e²) = 1 / B. */ + private final T chi; + + /** Χ². */ + private final T chi2; + + // Common factors from equinoctial coefficients + /** 2 * a / A . */ + private final T ax2oA; + + /** 1 / (A * B) . */ + private final T ooAB; + + /** B / A . */ + private final T BoA; + + /** B / (A * (1 + B)) . */ + private final T BoABpo; + + /** C / (2 * A * B) . */ + private final T Co2AB; + + /** μ / a . */ + private final T muoa; + + /** R / a . */ + private final T roa; + + /** Keplerian mean motion. */ + private final T n; + + /** Direction cosine α. */ + private final T alpha; + + /** Direction cosine β. */ + private final T beta; + + /** Direction cosine γ. */ + private final T gamma; + + /** Transform from body-fixed frame to inertial frame. */ + private final FieldStaticTransform bodyFixedToInertialTransform; + + /** + * Constructor. + * + * @param auxiliaryElements auxiliary elements related to the current orbit + * @param centralBodyFixedFrame rotating body frame + * @param provider provider for spherical harmonics + * @param parameters values of the force model parameters + */ + FieldDSSTGravityContext(final FieldAuxiliaryElements auxiliaryElements, + final Frame centralBodyFixedFrame, + final UnnormalizedSphericalHarmonicsProvider provider, + final T[] parameters) { + + super(auxiliaryElements); + + // µ + final T mu = parameters[0]; + + // Semi-major axis + final T a = auxiliaryElements.getSma(); + + // Keplerian Mean Motion + final T absA = FastMath.abs(a); + this.n = FastMath.sqrt(mu.divide(absA)).divide(absA); + + // A = sqrt(µ * |a|) + this.A = FastMath.sqrt(mu.multiply(absA)); + + // Χ = 1 / B + final T B = auxiliaryElements.getB(); + this.chi = auxiliaryElements.getB().reciprocal(); + this.chi2 = chi.multiply(chi); + + // Common factors from equinoctial coefficients + // 2 * a / A + this.ax2oA = a.divide(A).multiply(2.); + // B / A + this.BoA = B.divide(A);; + // 1 / AB + this.ooAB = A.multiply(B).reciprocal();; + // C / 2AB + this.Co2AB = auxiliaryElements.getC().multiply(ooAB).divide(2.);; + // B / (A * (1 + B)) + this.BoABpo = BoA.divide(B.add(1.)); + // &mu / a + this.muoa = mu.divide(a); + // R / a + this.roa = a.divide(provider.getAe()).reciprocal(); + + + // If (centralBodyFrame == null), then centralBodyFrame = orbit frame (see DSSTZonal constructors for more on this). + final Frame internalCentralBodyFrame = centralBodyFixedFrame == null ? auxiliaryElements.getFrame() : centralBodyFixedFrame; + + // Transform from body-fixed frame (typically ITRF) to inertial frame + bodyFixedToInertialTransform = internalCentralBodyFrame. + getStaticTransformTo(auxiliaryElements.getFrame(), auxiliaryElements.getDate()); + + final FieldVector3D zB = bodyFixedToInertialTransform.transformVector(Vector3D.PLUS_K); + + // Direction cosines for central body [Eq. 2.1.9-(1)] + alpha = FieldVector3D.dotProduct(zB, auxiliaryElements.getVectorF()); + beta = FieldVector3D.dotProduct(zB, auxiliaryElements.getVectorG()); + gamma = FieldVector3D.dotProduct(zB, auxiliaryElements.getVectorW()); + } + + /** A = sqrt(μ * a). + * @return A + */ + public T getA() { + return A; + } + + /** Get Χ = 1 / sqrt(1 - e²) = 1 / B. + * @return chi + */ + public T getChi() { + return chi; + } + + /** Get Χ². + * @return chi2 + */ + public T getChi2() { + return chi2; + } + + /** Getter for the ax2oA. + * @return the ax2oA + */ + public T getAx2oA() { + return ax2oA; + } + + /** Get ooAB = 1 / (A * B). + * @return ooAB + */ + public T getOoAB() { + return ooAB; + } + + /** Get B / A. + * @return BoA + */ + public T getBoA() { + return BoA; + } + + /** Get BoABpo = B / A(1 + B). + * @return BoABpo + */ + public T getBoABpo() { + return BoABpo; + } + + /** Get Co2AB = C / 2AB. + * @return Co2AB + */ + public T getCo2AB() { + return Co2AB; + } + + /** Get muoa = μ / a. + * @return the muoa + */ + public T getMuoa() { + return muoa; + } + + /** Get roa = R / a. + * @return roa + */ + public T getRoa() { + return roa; + } + + /** Get the Keplerian mean motion. + *

      The Keplerian mean motion is computed directly from semi major axis + * and central acceleration constant.

      + * @return Keplerian mean motion in radians per second + */ + public T getMeanMotion() { + return n; + } + + /** Get direction cosine α for central body. + * @return α + */ + public T getAlpha() { + return alpha; + } + + /** Get direction cosine β for central body. + * @return β + */ + public T getBeta() { + return beta; + } + + /** Get direction cosine γ for central body. + * @return the γ + */ + public T getGamma() { + return gamma; + } + + /** Getter for the bodyFixedToInertialTransform. + * @return the bodyFixedToInertialTransform + */ + public FieldStaticTransform getBodyFixedToInertialTransform() { + return bodyFixedToInertialTransform; + } +} diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTTesseralContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTTesseralContext.java index a3c47504a2..ad5c69a22b 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTTesseralContext.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTTesseralContext.java @@ -20,6 +20,7 @@ import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathUtils; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; import org.orekit.frames.FieldStaticTransform; import org.orekit.frames.Frame; @@ -36,7 +37,7 @@ * @since 10.0 * @param type of the field elements */ -public class FieldDSSTTesseralContext> extends FieldForceModelContext { +public class FieldDSSTTesseralContext> extends FieldDSSTGravityContext { /** Retrograde factor I. *

      @@ -53,47 +54,12 @@ public class FieldDSSTTesseralContext> extends */ private static final int I = 1; - /** A = sqrt(μ * a). */ - private T A; - - // Common factors for potential computation - /** Χ = 1 / sqrt(1 - e²) = 1 / B. */ - private T chi; - - /** Χ². */ - private T chi2; - /** Central body rotation angle θ. */ private T theta; - // Common factors from equinoctial coefficients - /** 2 * a / A .*/ - private T ax2oA; - - /** 1 / (A * B) .*/ - private T ooAB; - - /** B / A .*/ - private T BoA; - - /** B / (A * (1 + B)) .*/ - private T BoABpo; - - /** C / (2 * A * B) .*/ - private T Co2AB; - - /** μ / a .*/ - private T moa; - - /** R / a .*/ - private T roa; - /** ecc². */ private T e2; - /** Keplerian mean motion. */ - private T n; - /** Keplerian period. */ private T period; @@ -114,62 +80,34 @@ public class FieldDSSTTesseralContext> extends * to selected the right value for state date or by getting the parameters for a specific date */ FieldDSSTTesseralContext(final FieldAuxiliaryElements auxiliaryElements, - final Frame centralBodyFrame, - final UnnormalizedSphericalHarmonicsProvider provider, - final int maxFrequencyShortPeriodics, - final double bodyPeriod, - final T[] parameters) { + final Frame centralBodyFrame, + final UnnormalizedSphericalHarmonicsProvider provider, + final int maxFrequencyShortPeriodics, + final double bodyPeriod, + final T[] parameters) { - super(auxiliaryElements); + super(auxiliaryElements, centralBodyFrame, provider, parameters); + // Get field and zero final Field field = auxiliaryElements.getDate().getField(); final T zero = field.getZero(); - final T mu = parameters[0]; - - // Keplerian mean motion - final T absA = FastMath.abs(auxiliaryElements.getSma()); - n = FastMath.sqrt(mu.divide(absA)).divide(absA); - // Keplerian period final T a = auxiliaryElements.getSma(); - period = (a.getReal() < 0) ? zero.newInstance(Double.POSITIVE_INFINITY) : a.multiply(a.getPi().multiply(2.0)).multiply(a.divide(mu).sqrt()); - - A = FastMath.sqrt(mu.multiply(auxiliaryElements.getSma())); + period = (a.getReal() < 0) ? zero.newInstance(Double.POSITIVE_INFINITY) : getMeanMotion().reciprocal().multiply(MathUtils.TWO_PI); // Eccentricity square e2 = auxiliaryElements.getEcc().multiply(auxiliaryElements.getEcc()); // Central body rotation angle from equation 2.7.1-(3)(4). - final FieldStaticTransform t = centralBodyFrame.getStaticTransformTo(auxiliaryElements.getFrame(), auxiliaryElements.getDate()); + final FieldStaticTransform t = getBodyFixedToInertialTransform(); final FieldVector3D xB = t.transformVector(FieldVector3D.getPlusI(field)); final FieldVector3D yB = t.transformVector(FieldVector3D.getPlusJ(field)); theta = FastMath.atan2(auxiliaryElements.getVectorF().dotProduct(yB).negate().add((auxiliaryElements.getVectorG().dotProduct(xB)).multiply(I)), auxiliaryElements.getVectorF().dotProduct(xB).add(auxiliaryElements.getVectorG().dotProduct(yB).multiply(I))); - // Common factors from equinoctial coefficients - // 2 * a / A - ax2oA = auxiliaryElements.getSma().divide(A).multiply(2.); - // B / A - BoA = auxiliaryElements.getB().divide(A); - // 1 / AB - ooAB = A.multiply(auxiliaryElements.getB()).reciprocal(); - // C / 2AB - Co2AB = auxiliaryElements.getC().multiply(ooAB).divide(2.); - // B / (A * (1 + B)) - BoABpo = BoA.divide(auxiliaryElements.getB().add(1.)); - // &mu / a - moa = mu.divide(auxiliaryElements.getSma()); - // R / a - roa = auxiliaryElements.getSma().divide(provider.getAe()).reciprocal(); - - // Χ = 1 / B - chi = auxiliaryElements.getB().reciprocal(); - chi2 = chi.multiply(chi); - // Ratio of satellite to central body periods to define resonant terms ratio = period.divide(bodyPeriod); - } /** Get ecc². @@ -186,67 +124,13 @@ public T getTheta() { return theta; } - /** Get ax2oA = 2 * a / A . - * @return ax2oA - */ - public T getAx2oA() { - return ax2oA; - } - - /** Get Χ = 1 / sqrt(1 - e²) = 1 / B. - * @return chi - */ - public T getChi() { - return chi; - } - - /** Get Χ². - * @return chi2 - */ - public T getChi2() { - return chi2; - } - - /** Get B / A. - * @return BoA - */ - public T getBoA() { - return BoA; - } - - /** Get ooAB = 1 / (A * B). - * @return ooAB - */ - public T getOoAB() { - return ooAB; - } - - /** Get Co2AB = C / 2AB. - * @return Co2AB - */ - public T getCo2AB() { - return Co2AB; - } - - /** Get BoABpo = B / A(1 + B). - * @return BoABpo - */ - public T getBoABpo() { - return BoABpo; - } - /** Get μ / a . * @return moa + * @deprecated since 12.2 Use getMuoa() instead */ + @Deprecated public T getMoa() { - return moa; - } - - /** Get roa = R / a. - * @return roa - */ - public T getRoa() { - return roa; + return getMuoa(); } /** Get the Keplerian period. @@ -258,20 +142,10 @@ public T getOrbitPeriod() { return period; } - /** Get the Keplerian mean motion. - *

      The Keplerian mean motion is computed directly from semi major axis - * and central acceleration constant.

      - * @return Keplerian mean motion in radians per second - */ - public T getMeanMotion() { - return n; - } - /** Get the ratio of satellite period to central body rotation period. * @return ratio */ public T getRatio() { return ratio; } - } diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTZonalContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTZonalContext.java index 368cecd6b3..ef105318f8 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTZonalContext.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTZonalContext.java @@ -17,8 +17,8 @@ package org.orekit.propagation.semianalytical.dsst.forces; import org.hipparchus.CalculusFieldElement; -import org.hipparchus.util.FastMath; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.frames.Frame; import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; import org.orekit.time.AbsoluteDate; @@ -32,34 +32,10 @@ * @since 10.0 * @param type of the field elements */ -public class FieldDSSTZonalContext> extends FieldForceModelContext { +public class FieldDSSTZonalContext> extends FieldDSSTGravityContext { - // Common factors for potential computation - /** A = sqrt(μ * a). */ - private final T A; - /** Χ = 1 / sqrt(1 - e²) = 1 / B. */ - private T X; - /** Χ². */ - private T XX; - /** Χ³. */ - private T XXX; - /** 1 / (A * B) . */ - private T ooAB; - /** B / A . */ - private T BoA; - /** B / A(1 + B) . */ - private T BoABpo; - /** -C / (2 * A * B) . */ - private T mCo2AB; - /** -2 * a / A . */ - private T m2aoA; - /** μ / a . */ - private T muoa; - /** R / a . */ - private T roa; - - /** Keplerian mean motion. */ - private final T n; + /** Χ³ = 1 / B³. */ + private final T chi3; // Short period terms /** h * k. */ @@ -83,8 +59,7 @@ public class FieldDSSTZonalContext> extends Fi /** B * B. */ private T BB; - /** - * Simple constructor. + /** Constructor with central body frame equals orbit frame. * * @param auxiliaryElements auxiliary elements related to the current orbit * @param provider provider for spherical harmonics @@ -92,42 +67,40 @@ public class FieldDSSTZonalContext> extends Fi * for each parameters corresponding to state date) obtained by calling the extract * parameter method {@link #extractParameters(double[], AbsoluteDate)} * to selected the right value for state date or by getting the parameters for a specific date + * @deprecated since 12.2 and issue 1104, should be removed in 13.0 */ + @Deprecated FieldDSSTZonalContext(final FieldAuxiliaryElements auxiliaryElements, - final UnnormalizedSphericalHarmonicsProvider provider, - final T[] parameters) { - - super(auxiliaryElements); - - final T mu = parameters[0]; + final UnnormalizedSphericalHarmonicsProvider provider, + final T[] parameters) { - // Keplerian mean motion - final T absA = FastMath.abs(auxiliaryElements.getSma()); - n = FastMath.sqrt(mu.divide(absA)).divide(absA); + this(auxiliaryElements, auxiliaryElements.getFrame(), provider, parameters); + } - A = FastMath.sqrt(mu.multiply(auxiliaryElements.getSma())); + /** Constructor with central body frame potentially different from orbit frame. + * + * @param auxiliaryElements auxiliary elements related to the current orbit + * @param centralBodyFrame rotating body frame + * @param provider provider for spherical harmonics + * @param parameters values of the force model parameters (only 1 values + * for each parameters corresponding to state date) obtained by calling the extract + * parameter method {@link #extractParameters(double[], AbsoluteDate)} + * to selected the right value for state date or by getting the parameters for a specific date + * @since 12.2 + */ + FieldDSSTZonalContext(final FieldAuxiliaryElements auxiliaryElements, + final Frame centralBodyFrame, + final UnnormalizedSphericalHarmonicsProvider provider, + final T[] parameters) { - // Χ = 1 / B - X = auxiliaryElements.getB().reciprocal(); - XX = X.square(); - XXX = X.multiply(XX); + super(auxiliaryElements, centralBodyFrame, provider, parameters); - // 1 / AB - ooAB = (A.multiply(auxiliaryElements.getB())).reciprocal(); - // B / A - BoA = auxiliaryElements.getB().divide(A); - // -C / 2AB - mCo2AB = auxiliaryElements.getC().multiply(ooAB).divide(2.).negate(); - // B / A(1 + B) - BoABpo = BoA.divide(auxiliaryElements.getB().add(1.)); - // -2 * a / A - m2aoA = auxiliaryElements.getSma().divide(A).multiply(2.).negate(); - // μ / a - muoa = mu.divide(auxiliaryElements.getSma()); - // R / a - roa = auxiliaryElements.getSma().divide(provider.getAe()).reciprocal(); + // Chi3 + final T chi = getChi(); + this.chi3 = chi.multiply(getChi2()); // Short period terms + // ----- // h * k. hk = auxiliaryElements.getH().multiply(auxiliaryElements.getK()); @@ -136,99 +109,72 @@ public class FieldDSSTZonalContext> extends Fi // (k² - h²) / 2. k2mh2o2 = k2mh2.divide(2.); // 1 / (n² * a²) = 1 / (n * A) - oon2a2 = (A.multiply(n)).reciprocal(); + oon2a2 = (getA().multiply(getMeanMotion())).reciprocal(); // 1 / (n² * a) = a / (n * A) oon2a = auxiliaryElements.getSma().multiply(oon2a2); // χ³ / (n² * a) - x3on2a = XXX.multiply(oon2a); + x3on2a = chi3.multiply(oon2a); // χ / (n² * a²) - xon2a2 = X.multiply(oon2a2); + xon2a2 = chi.multiply(oon2a2); // (C * χ) / ( 2 * n² * a² ) cxo2n2a2 = xon2a2.multiply(auxiliaryElements.getC()).divide(2.); // (χ²) / (n² * a² * (χ + 1 ) ) - x2on2a2xp1 = xon2a2.multiply(X).divide(X.add(1.)); + x2on2a2xp1 = xon2a2.multiply(chi).divide(chi.add(1.)); // B * B BB = auxiliaryElements.getB().multiply(auxiliaryElements.getB()); - } /** Get Χ = 1 / sqrt(1 - e²) = 1 / B. * @return Χ + * @deprecated since 12.2 Use getChi() instead */ + @Deprecated public T getX() { - return X; + return getChi(); } /** Get Χ². * @return Χ². + * @deprecated since 12.2 Use getChi2() instead */ + @Deprecated public T getXX() { - return XX; + return getChi2(); + } + + /** Getter for the Χ³. + * @return the Χ³ + */ + @Deprecated + public T getChi3() { + return chi3; } /** Get Χ³. * @return Χ³ + * @deprecated since 12.2 Use getChi3() instead */ + @Deprecated public T getXXX() { - return XXX; + return getChi3(); } /** Get m2aoA = -2 * a / A. * @return m2aoA + * @deprecated since 12.2 Use -getAx2oA()() instead */ + @Deprecated public T getM2aoA() { - return m2aoA; - } - - /** Get B / A. - * @return BoA - */ - public T getBoA() { - return BoA; - } - - /** Get ooAB = 1 / (A * B). - * @return ooAB - */ - public T getOoAB() { - return ooAB; + return getAx2oA().negate(); } /** Get mCo2AB = -C / 2AB. * @return mCo2AB + * @deprecated since 12.2 Use -getCo2AB()() instead */ + @Deprecated public T getMCo2AB() { - return mCo2AB; - } - - /** Get BoABpo = B / A(1 + B). - * @return BoABpo - */ - public T getBoABpo() { - return BoABpo; - } - - /** Get μ / a . - * @return muoa - */ - public T getMuoa() { - return muoa; - } - - /** Get roa = R / a. - * @return roa - */ - public T getRoa() { - return roa; - } - - /** Get the Keplerian mean motion. - *

      The Keplerian mean motion is computed directly from semi major axis - * and central acceleration constant.

      - * @return Keplerian mean motion in radians per second - */ - public T getMeanMotion() { - return n; + return getCo2AB().negate(); } /** Get h * k. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/J2SquaredModel.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/J2SquaredModel.java index 4279eeb91d..86699f1f6a 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/J2SquaredModel.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/J2SquaredModel.java @@ -17,6 +17,14 @@ package org.orekit.propagation.semianalytical.dsst.forces; import org.hipparchus.CalculusFieldElement; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.PropagationType; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements; +import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; + +import java.util.Collections; +import java.util.List; /** * Semi-analytical J2-squared model. @@ -49,4 +57,63 @@ public interface J2SquaredModel { */ > T[] computeMeanEquinoctialSecondOrderTerms(FieldDSSTJ2SquaredClosedFormContext context); + /** + * Performs initialization of J2-squared short period terms prior to propagation. + * @param auxiliaryElements auxiliary elements + * @param type type of the elements used (MEAN or OSCULATING) + * @param parameters force model parameters + * @return a list containing the initialized short period terms + * @since 12.2 + */ + default List initializeShortPeriodTerms(final AuxiliaryElements auxiliaryElements, + final PropagationType type, + final double[] parameters) { + return Collections.emptyList(); + } + + /** + * Performs initialization of J2-squared short period terms prior to propagation. + * @param auxiliaryElements auxiliary elements + * @param type type of the orbital elements used (MEAN or OSCULATING) + * @param parameters force model parameters + * @param type of the field elements + * @return a list containing the initialized short period terms + * @since 12.2 + */ + default > List> initializeShortPeriodTerms(final FieldAuxiliaryElements auxiliaryElements, + final PropagationType type, + final T[] parameters) { + return Collections.emptyList(); + } + + /** Update the J2-squared short period terms. + *

      + * The {@link ShortPeriodTerms short period terms} that will be updated + * are the ones that were returned during the call to {@link + * #initializeShortPeriodTerms(AuxiliaryElements, PropagationType, double[])}. + *

      + * @param parameters force model parameters + * @param meanStates mean states information: date, kinematics, attitude + * @since 12.2 + */ + default void updateShortPeriodTerms(final double[] parameters, final SpacecraftState... meanStates) { + // Does nothing by default + } + + /** Update the J2-squared short period terms. + *

      + * The {@link ShortPeriodTerms short period terms} that will be updated + * are the ones that were returned during the call to {@link + * #initializeShortPeriodTerms(AuxiliaryElements, PropagationType, double[])}. + *

      + * @param parameters force model parameters + * @param meanStates mean states information: date, kinematics, attitude + * @param type of the field elements + * @since 12.2 + */ + @SuppressWarnings("unchecked") + default > void updateShortPeriodTerms(final T[] parameters, final FieldSpacecraftState... meanStates) { + // Does nothing by default + } + } diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/AuxiliaryElements.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/AuxiliaryElements.java index aa4933213f..82a625cf51 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/AuxiliaryElements.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/AuxiliaryElements.java @@ -21,6 +21,7 @@ import org.hipparchus.util.MathUtils; import org.orekit.frames.Frame; import org.orekit.orbits.Orbit; +import org.orekit.propagation.semianalytical.dsst.forces.DSSTGravityContext; import org.orekit.time.AbsoluteDate; @@ -104,15 +105,6 @@ public class AuxiliaryElements { /** Equinoctial frame w vector. */ private final Vector3D w; - /** Direction cosine α. */ - private final double alpha; - - /** Direction cosine β. */ - private final double beta; - - /** Direction cosine γ. */ - private final double gamma; - /** Simple constructor. * @param orbit related mean orbit for auxiliary elements * @param retrogradeFactor retrograde factor I [Eq. 2.1.2-(2)] @@ -167,11 +159,6 @@ public AuxiliaryElements(final Orbit orbit, final int retrogradeFactor) { f = new Vector3D(ooC, new Vector3D(1. - p2 + q2, pq2, -px2 * I)); g = new Vector3D(ooC, new Vector3D(pq2 * I, (1. + p2 - q2) * I, qx2)); w = new Vector3D(ooC, new Vector3D(px2, -qx2, (1. - p2 - q2) * I)); - - // Direction cosines for central body [Eq. 2.1.9-(1)] - alpha = f.getZ(); - beta = g.getZ(); - gamma = w.getZ(); } /** Get the orbit. @@ -332,23 +319,28 @@ public Vector3D getVectorW() { /** Get direction cosine α for central body. * @return α + * @deprecated since 12.2, use {@link DSSTGravityContext#getAlpha()} instead */ + @Deprecated public double getAlpha() { - return alpha; + return f.getZ(); } /** Get direction cosine β for central body. * @return β + * @deprecated since 12.2, use {@link DSSTGravityContext#getBeta()} instead */ + @Deprecated public double getBeta() { - return beta; + return g.getZ(); } /** Get direction cosine γ for central body. * @return γ + * @deprecated since 12.2, use {@link DSSTGravityContext#getGamma()} instead */ + @Deprecated public double getGamma() { - return gamma; + return w.getZ(); } - } diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldAuxiliaryElements.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldAuxiliaryElements.java index 5bfcf9a87b..2c3ee58638 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldAuxiliaryElements.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldAuxiliaryElements.java @@ -22,6 +22,7 @@ import org.hipparchus.util.MathUtils; import org.orekit.frames.Frame; import org.orekit.orbits.FieldOrbit; +import org.orekit.propagation.semianalytical.dsst.forces.FieldDSSTGravityContext; import org.orekit.time.FieldAbsoluteDate; /** Container class for common parameters used by all DSST forces. @@ -105,15 +106,6 @@ public class FieldAuxiliaryElements> { /** Equinoctial frame w vector. */ private final FieldVector3D w; - /** Direction cosine α. */ - private final T alpha; - - /** Direction cosine β. */ - private final T beta; - - /** Direction cosine γ. */ - private final T gamma; - /** Simple constructor. * @param orbit related mean orbit for auxiliary elements * @param retrogradeFactor retrograde factor I [Eq. 2.1.2-(2)] @@ -170,11 +162,6 @@ public FieldAuxiliaryElements(final FieldOrbit orbit, final int retrogradeFac f = new FieldVector3D<>(ooC, new FieldVector3D<>(p2.negate().add(1.).add(q2), pq2, px2.multiply(I).negate())); g = new FieldVector3D<>(ooC, new FieldVector3D<>(pq2.multiply(I), (p2.add(1.).subtract(q2)).multiply(I), qx2)); w = new FieldVector3D<>(ooC, new FieldVector3D<>(px2, qx2.negate(), (p2.add(q2).negate().add(1.)).multiply(I))); - - // Direction cosines for central body [Eq. 2.1.9-(1)] - alpha = (T) f.getZ(); - beta = (T) g.getZ(); - gamma = (T) w.getZ(); } /** Get the orbit. @@ -333,32 +320,38 @@ public FieldVector3D getVectorW() { return w; } + /** Transforms the FieldAuxiliaryElements instance into an AuxiliaryElements instance. + * @return the AuxiliaryElements instance + * @since 11.3.3 + */ + public AuxiliaryElements toAuxiliaryElements() { + return new AuxiliaryElements(orbit.toOrbit(), getRetrogradeFactor()); + } + /** Get direction cosine α for central body. * @return α + * @deprecated since 12.2, use {@link FieldDSSTGravityContext#getAlpha()} instead */ + @Deprecated public T getAlpha() { - return alpha; + return (T) f.getZ(); } /** Get direction cosine β for central body. * @return β + * @deprecated since 12.2, use {@link FieldDSSTGravityContext#getBeta()} instead */ + @Deprecated public T getBeta() { - return beta; + return (T) g.getZ(); } /** Get direction cosine γ for central body. * @return γ + * @deprecated since 12.2, use {@link FieldDSSTGravityContext#getGamma()} instead */ + @Deprecated public T getGamma() { - return gamma; - } - - /** Transforms the FieldAuxiliaryElements instance into an AuxiliaryElements instance. - * @return the AuxiliaryElements instance - * @since 11.3.3 - */ - public AuxiliaryElements toAuxiliaryElements() { - return new AuxiliaryElements(orbit.toOrbit(), getRetrogradeFactor()); + return (T) w.getZ(); } } diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenTesseralLinear.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenTesseralLinear.java index b1365fd255..1995bdf2df 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenTesseralLinear.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenTesseralLinear.java @@ -303,7 +303,7 @@ private FieldGradient getValueGradient(final T e2, final T chi, final T chi2) final T coef = zero.subtract(mnm1 + 1.5); final T derivative = coef.multiply(chi2).multiply(value). add(FastMath.pow(chi2, -mnm1 - 1).multiply(serie.getPartialDerivative(0)).divide(chi)); - return new FieldGradient<>(value, derivative); + return new FieldGradient(value, derivative); } /** Generate the serie expansion in e². diff --git a/src/main/java/org/orekit/time/AbsoluteDate.java b/src/main/java/org/orekit/time/AbsoluteDate.java index e31a2e2c1c..549f699247 100644 --- a/src/main/java/org/orekit/time/AbsoluteDate.java +++ b/src/main/java/org/orekit/time/AbsoluteDate.java @@ -1536,4 +1536,53 @@ public String toStringWithoutUtcOffset(final TimeScale timeScale, .toStringWithoutUtcOffset(timeScale.minuteDuration(this), fractionDigits); } + /** + * Return the given date as a Modified Julian Date expressed in UTC. + * + * @return double representation of the given date as Modified Julian Date. + * + * @since 12.2 + */ + @DefaultDataContext + public double getMJD() { + return this.getJD() - DateComponents.JD_TO_MJD; + } + + /** + * Return the given date as a Modified Julian Date expressed in given timescale. + * + * @param ts time scale + * + * @return double representation of the given date as Modified Julian Date. + * + * @since 12.2 + */ + public double getMJD(final TimeScale ts) { + return this.getJD(ts) - DateComponents.JD_TO_MJD; + } + + /** + * Return the given date as a Julian Date expressed in UTC. + * + * @return double representation of the given date as Julian Date. + * + * @since 12.2 + */ + @DefaultDataContext + public double getJD() { + return getJD(TimeScalesFactory.getUTC()); + } + + /** + * Return the given date as a Julian Date expressed in given timescale. + * + * @param ts time scale + * + * @return double representation of the given date as Julian Date. + * + * @since 12.2 + */ + public double getJD(final TimeScale ts) { + return this.getComponents(ts).offsetFrom(DateTimeComponents.JULIAN_EPOCH) / Constants.JULIAN_DAY; + } } diff --git a/src/main/java/org/orekit/time/AbstractTimeScales.java b/src/main/java/org/orekit/time/AbstractTimeScales.java index b80a70bddd..7518fcaec6 100644 --- a/src/main/java/org/orekit/time/AbstractTimeScales.java +++ b/src/main/java/org/orekit/time/AbstractTimeScales.java @@ -78,16 +78,18 @@ protected abstract EOPHistory getEopHistory(IERSConventions conventions, @Override public UT1Scale getUT1(final IERSConventions conventions, final boolean simpleEOP) { - return ut1Map.computeIfAbsent( - new Pair<>(conventions, simpleEOP), - k -> getUT1(getEopHistory(conventions, simpleEOP))); + final Pair key = new Pair<>(conventions, simpleEOP); + synchronized (this) { + return ut1Map.computeIfAbsent(key, k -> getUT1(getEopHistory(conventions, simpleEOP))); + } } @Override public GMSTScale getGMST(final IERSConventions conventions, final boolean simpleEOP) { - return gmstMap.computeIfAbsent( - new Pair<>(conventions, simpleEOP), - k -> new GMSTScale(getUT1(conventions, simpleEOP))); + final Pair key = new Pair<>(conventions, simpleEOP); + synchronized (this) { + return gmstMap.computeIfAbsent(key, k -> new GMSTScale(getUT1(conventions, simpleEOP))); + } } @Override diff --git a/src/main/java/org/orekit/time/DateComponents.java b/src/main/java/org/orekit/time/DateComponents.java index f836e6e2bd..3955e969b4 100644 --- a/src/main/java/org/orekit/time/DateComponents.java +++ b/src/main/java/org/orekit/time/DateComponents.java @@ -17,7 +17,6 @@ package org.orekit.time; import java.io.Serializable; -import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.Locale; import java.util.regex.Matcher; @@ -103,6 +102,9 @@ public class DateComponents implements Serializable, Comparable */ public static final DateComponents MIN_EPOCH; + /** Offset between julian day epoch and modified julian day epoch. */ + public static final double JD_TO_MJD = 2400000.5; + /** Serializable UID. */ private static final long serialVersionUID = -2462694707837970938L; @@ -124,15 +126,10 @@ public class DateComponents implements Serializable, Comparable /** Formatting symbols used in {@link #toString()}. */ private static final DecimalFormatSymbols US_SYMBOLS = new DecimalFormatSymbols(Locale.US); - /** Format for years. */ - private static final DecimalFormat FOUR_DIGITS = new DecimalFormat("0000", US_SYMBOLS); - - /** Format for months and days. */ - private static final DecimalFormat TWO_DIGITS = new DecimalFormat("00", US_SYMBOLS); - /** Offset between J2000 epoch and modified julian day epoch. */ private static final int MJD_TO_J2000 = 51544; + /** Basic and extended format calendar date. */ private static final Pattern CALENDAR_FORMAT = Pattern.compile("^(-?\\d\\d\\d\\d)-?(\\d\\d)-?(\\d\\d)$"); @@ -473,11 +470,7 @@ public int getDayOfYear() { * @return string representation of the date. */ public String toString() { - return new StringBuilder(). - append(FOUR_DIGITS.format(year)).append('-'). - append(TWO_DIGITS.format(month)).append('-'). - append(TWO_DIGITS.format(day)). - toString(); + return String.format(Locale.US, "%04d-%02d-%02d", year, month, day); } /** {@inheritDoc} */ diff --git a/src/main/java/org/orekit/time/FieldAbsoluteDate.java b/src/main/java/org/orekit/time/FieldAbsoluteDate.java index e37c796c07..8d6441cac3 100644 --- a/src/main/java/org/orekit/time/FieldAbsoluteDate.java +++ b/src/main/java/org/orekit/time/FieldAbsoluteDate.java @@ -28,6 +28,8 @@ import org.hipparchus.Field; import org.hipparchus.FieldElement; import org.hipparchus.analysis.differentiation.Derivative; +import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2; +import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2Field; import org.hipparchus.complex.Complex; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathUtils; @@ -517,6 +519,20 @@ private FieldAbsoluteDate(final long epoch, final long tA, final TimeUnit tATime } } + /** + * Creates Field date with offset as univariate derivative of second order, with a unit linear coefficient in time. + * @return univariate derivative 2 date + * @since 12.2 + */ + public FieldAbsoluteDate> toFUD2Field() { + final FieldUnivariateDerivative2Field fud2Field = FieldUnivariateDerivative2Field.getUnivariateDerivative2Field(field); + final AbsoluteDate date = toAbsoluteDate(); + final T fieldShift = durationFrom(date); + final FieldUnivariateDerivative2 fud2Shift = new FieldUnivariateDerivative2<>(fieldShift, field.getOne(), + field.getZero()); + return new FieldAbsoluteDate<>(fud2Field, date).shiftedBy(fud2Shift); + } + /** Extract time components from an instant within the day. * @param instant instant to extract the number of seconds within the day * @return time components @@ -1766,6 +1782,54 @@ public AbsoluteDate toAbsoluteDate() { public boolean hasZeroField() { return (offset instanceof Derivative || offset instanceof Complex) && offset.subtract(offset.getReal()).isZero(); } + + /** + * Return the given date as a Modified Julian Date expressed in UTC. + * + * @return double representation of the given date as Modified Julian Date. + * @since 12.2 + */ + @DefaultDataContext + public T getMJD() { + return this.getMJD(TimeScalesFactory.getUTC()); + } + + /** + * Return the given date as a Modified Julian Date expressed in given timescale. + * + * @param ts time scale + * @return double representation of the given date as Modified Julian Date. + * @since 12.2 + */ + public T getMJD(final TimeScale ts) { + final AbsoluteDate absoluteDate = toAbsoluteDate(); + final T shift = durationFrom(absoluteDate).divide(Constants.JULIAN_DAY); + return shift.add(absoluteDate.getMJD(ts)); + } + + /** + * Return the given date as a Julian Date expressed in UTC. + * + * @return double representation of the given date as Julian Date. + * @since 12.2 + */ + @DefaultDataContext + public T getJD() { + return getJD(TimeScalesFactory.getUTC()); + } + + /** + * Return the given date as a Julian Date expressed in given timescale. + * + * @param ts time scale + * @return double representation of the given date as Julian Date. + * @since 12.2 + */ + public T getJD(final TimeScale ts) { + final AbsoluteDate absoluteDate = toAbsoluteDate(); + final T shift = durationFrom(absoluteDate).divide(Constants.JULIAN_DAY); + return shift.add(absoluteDate.getJD(ts)); + } } diff --git a/src/main/java/org/orekit/time/PreloadedTimeScales.java b/src/main/java/org/orekit/time/PreloadedTimeScales.java index 48631ef9ed..ca32861d0b 100644 --- a/src/main/java/org/orekit/time/PreloadedTimeScales.java +++ b/src/main/java/org/orekit/time/PreloadedTimeScales.java @@ -110,10 +110,12 @@ public UTCScale getUTC() { @Override protected EOPHistory getEopHistory(final IERSConventions conventions, final boolean simpleEOP) { + final Collection data; + synchronized (this) { + data = eopMap.computeIfAbsent(conventions, c -> eopSupplier.apply(c, this)); + } return new EOPHistory(conventions, EOPHistory.DEFAULT_INTERPOLATION_DEGREE, - eopMap.computeIfAbsent(conventions, c -> eopSupplier.apply(c, this)), - simpleEOP, - this); + data, simpleEOP, this); } @Override diff --git a/src/main/java/org/orekit/utils/CartesianCovarianceUtils.java b/src/main/java/org/orekit/utils/CartesianCovarianceUtils.java new file mode 100644 index 0000000000..e930961e25 --- /dev/null +++ b/src/main/java/org/orekit/utils/CartesianCovarianceUtils.java @@ -0,0 +1,108 @@ +/* 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.utils; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.linear.MatrixUtils; +import org.hipparchus.linear.RealMatrix; +import org.orekit.frames.Frame; +import org.orekit.frames.KinematicTransform; +import org.orekit.frames.LOFType; +import org.orekit.frames.Transform; +import org.orekit.time.AbsoluteDate; + +/** + * Utility class for conversions related to Cartesian covariance matrices. + * + * @author Romain Serra + * @since 12.2 + */ +public class CartesianCovarianceUtils { + + /** + * Private constructor. + */ + private CartesianCovarianceUtils() { + // utility class + } + + /** + * Convert input position-velocity covariance matrix between reference frames. + * @param inputFrame input frame + * @param outputFrame output frame + * @param covarianceMatrix position-velocity covariance matrix in reference frame + * @param date epoch + * @return converted covariance matrix + */ + public static RealMatrix changeReferenceFrame(final Frame inputFrame, final RealMatrix covarianceMatrix, + final AbsoluteDate date, final Frame outputFrame) { + final KinematicTransform kinematicTransform = inputFrame.getKinematicTransformTo(outputFrame, date); + return changePositionVelocityFrame(covarianceMatrix, kinematicTransform); + } + + /** + * Convert input position-velocity covariance matrix from reference frame to local one. + * @param position position vector in reference frame + * @param velocity velocity vector in reference frame + * @param covarianceMatrix position-velocity covariance matrix in reference frame + * @param lofType output local orbital frame + * @return converted covariance matrix + */ + public static RealMatrix convertToLofType(final Vector3D position, final Vector3D velocity, + final RealMatrix covarianceMatrix, final LOFType lofType) { + final Transform transformFromInertial = transformToLofType(lofType, position, velocity); + return changePositionVelocityFrame(covarianceMatrix, transformFromInertial); + } + + /** + * Convert input position-velocity covariance matrix from local frame to reference one. + * @param position position vector in reference frame + * @param velocity velocity vector in reference frame + * @param covarianceMatrix position-velocity covariance matrix in local frame + * @param lofType input local orbital frame + * @return converted covariance matrix + */ + public static RealMatrix convertFromLofType(final LOFType lofType, final RealMatrix covarianceMatrix, + final Vector3D position, final Vector3D velocity) { + final Transform transformFromInertial = transformToLofType(lofType, position, velocity); + return changePositionVelocityFrame(covarianceMatrix, transformFromInertial.getInverse()); + } + + /** + * Get the transform from local orbital frame to reference frame. + * @param lofType input local frame type + * @param position position in reference frame + * @param velocity velocity in reference frame + * @return transform + */ + private static Transform transformToLofType(final LOFType lofType, final Vector3D position, + final Vector3D velocity) { + return lofType.transformFromInertial(null, new PVCoordinates(position, velocity)); + } + + /** + * Convert the input position-velocity covariance matrix according to input transformation. + * @param covarianceMatrix original covariance matrix + * @param kinematicTransform kinematic frame transform + * @return transformed covariance matrix + */ + private static RealMatrix changePositionVelocityFrame(final RealMatrix covarianceMatrix, + final KinematicTransform kinematicTransform) { + final RealMatrix jacobianTransformPV = MatrixUtils.createRealMatrix(kinematicTransform.getPVJacobian()); + return jacobianTransformPV.multiply(covarianceMatrix.multiplyTransposed(jacobianTransformPV)); + } +} diff --git a/src/main/java/org/orekit/utils/ExtendedPositionProvider.java b/src/main/java/org/orekit/utils/ExtendedPositionProvider.java index 1df3ceedd0..9f8a777750 100644 --- a/src/main/java/org/orekit/utils/ExtendedPositionProvider.java +++ b/src/main/java/org/orekit/utils/ExtendedPositionProvider.java @@ -20,7 +20,6 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2; -import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2Field; import org.hipparchus.analysis.differentiation.UnivariateDerivative2; import org.hipparchus.analysis.differentiation.UnivariateDerivative2Field; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; @@ -68,13 +67,7 @@ default TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final */ default > TimeStampedFieldPVCoordinates getPVCoordinates(final FieldAbsoluteDate date, final Frame frame) { - final Field field = date.getField(); - final FieldUnivariateDerivative2Field fud2Field = FieldUnivariateDerivative2Field.getUnivariateDerivative2Field(field); - final T fieldShift = date.durationFrom(date.toAbsoluteDate()); - final FieldUnivariateDerivative2 fud2Shift = new FieldUnivariateDerivative2<>(fieldShift, field.getOne(), - field.getZero()); - final FieldAbsoluteDate> fud2Date = new FieldAbsoluteDate<>(fud2Field, - date.toAbsoluteDate()).shiftedBy(fud2Shift); + final FieldAbsoluteDate> fud2Date = date.toFUD2Field(); final FieldVector3D> fud2Position = getPosition(fud2Date, frame); final FieldVector3D position = new FieldVector3D<>(fud2Position.getX().getValue(), fud2Position.getY().getValue(), fud2Position.getZ().getValue()); diff --git a/src/main/java/org/orekit/utils/FieldAbsolutePVCoordinates.java b/src/main/java/org/orekit/utils/FieldAbsolutePVCoordinates.java index c83a2cc435..9946056c6d 100644 --- a/src/main/java/org/orekit/utils/FieldAbsolutePVCoordinates.java +++ b/src/main/java/org/orekit/utils/FieldAbsolutePVCoordinates.java @@ -18,6 +18,7 @@ import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.DerivativeStructure; import org.hipparchus.analysis.differentiation.FieldDerivative; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; @@ -85,6 +86,14 @@ public FieldAbsolutePVCoordinates(final Frame frame, final TimeStampedFieldPVCoo this.frame = frame; } + /** Build from Field and non-Fielded object. + * @param field field + * @param pva non-Field AbsolutePVCoordinates + */ + public FieldAbsolutePVCoordinates(final Field field, final AbsolutePVCoordinates pva) { + this(pva.getFrame(), new TimeStampedFieldPVCoordinates<>(field, pva)); + } + /** Multiplicative constructor *

      Build a FieldAbsolutePVCoordinates from another one and a scale factor.

      *

      The TimeStampedFieldPVCoordinates built will be a * AbsPva

      diff --git a/src/main/java/org/orekit/utils/units/UnitsCache.java b/src/main/java/org/orekit/utils/units/UnitsCache.java index 1972a14872..5e61d68dcd 100644 --- a/src/main/java/org/orekit/utils/units/UnitsCache.java +++ b/src/main/java/org/orekit/utils/units/UnitsCache.java @@ -50,7 +50,9 @@ public Unit getUnits(final String specification) { return Unit.NONE; } - return cache.computeIfAbsent(specification, s -> Unit.parse(specification)); + synchronized (cache) { + return cache.computeIfAbsent(specification, s -> Unit.parse(specification)); + } } diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_ca.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_ca.utf8 index 56517df3ec..3cae0bdf1f 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_ca.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_ca.utf8 @@ -247,6 +247,9 @@ INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION = estat inicial no especificat # target event date must be before {1} by {3,number,0.0##############E0} seconds or after {2} by {3,number,0.0##############E0} seconds, but target event date {0} is {4,number,0.0##############E0} seconds before {1} and {5,number,0.0##############E0} seconds after {2} so it cannot be added EVENT_DATE_TOO_CLOSE = la data objectiu de l''esdeveniment ha de ser anterior a {1} de {3,number,0.0##############E0} segons o posterior a {2} de {3,number,0.0##############E0} segons; no obstant, la data objectiu {0} és {4,number,0.0##############E0} segons abans de {1} i {5,number,0.0##############E0} segons després de {2}, per això no es pot afegir +# trying to propagate Cartesian adjoint whilst integration equations of motion with non-Cartesian coordinates +WRONG_COORDINATES_FOR_ADJOINT_EQUATION = + # unable to read header record from JPL ephemerides binary file {0} UNABLE_TO_READ_JPL_HEADER = impossible de llegir la capçalera del fitxer binari d''efemèrides del JPL {0} @@ -887,3 +890,10 @@ WALKER_INCONSISTENT_PLANES = # Infinite value appears during computation of atmospheric density in NRLMSISE00 model INFINITE_NRLMSISE00_DENSITY = + +# Propagator builder cannot be cloned +PROPAGATOR_BUILDER_NOT_CLONEABLE = + +# field "{0}" is too long, maximum length is {1} characters +FIELD_TOO_LONG = + diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_da.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_da.utf8 index 03dfe749fe..2c3daa1666 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_da.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_da.utf8 @@ -247,6 +247,9 @@ INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION = initialstatus er ikke præci # target event date must be before {1} by {3,number,0.0##############E0} seconds or after {2} by {3,number,0.0##############E0} seconds, but target event date {0} is {4,number,0.0##############E0} seconds before {1} and {5,number,0.0##############E0} seconds after {2} so it cannot be added EVENT_DATE_TOO_CLOSE = +# trying to propagate Cartesian adjoint whilst integration equations of motion with non-Cartesian coordinates +WRONG_COORDINATES_FOR_ADJOINT_EQUATION = + # unable to read header record from JPL ephemerides binary file {0} UNABLE_TO_READ_JPL_HEADER = kan ikke læse startblokken fra den binære JPL efemeridefil {0} @@ -887,3 +890,9 @@ WALKER_INCONSISTENT_PLANES = # Infinite value appears during computation of atmospheric density in NRLMSISE00 model INFINITE_NRLMSISE00_DENSITY = + +# Propagator builder cannot be cloned +PROPAGATOR_BUILDER_NOT_CLONEABLE = + +# field "{0}" is too long, maximum length is {1} characters +FIELD_TOO_LONG = diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_de.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_de.utf8 index 3732af6ee1..f6f7b992e6 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_de.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_de.utf8 @@ -247,6 +247,9 @@ INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION = Anfangs Status nicht spezifi # target event date must be before {1} by {3,number,0.0##############E0} seconds or after {2} by {3,number,0.0##############E0} seconds, but target event date {0} is {4,number,0.0##############E0} seconds before {1} and {5,number,0.0##############E0} seconds after {2} so it cannot be added EVENT_DATE_TOO_CLOSE = +# trying to propagate Cartesian adjoint whilst integration equations of motion with non-Cartesian coordinates +WRONG_COORDINATES_FOR_ADJOINT_EQUATION = + # unable to read header record from JPL ephemerides binary file {0} UNABLE_TO_READ_JPL_HEADER = Die Kopfzeile der JPL Ephemerides Binärdatei {0} ist nicht lesbar @@ -887,3 +890,10 @@ WALKER_INCONSISTENT_PLANES = # Infinite value appears during computation of atmospheric density in NRLMSISE00 model INFINITE_NRLMSISE00_DENSITY = + +# Propagator builder cannot be cloned +PROPAGATOR_BUILDER_NOT_CLONEABLE = + +# field "{0}" is too long, maximum length is {1} characters +FIELD_TOO_LONG = + diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_el.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_el.utf8 index 5c3a6ccb88..9b79896da4 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_el.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_el.utf8 @@ -247,6 +247,9 @@ INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION = H αρχική κατάστ # target event date must be before {1} by {3,number,0.0##############E0} seconds or after {2} by {3,number,0.0##############E0} seconds, but target event date {0} is {4,number,0.0##############E0} seconds before {1} and {5,number,0.0##############E0} seconds after {2} so it cannot be added EVENT_DATE_TOO_CLOSE = +# trying to propagate Cartesian adjoint whilst integration equations of motion with non-Cartesian coordinates +WRONG_COORDINATES_FOR_ADJOINT_EQUATION = + # unable to read header record from JPL ephemerides binary file {0} UNABLE_TO_READ_JPL_HEADER = αδύνατο να διαβάσει καταγραφή επικεφαλίδος από το JPL δυαδικό αρχείο {0} @@ -887,3 +890,9 @@ WALKER_INCONSISTENT_PLANES = # Infinite value appears during computation of atmospheric density in NRLMSISE00 model INFINITE_NRLMSISE00_DENSITY = + +# Propagator builder cannot be cloned +PROPAGATOR_BUILDER_NOT_CLONEABLE = + +# field "{0}" is too long, maximum length is {1} characters +FIELD_TOO_LONG = diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_en.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_en.utf8 index d4a40f432c..4ca787a237 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_en.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_en.utf8 @@ -247,6 +247,9 @@ INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION = initial state not specified # target event date must be before {1} by {3,number,0.0##############E0} seconds or after {2} by {3,number,0.0##############E0} seconds, but target event date {0} is {4,number,0.0##############E0} seconds before {1} and {5,number,0.0##############E0} seconds after {2} so it cannot be added EVENT_DATE_TOO_CLOSE = target event date must be before {1} by {3,number,0.0##############E0} seconds or after {2} by {3,number,0.0##############E0} seconds, but target event date {0} is {4,number,0.0##############E0} seconds before {1} and {5,number,0.0##############E0} seconds after {2} so it cannot be added +# trying to propagate Cartesian adjoint whilst integration equations of motion with non-Cartesian coordinates +WRONG_COORDINATES_FOR_ADJOINT_EQUATION = trying to propagate Cartesian adjoint whilst integration equations of motion with non-Cartesian coordinates + # unable to read header record from JPL ephemerides binary file {0} UNABLE_TO_READ_JPL_HEADER = unable to read header record from JPL ephemerides binary file {0} @@ -887,3 +890,9 @@ WALKER_INCONSISTENT_PLANES = number of planes {0} is inconsistent with number of # Infinite value appears during computation of atmospheric density in NRLMSISE00 model INFINITE_NRLMSISE00_DENSITY = Infinite value appears during computation of atmospheric density in NRLMSISE00 model + +# Propagator builder cannot be cloned +PROPAGATOR_BUILDER_NOT_CLONEABLE = Propagator builder cannot be cloned + +# field "{0}" is too long, maximum length is {1} characters +FIELD_TOO_LONG = field "{0}" is too long, maximum length is {1} characters diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_es.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_es.utf8 index 0c6363d924..83a088108c 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_es.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_es.utf8 @@ -247,6 +247,9 @@ INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION = estado inicial no especifica # target event date must be before {1} by {3,number,0.0##############E0} seconds or after {2} by {3,number,0.0##############E0} seconds, but target event date {0} is {4,number,0.0##############E0} seconds before {1} and {5,number,0.0##############E0} seconds after {2} so it cannot be added EVENT_DATE_TOO_CLOSE = la fecha objetivo del evento debe ser anterior a {1} de {3,number,0.0##############E0} segundos o posterior a {2} de {3,number,0.0##############E0} segundos; sin embargo, la fecha objetivo {0} es {4,number,0.0##############E0} segundos antes de {1} y {5,number,0.0##############E0} segundos después de {2}, por lo que no puede añadirse +# trying to propagate Cartesian adjoint whilst integration equations of motion with non-Cartesian coordinates +WRONG_COORDINATES_FOR_ADJOINT_EQUATION = + # unable to read header record from JPL ephemerides binary file {0} UNABLE_TO_READ_JPL_HEADER = imposible de leer el encabezado del fichero binario de efemérides del JPL {0} @@ -888,3 +891,8 @@ WALKER_INCONSISTENT_PLANES = # Infinite value appears during computation of atmospheric density in NRLMSISE00 model INFINITE_NRLMSISE00_DENSITY = +# Propagator builder cannot be cloned +PROPAGATOR_BUILDER_NOT_CLONEABLE = + +# field "{0}" is too long, maximum length is {1} characters +FIELD_TOO_LONG = diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_fr.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_fr.utf8 index 5c8ccff1ef..c703dee0aa 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_fr.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_fr.utf8 @@ -247,6 +247,9 @@ INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION = état initial non spécifié # target event date must be before {1} by {3,number,0.0##############E0} seconds or after {2} by {3,number,0.0##############E0} seconds, but target event date {0} is {4,number,0.0##############E0} seconds before {1} and {5,number,0.0##############E0} seconds after {2} so it cannot be added EVENT_DATE_TOO_CLOSE = la date cible de l''évènement doit être antérieure à {1} de {3,number,0.0##############E0} secondes ou postérieure à {2} de {3,number,0.0##############E0} secondes, cependant la date cible {0} est {4,number,0.0##############E0} secondes avant {1} et {5,number,0.0##############E0} après {2} donc elle ne peut pas être ajoutée +# trying to propagate Cartesian adjoint whilst integration equations of motion with non-Cartesian coordinates +WRONG_COORDINATES_FOR_ADJOINT_EQUATION = tentative de propagation de variables adjointes cartésiennes alors que les équations du mouvement son intégrées avec des coordonnées non cartésiennes + # unable to read header record from JPL ephemerides binary file {0} UNABLE_TO_READ_JPL_HEADER = impossible de lire l''en-tête du fichier d''éphémérides du JPL {0} @@ -888,3 +891,8 @@ WALKER_INCONSISTENT_PLANES = le nombre {0} de plans est incohérent avec le nomb # Infinite value appears during computation of atmospheric density in NRLMSISE00 model INFINITE_NRLMSISE00_DENSITY = Valeur infinie lors du calcul de la densité atmosphérique dans le modèle NRLMSISE00 +# Propagator builder cannot be cloned +PROPAGATOR_BUILDER_NOT_CLONEABLE = Le constructor de propagateur ne peut pas être cloné + +# field "{0}" is too long, maximum length is {1} characters +FIELD_TOO_LONG = le champ « {0} » est trop long, la longueur maximale est de {1} caractères diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_gl.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_gl.utf8 index 01cef4f554..60d61c43e9 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_gl.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_gl.utf8 @@ -247,6 +247,9 @@ INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION = estado inicial non especific # target event date must be before {1} by {3,number,0.0##############E0} seconds or after {2} by {3,number,0.0##############E0} seconds, but target event date {0} is {4,number,0.0##############E0} seconds before {1} and {5,number,0.0##############E0} seconds after {2} so it cannot be added EVENT_DATE_TOO_CLOSE = +# trying to propagate Cartesian adjoint whilst integration equations of motion with non-Cartesian coordinates +WRONG_COORDINATES_FOR_ADJOINT_EQUATION = + # unable to read header record from JPL ephemerides binary file {0} UNABLE_TO_READ_JPL_HEADER = imposible de ler o encabezado do ficheiro de efemérides do JPL {0} @@ -888,3 +891,8 @@ WALKER_INCONSISTENT_PLANES = # Infinite value appears during computation of atmospheric density in NRLMSISE00 model INFINITE_NRLMSISE00_DENSITY = +# Propagator builder cannot be cloned +PROPAGATOR_BUILDER_NOT_CLONEABLE = + +# field "{0}" is too long, maximum length is {1} characters +FIELD_TOO_LONG = diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_it.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_it.utf8 index 839812ba7a..08605ce363 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_it.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_it.utf8 @@ -247,6 +247,9 @@ INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION = stato iniziale non specifica # target event date must be before {1} by {3,number,0.0##############E0} seconds or after {2} by {3,number,0.0##############E0} seconds, but target event date {0} is {4,number,0.0##############E0} seconds before {1} and {5,number,0.0##############E0} seconds after {2} so it cannot be added EVENT_DATE_TOO_CLOSE = la data obiettivo dell'evento deve essere prima del {1} di {3,number,0.0##############E0} secondi o dopo il {2} di {3,number,0.0##############E0} secondi, ma la data obiettivo dell'evento {0} è {4,number,0.0##############E0} secondi prima del {1} e {5,number,0.0##############E0} secondi dopo il {2} quindi non può essere aggiunta +# trying to propagate Cartesian adjoint whilst integration equations of motion with non-Cartesian coordinates +WRONG_COORDINATES_FOR_ADJOINT_EQUATION = + # unable to read header record from JPL ephemerides binary file {0} UNABLE_TO_READ_JPL_HEADER = impossibile leggere l''intestazione del file di effemeridi del JPL {0} @@ -887,3 +890,9 @@ WALKER_INCONSISTENT_PLANES = # Infinite value appears during computation of atmospheric density in NRLMSISE00 model INFINITE_NRLMSISE00_DENSITY = + +# Propagator builder cannot be cloned +PROPAGATOR_BUILDER_NOT_CLONEABLE = + +# field "{0}" is too long, maximum length is {1} characters +FIELD_TOO_LONG = diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_no.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_no.utf8 index b5836a1f62..f26e4d3390 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_no.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_no.utf8 @@ -247,6 +247,9 @@ INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION = initialstatusen er ikke pres # target event date must be before {1} by {3,number,0.0##############E0} seconds or after {2} by {3,number,0.0##############E0} seconds, but target event date {0} is {4,number,0.0##############E0} seconds before {1} and {5,number,0.0##############E0} seconds after {2} so it cannot be added EVENT_DATE_TOO_CLOSE = +# trying to propagate Cartesian adjoint whilst integration equations of motion with non-Cartesian coordinates +WRONG_COORDINATES_FOR_ADJOINT_EQUATION = + # unable to read header record from JPL ephemerides binary file {0} UNABLE_TO_READ_JPL_HEADER = umulig å lese startblokken fra den binære JPL efemeridefilen {0} @@ -887,3 +890,9 @@ WALKER_INCONSISTENT_PLANES = # Infinite value appears during computation of atmospheric density in NRLMSISE00 model INFINITE_NRLMSISE00_DENSITY = + +# Propagator builder cannot be cloned +PROPAGATOR_BUILDER_NOT_CLONEABLE = + +# field "{0}" is too long, maximum length is {1} characters +FIELD_TOO_LONG = diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_ro.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_ro.utf8 index aaf4d32ab9..eafb4b6a66 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_ro.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_ro.utf8 @@ -247,6 +247,9 @@ INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION = stare inițială nespecifica # target event date must be before {1} by {3,number,0.0##############E0} seconds or after {2} by {3,number,0.0##############E0} seconds, but target event date {0} is {4,number,0.0##############E0} seconds before {1} and {5,number,0.0##############E0} seconds after {2} so it cannot be added EVENT_DATE_TOO_CLOSE = data evenimentului țintă trebuie să fie înainte de {1} cu {3,number,0.0##############E0} secunde sau după [2} cu {3,number,0.0##############E0} secunde, dar data evenimentului țintă {0} este cu {4,number,0.0##############E0} secunde înainte de {1} și {5,number,0.0##############E0} sende după {2} deci nu poate fi adăugată +# trying to propagate Cartesian adjoint whilst integration equations of motion with non-Cartesian coordinates +WRONG_COORDINATES_FOR_ADJOINT_EQUATION = + # unable to read header record from JPL ephemerides binary file {0} UNABLE_TO_READ_JPL_HEADER = imposibil de citit antetul fișierului de efemeride JPL {0} @@ -887,3 +890,10 @@ WALKER_INCONSISTENT_PLANES = numărul de planuri {0} este neconsecvent cu număr # Infinite value appears during computation of atmospheric density in NRLMSISE00 model INFINITE_NRLMSISE00_DENSITY = valori infinite au fost detectate în timpul calculului densității atmosferice pentru modelul NRLMSISE00 + +# Propagator builder cannot be cloned +PROPAGATOR_BUILDER_NOT_CLONEABLE = + +# field "{0}" is too long, maximum length is {1} characters +FIELD_TOO_LONG = + diff --git a/src/site/markdown/data/supported-data-types.md b/src/site/markdown/data/supported-data-types.md index 449687fb8d..c9816a8aa8 100644 --- a/src/site/markdown/data/supported-data-types.md +++ b/src/site/markdown/data/supported-data-types.md @@ -39,41 +39,42 @@ used up to 2009, new format used since 2010, the csv format and the xml format a Thevsupported formats for `finals2000A` files for IAU-2006/2000A and the finals files for IAU-1980 are both the XML format and the columns format. -| data type | format | default naming pattern | source | -|------------------------------------------------------------------------------------------|-------------------------------------------|---------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------| -| leap seconds introduction history | USNO tai-utc | tai-utc.dat[.gz\|.Z] | [https://maia.usno.navy.mil/ser7/tai-utc.dat](https://maia.usno.navy.mil/ser7/tai-utc.dat) | -| leap seconds introduction history | IERS history | UTC-TAI.history[.gz\|.Z] | [https://hpiers.obspm.fr/eoppc/bul/bulc/UTC-TAI.history](https://hpiers.obspm.fr/eoppc/bul/bulc/UTC-TAI.history) | -| weekly Earth Orientation Parameters, IAU-1980 and IAU-2000, rapid service and prediction | IERS Bulletin A | bulletina-xxxx-\#\#\#{.txt\|.csv\|xml}[.gz\|.Z] | [https://datacenter.iers.org/products/eop/rapid/bulletina/](https://datacenter.iers.org/products/eop/rapid/bulletina/) | -| monthly Earth Orientation Parameters model IAU 2006/2000A, final values | IERS Bulletin B | bulletinb-\#\#\#{.txt\|.csv\|.xml}[.gz\|.Z] | [https://datacenter.iers.org/products/eop/bulletinb/format_2009/](https://datacenter.iers.org/products/eop/bulletinb/format_2009/) | -| yearly Earth Orientation Parameters model IAU 2006/2000A | IERS EOP C04 | eopc04*{.##\|.csv\|.xml}[.gz\|.Z] | [https://datacenter.iers.org/products/eop/long-term/](https://datacenter.iers.org/products/eop/long-term/) | -| Earth Orientation Parameters model IAU 2006/2000A | IERS standard EOP | finals2000A.\*.[.gz\|.Z] | [https://datacenter.iers.org/data/9/finals2000A.all](https://datacenter.iers.org/data/9/finals2000A.all) | -| Earth Orientation Parameters model IAU 1980 | IERS standard EOP | finals.\*.[.gz\|.Z] | [https://datacenter.iers.org/data/7/finals.all](https://datacenter.iers.org/data/7/finals.all) | -| JPL DE 4xx planets ephemerides | DE 4xx binary | (l/u)nx(m/p)\#\#\#\#.4\#\#[.gz\|.Z] | [https://ssd.jpl.nasa.gov/ftp/eph/planets/Linux/](https://ssd.jpl.nasa.gov/ftp/eph/planets/Linux/) | -| IMCCE inpop planets ephemerides | DE 4xx binary | inpop\*_m\#\#\#\#_p\#\#\#\#*.dat[.gz\|.Z] | [https://ftp.imcce.fr/pub/ephem/planets/inpop19a/](https://ftp.imcce.fr/pub/ephem/planets/inpop19a/) | -| Eigen gravity field (old format) | SHM format | eigen\_\*\_coef[.gz\|.Z] | [http://op.gfz-potsdam.de/grace/results/main\_RESULTS.html#gravity](http://op.gfz-potsdam.de/grace/results/main_RESULTS.html#gravity) | -| gravity fields from International Centre for Global Earth Models | ICGEM format | \*.gfc, g\#\#\#\_eigen\_\*\_coef[.gz\|.Z] | [http://icgem.gfz-potsdam.de/tom_longtime](http://icgem.gfz-potsdam.de/tom_longtime) | -| EGM gravity field | EGM format | egm\#\#\_to\#\*[.gz\|.Z] | [https://cddis.nasa.gov/926/egm96/getit.html](https://cddis.nasa.gov/926/egm96/getit.html) | -| Marshall Solar Activity Future Estimation | MSAFE format | jan\#\#\#\#f10.txt to dec\#\#\#\#f10[_prd].txt[.gz\|.Z] | [https://www.nasa.gov/msfcsolar/archivedforecast](https://www.nasa.gov/msfcsolar/archivedforecast) | -| Klobuchar coefficients | Bern Astronomical Institute format | CGIM\#\#\#0.\#\#N [.gz\|.Z] | [http://ftp.aiub.unibe.ch/CODE/](http://ftp.aiub.unibe.ch/CODE/) | -| Vienna Mapping Function | VMF | VMF\*.\#\#H | [https://vmf.geo.tuwien.ac.at/trop_products/GRID/](https://vmf.geo.tuwien.ac.at/trop_products/GRID/) | -| Global Ionosphere Map | ionex | \*\.\#\#i | [CDDIS](https://cddis.nasa.gov) | -| space weather | CSSI format | SpaceWeather-All-v1.2.txt | [ftp://ftp.agi.com/pub/DynamicEarthData/SpaceWeather-All-v1.2.txt](ftp://ftp.agi.com/pub/DynamicEarthData/SpaceWeather-All-v1.2.txt) | -| JB2008 SOLFSMY | Space Environment format | SOLFSMY.TXT | [https://sol.spacenvironment.net/JB2008/indices/SOLFSMY.TXT](https://sol.spacenvironment.net/JB2008/indices/SOLFSMY.TXT) | -| JB2008 DTC | Space Environment format | DTCFILE.TXT | [https://sol.spacenvironment.net/JB2008/indices/DTCFILE.TXT](https://sol.spacenvironment.net/JB2008/indices/DTCFILE.TXT) | -| EOP files to ITRF versions mapping | Orekit itrf-versions.conf | itrf-versions.conf | [Orekit Physical Data Archive](https://gitlab.orekit.org/orekit/orekit-data/-/archive/master/orekit-data-master.zip) | -| CCSDS Attitude Data Message | CCSDS ADM V1 and V2 (KVN and XML) | none (must be loaded explicitly) | various, can be produced by Orekit itself | -| CCSDS Orbit Data Message | CCSDS ODM V2 and V3 (KVN and XML) | none (must be loaded explicitly) | various, can be produced by Orekit itself | -| CCSDS Tracking Data Message | CCSDS TDM V1 and V2 (KVN and XML) | none (must be loaded explicitly) | various, can be produced by Orekit itself | -| CCSDS Navigation Data Message | CCSDS NDM V1 and V3 (KVN and XML) | none (must be loaded explicitly) | various, can be produced by Orekit itself | -| CCSDS Conjunction Data Message | CCSDS CDM V1 (KVN and XML) | none (must be loaded explicitly) | various, can be produced by Orekit itself | -| GNSS Antenna data | Antex | \*.atx | various, mainly [IGS](https://files.igs.org/pub/station/general/igs14.atx) | -| GNSS measurements | Receiver Independant EXchange Format 2-4 | \*.\"\#{od}\|\*.{crx\|rnx}[.gz\|.Z] | various, can be produced by Orekit itself | -| GNSS clock | rinex clock 3 | \*.clk[.gz\|.Z] | various | -| GNSS solutions | Solution Independant EXchange Format 2 | \*.snx[.gz\|.Z] | various | -| GNSS orbits | SP3 a, c and d | \*.sp3[.gz\|.Z] | various, can be produced by Orekit itself | -| GNSS navigation | based on RINEX 2-4 | \*.n[.gz\|.Z] | various | -| GNSS almanach | SEM and YUMA | | various | -| GNSS real time (navigation, clock...) | IGS SSR messages, through RTCM and NTRIP | none (streaming data) | various, sourcetable usually from [BKG](https://products.igs-ip.net/home) | -| laser ranging prediction file | CPF format | | various, mainly [CDDIS](https://cddis.nasa.gov) | -| laser ranging data | CRD format | | various, mainly [CDDIS](https://cddis.nasa.gov) | -| ocean loading coefficients | BLQ format | *.blq | [Onsala Space Observatory](http://holt.oso.chalmers.se/loading/) | +| data type | format | default naming pattern | source | +|------------------------------------------------------------------------------------------|------------------------------------------|---------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------| +| leap seconds introduction history | USNO tai-utc | tai-utc.dat[.gz\|.Z] | [https://maia.usno.navy.mil/ser7/tai-utc.dat](https://maia.usno.navy.mil/ser7/tai-utc.dat) | +| leap seconds introduction history | IERS history | UTC-TAI.history[.gz\|.Z] | [https://hpiers.obspm.fr/eoppc/bul/bulc/UTC-TAI.history](https://hpiers.obspm.fr/eoppc/bul/bulc/UTC-TAI.history) | +| weekly Earth Orientation Parameters, IAU-1980 and IAU-2000, rapid service and prediction | IERS Bulletin A | bulletina-xxxx-\#\#\#{.txt\|.csv\|xml}[.gz\|.Z] | [https://datacenter.iers.org/products/eop/rapid/bulletina/](https://datacenter.iers.org/products/eop/rapid/bulletina/) | +| monthly Earth Orientation Parameters model IAU 2006/2000A, final values | IERS Bulletin B | bulletinb-\#\#\#{.txt\|.csv\|.xml}[.gz\|.Z] | [https://datacenter.iers.org/products/eop/bulletinb/format_2009/](https://datacenter.iers.org/products/eop/bulletinb/format_2009/) | +| yearly Earth Orientation Parameters model IAU 2006/2000A | IERS EOP C04 | eopc04*{.##\|.csv\|.xml}[.gz\|.Z] | [https://datacenter.iers.org/products/eop/long-term/](https://datacenter.iers.org/products/eop/long-term/) | +| Earth Orientation Parameters model IAU 2006/2000A | IERS standard EOP | finals2000A.\*.[.gz\|.Z] | [https://datacenter.iers.org/data/9/finals2000A.all](https://datacenter.iers.org/data/9/finals2000A.all) | +| Earth Orientation Parameters model IAU 1980 | IERS standard EOP | finals.\*.[.gz\|.Z] | [https://datacenter.iers.org/data/7/finals.all](https://datacenter.iers.org/data/7/finals.all) | +| JPL DE 4xx planets ephemerides | DE 4xx binary | (l/u)nx(m/p)\#\#\#\#.4\#\#[.gz\|.Z] | [https://ssd.jpl.nasa.gov/ftp/eph/planets/Linux/](https://ssd.jpl.nasa.gov/ftp/eph/planets/Linux/) | +| IMCCE inpop planets ephemerides | DE 4xx binary | inpop\*_m\#\#\#\#_p\#\#\#\#*.dat[.gz\|.Z] | [https://ftp.imcce.fr/pub/ephem/planets/inpop19a/](https://ftp.imcce.fr/pub/ephem/planets/inpop19a/) | +| Eigen gravity field (old format) | SHM format | eigen\_\*\_coef[.gz\|.Z] | [http://op.gfz-potsdam.de/grace/results/main\_RESULTS.html#gravity](http://op.gfz-potsdam.de/grace/results/main_RESULTS.html#gravity) | +| gravity fields from International Centre for Global Earth Models | ICGEM format | \*.gfc, g\#\#\#\_eigen\_\*\_coef[.gz\|.Z] | [http://icgem.gfz-potsdam.de/tom_longtime](http://icgem.gfz-potsdam.de/tom_longtime) | +| EGM gravity field | EGM format | egm\#\#\_to\#\*[.gz\|.Z] | [https://cddis.nasa.gov/926/egm96/getit.html](https://cddis.nasa.gov/926/egm96/getit.html) | +| GRGM1200B and GRGM1200L gravity field | SHA format | sha.* | [https://pgda.gsfc.nasa.gov/products/75](https://pgda.gsfc.nasa.gov/products/75) | +| Marshall Solar Activity Future Estimation | MSAFE format | jan\#\#\#\#f10.txt to dec\#\#\#\#f10[_prd].txt[.gz\|.Z] | [https://www.nasa.gov/msfcsolar/archivedforecast](https://www.nasa.gov/msfcsolar/archivedforecast) | +| Klobuchar coefficients | Bern Astronomical Institute format | CGIM\#\#\#0.\#\#N [.gz\|.Z] | [http://ftp.aiub.unibe.ch/CODE/](http://ftp.aiub.unibe.ch/CODE/) | +| Vienna Mapping Function | VMF | VMF\*.\#\#H | [https://vmf.geo.tuwien.ac.at/trop_products/GRID/](https://vmf.geo.tuwien.ac.at/trop_products/GRID/) | +| Global Ionosphere Map | ionex | \*\.\#\#i | [CDDIS](https://cddis.nasa.gov) | +| space weather | CSSI format | SpaceWeather-All-v1.2.txt | [ftp://ftp.agi.com/pub/DynamicEarthData/SpaceWeather-All-v1.2.txt](ftp://ftp.agi.com/pub/DynamicEarthData/SpaceWeather-All-v1.2.txt) | +| JB2008 SOLFSMY | Space Environment format | SOLFSMY.TXT | [https://sol.spacenvironment.net/JB2008/indices/SOLFSMY.TXT](https://sol.spacenvironment.net/JB2008/indices/SOLFSMY.TXT) | +| JB2008 DTC | Space Environment format | DTCFILE.TXT | [https://sol.spacenvironment.net/JB2008/indices/DTCFILE.TXT](https://sol.spacenvironment.net/JB2008/indices/DTCFILE.TXT) | +| EOP files to ITRF versions mapping | Orekit itrf-versions.conf | itrf-versions.conf | [Orekit Physical Data Archive](https://gitlab.orekit.org/orekit/orekit-data/-/archive/master/orekit-data-master.zip) | +| CCSDS Attitude Data Message | CCSDS ADM V1 and V2 (KVN and XML) | none (must be loaded explicitly) | various, can be produced by Orekit itself | +| CCSDS Orbit Data Message | CCSDS ODM V2 and V3 (KVN and XML) | none (must be loaded explicitly) | various, can be produced by Orekit itself | +| CCSDS Tracking Data Message | CCSDS TDM V1 and V2 (KVN and XML) | none (must be loaded explicitly) | various, can be produced by Orekit itself | +| CCSDS Navigation Data Message | CCSDS NDM V1 and V3 (KVN and XML) | none (must be loaded explicitly) | various, can be produced by Orekit itself | +| CCSDS Conjunction Data Message | CCSDS CDM V1 (KVN and XML) | none (must be loaded explicitly) | various, can be produced by Orekit itself | +| GNSS Antenna data | Antex | \*.atx | various, mainly [IGS](https://files.igs.org/pub/station/general/igs14.atx) | +| GNSS measurements | Receiver Independant EXchange Format 2-4 | \*.\"\#{od}\|\*.{crx\|rnx}[.gz\|.Z] | various, can be produced by Orekit itself | +| GNSS clock | rinex clock 3 | \*.clk[.gz\|.Z] | various | +| GNSS solutions | Solution Independant EXchange Format 2 | \*.snx[.gz\|.Z] | various | +| GNSS orbits | SP3 a, c and d | \*.sp3[.gz\|.Z] | various, can be produced by Orekit itself | +| GNSS navigation | based on RINEX 2-4 | \*.n[.gz\|.Z] | various | +| GNSS almanach | SEM and YUMA | | various | +| GNSS real time (navigation, clock...) | IGS SSR messages, through RTCM and NTRIP | none (streaming data) | various, sourcetable usually from [BKG](https://products.igs-ip.net/home) | +| laser ranging prediction file | CPF format | | various, mainly [CDDIS](https://cddis.nasa.gov) | +| laser ranging data | CRD format | | various, mainly [CDDIS](https://cddis.nasa.gov) | +| ocean loading coefficients | BLQ format | *.blq | [Onsala Space Observatory](http://holt.oso.chalmers.se/loading/) | diff --git a/src/site/markdown/downloads.md.vm b/src/site/markdown/downloads.md.vm index 80738794d1..c2497ad264 100644 --- a/src/site/markdown/downloads.md.vm +++ b/src/site/markdown/downloads.md.vm @@ -45,7 +45,7 @@ with groupID org.orekit and artifactId orekit so maven internal mechanism will download automatically all artifacts and dependencies as required. -#set ( $versions = {"12.1.2": "2024-07-13", "12.1.1": "2024-06-25", "12.1": "2024-06-24", "12.0.2": "2024-03-15", "12.0.1": "2023-12-31", "12.0": "2023-11-08", "11.3.3": "2023-06-30", "11.3.2": "2023-02-17", "11.3.1": "2022-12-24", "11.3": "2022-10-25", "11.2.1": "2022-08-01", "11.2": "2022-06-20", "11.1.2": "2022-04-27", "11.1.1": "2022-03-17", "11.1": "2022-02-14", "11.0.2": "2021-11-24", "11.0.1": "2021-10-22", "11.0": "2021-09-20", "10.3.1": "2021-06-16", "10.3": "2020-12-21", "10.2": "2020-07-14", "10.1": "2020-02-19", "10.0": "2019-06-24", "9.3.1": "2019-03-16", "9.3": "2019-01-25", "9.2": "2018-05-26","9.1": "2017-11-26","9.0.1": "2017-11-03","9.0": "2017-07-26","8.0.1": "2017-11-03","8.0": "2016-06-30","7.2.1": "2017-11-03","7.2": "2016-04-05","7.1": "2016-02-07","7.0": "2015-01-11","6.1": "2013-12-13","6.0": "2013-04-23","5.0.3": "2011-07-13","5.0.2": "2011-07-11","5.0.1": "2011-04-18"} ) +#set ( $versions = {"12.2": "2024-10-18", "12.1.3": "2024-09-04", "12.1.2": "2024-07-13", "12.1.1": "2024-06-25", "12.1": "2024-06-24", "12.0.2": "2024-03-15", "12.0.1": "2023-12-31", "12.0": "2023-11-08", "11.3.3": "2023-06-30", "11.3.2": "2023-02-17", "11.3.1": "2022-12-24", "11.3": "2022-10-25", "11.2.1": "2022-08-01", "11.2": "2022-06-20", "11.1.2": "2022-04-27", "11.1.1": "2022-03-17", "11.1": "2022-02-14", "11.0.2": "2021-11-24", "11.0.1": "2021-10-22", "11.0": "2021-09-20", "10.3.1": "2021-06-16", "10.3": "2020-12-21", "10.2": "2020-07-14", "10.1": "2020-02-19", "10.0": "2019-06-24", "9.3.1": "2019-03-16", "9.3": "2019-01-25", "9.2": "2018-05-26","9.1": "2017-11-26","9.0.1": "2017-11-03","9.0": "2017-07-26","8.0.1": "2017-11-03","8.0": "2016-06-30","7.2.1": "2017-11-03","7.2": "2016-04-05","7.1": "2016-02-07","7.0": "2015-01-11","6.1": "2013-12-13","6.0": "2013-04-23","5.0.3": "2011-07-13","5.0.2": "2011-07-11","5.0.1": "2011-04-18"} ) #foreach( $version in $versions.entrySet() ) | package | link | diff --git a/src/site/markdown/faq.md b/src/site/markdown/faq.md index e5993cd9bf..09625dfb8d 100644 --- a/src/site/markdown/faq.md +++ b/src/site/markdown/faq.md @@ -158,6 +158,8 @@ Math to Hipparchus Orekit 12.1 | Hipparchus 3.1 Orekit 12.1.1 | Hipparchus 3.1 Orekit 12.1.2 | Hipparchus 3.1 + Orekit 12.1.3 | Hipparchus 3.1 + Orekit 12.2 | Hipparchus 3.1 ### Maven failed to compile Orekit and complained about a missing artifact. diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md index ff757c0412..3d135a5551 100644 --- a/src/site/markdown/index.md +++ b/src/site/markdown/index.md @@ -95,7 +95,7 @@ * central attraction * gravity models including time-dependent like trends and pulsations (automatic reading of ICGEM (new Eigen models), SHM (old Eigen models), - EGM and GRGS gravity field files formats, even compressed) + EGM, SHA (GRGM1200B and GRGM1200L) and GRGS gravity field files formats, even compressed) * atmospheric drag * third body attraction (with data for Sun, Moon and all solar systems planets) * radiation pressure with eclipses (multiple oblate spheroids occulting bodies, multiple coefficients for bow and wing models) @@ -196,6 +196,7 @@ * central body related attitude (nadir pointing, center pointing, target pointing, yaw compensation, yaw-steering), * orbit referenced attitudes (LOF aligned, offset on all axes), * space referenced attitudes (inertial, celestial body-pointed, spin-stabilized) + * attitude aligned with one target and constrained by another target * tabulated attitudes, either respective to inertial frame or respective to Local Orbital Frames * specific law for GNSS satellites: GPS (block IIA, block IIF, block IIF), GLONASS, GALILEO, BEIDOU (GEO, IGSO, MEO) * torque-free for general (non-symmetrical) body @@ -326,6 +327,12 @@ * sampling of zones of interest as grids of points * construction of trajectories using loxodromes (commonly, a rhumb line) + * Indirect optimal control + + * adjoint equations as defined by Pontryagin's Maximum Principle with Cartesian coordinates for a range of forces (gravitational, inertial) including J2 + * so-called energy cost functions (proportional to the integral of the control vector's squared norm), with Hamiltonian evaluation + * single shooting based on Newton algorithm for the case of fixed time, fixed Cartesian bounds + * Collisions * loading and writing of CCSDS Conjunction Data Messages (CDM in both KVN and XML formats) diff --git a/src/test/java/org/orekit/attitudes/AlignedAndConstrainedTest.java b/src/test/java/org/orekit/attitudes/AlignedAndConstrainedTest.java new file mode 100644 index 0000000000..597f9566ab --- /dev/null +++ b/src/test/java/org/orekit/attitudes/AlignedAndConstrainedTest.java @@ -0,0 +1,391 @@ +/* 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.UnivariateVectorFunction; +import org.hipparchus.analysis.differentiation.FiniteDifferencesDifferentiator; +import org.hipparchus.analysis.differentiation.UnivariateDerivative1; +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.Binary64; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.FastMath; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.orekit.Utils; +import org.orekit.bodies.CelestialBody; +import org.orekit.bodies.CelestialBodyFactory; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.frames.FieldTransform; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.frames.Transform; +import org.orekit.orbits.FieldCartesianOrbit; +import org.orekit.orbits.FieldOrbit; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.propagation.FieldPropagator; +import org.orekit.propagation.Propagator; +import org.orekit.propagation.analytical.FieldKeplerianPropagator; +import org.orekit.propagation.analytical.KeplerianPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.Constants; +import org.orekit.utils.IERSConventions; +import org.orekit.utils.PVCoordinates; + +import java.util.function.Function; + +public class AlignedAndConstrainedTest { + + @Test + public void testAlignmentsEarthSun() { + doTestAlignment(Vector3D.PLUS_K, PredefinedTarget.EARTH, + Vector3D.PLUS_I, PredefinedTarget.SUN, + t -> Vector3D.ZERO, + t -> sun.getPosition(t, eme2000), + 1.0e-15, 1.0e-15); + } + + @Test + public void testAlignmentsEarthSunField() { + final Binary64Field field = Binary64Field.getInstance(); + doTestAlignment(field, + Vector3D.PLUS_K, PredefinedTarget.EARTH, + Vector3D.PLUS_I, PredefinedTarget.SUN, + t -> FieldVector3D.getZero(field), + t -> sun.getPosition(t, eme2000), + 1.0e-15, 1.0e-15); + } + + @Test + public void testDerivativesEarthSun() { + doTestDerivatives(Vector3D.PLUS_K, PredefinedTarget.EARTH, + Vector3D.PLUS_I, PredefinedTarget.SUN, + 2.0e-15); + } + + @Test + public void testAlignmentsNadirNorth() { + doTestAlignment(Vector3D.PLUS_K, PredefinedTarget.NADIR, + Vector3D.PLUS_I, PredefinedTarget.NORTH, + t -> { + final Vector3D pH = orbit.shiftedBy(t.durationFrom(orbit.getDate())).getPosition(); + final GeodeticPoint gpH = earth.transform(pH, eme2000, t); + final GeodeticPoint gp0 = new GeodeticPoint(gpH.getLatitude(), gpH.getLongitude(), 0.0); + final Vector3D p0 = earth.transform(gp0); + final Transform earth2Inert = earth.getBodyFrame().getTransformTo(eme2000, t); + return earth2Inert.transformPosition(p0); + }, + t -> Vector3D.PLUS_K, + 1.0e-13, 4.0e-5); + } + + @Test + public void testAlignmentsNadirNorthField() { + final Binary64Field field = Binary64Field.getInstance(); + doTestAlignment(field, + Vector3D.PLUS_K, PredefinedTarget.NADIR, + Vector3D.PLUS_I, PredefinedTarget.NORTH, + t -> { + final FieldVector3D pH = getOrbit(field).shiftedBy(t.durationFrom(orbit.getDate())).getPosition(); + final FieldGeodeticPoint gpH = earth.transform(pH, eme2000, t); + final FieldGeodeticPoint gp0 = new FieldGeodeticPoint<>(gpH.getLatitude(), + gpH.getLongitude(), + field.getZero()); + final FieldVector3D p0 = earth.transform(gp0); + final FieldTransform earth2Inert = earth.getBodyFrame().getTransformTo(eme2000, t); + return earth2Inert.transformPosition(p0); + }, + t -> FieldVector3D.getPlusK(field), + 1.0e-15, 4.0e-5); + } + + @Test + public void testDerivativesNadirNorth() { + doTestDerivatives(Vector3D.PLUS_K, PredefinedTarget.NADIR, + Vector3D.PLUS_I, PredefinedTarget.NORTH, + 2.0e-15); + } + + @Test + public void testAlignmentsVelocityMomentum() { + doTestAlignment(Vector3D.MINUS_J, PredefinedTarget.VELOCITY, + Vector3D.MINUS_K, PredefinedTarget.MOMENTUM, + t -> orbit.shiftedBy(t.durationFrom(orbit.getDate())).getPVCoordinates().getVelocity().normalize(), + t -> orbit.shiftedBy(t.durationFrom(orbit.getDate())).getPVCoordinates().getMomentum().normalize(), + 1.0e-10, 1.0e-15); + } + + @Test + public void testAlignmentsVelocityMomentumField() { + final Binary64Field field = Binary64Field.getInstance(); + doTestAlignment(field, + Vector3D.MINUS_J, PredefinedTarget.VELOCITY, + Vector3D.MINUS_K, PredefinedTarget.MOMENTUM, + t -> getOrbit(field).shiftedBy(t.durationFrom(orbit.getDate())).getPVCoordinates().getVelocity().normalize(), + t -> getOrbit(field).shiftedBy(t.durationFrom(orbit.getDate())).getPVCoordinates().getMomentum().normalize(), + 1.0e-10, 1.0e-15); + } + + @Test + public void testDerivativesVelocityMomentum() { + doTestDerivatives(Vector3D.MINUS_J, PredefinedTarget.VELOCITY, + Vector3D.MINUS_K, PredefinedTarget.MOMENTUM, + 7.0e-16); + } + + @Test + public void testAlignmentsStationEast() { + doTestAlignment(Vector3D.PLUS_K, new GroundPointTarget(station), + Vector3D.PLUS_I, PredefinedTarget.EAST, + t -> earth.getBodyFrame().getTransformTo(eme2000, t).transformPosition(station), + t -> { + final Transform earth2Inert = earth.getBodyFrame().getTransformTo(eme2000, t); + final Vector3D pInert = orbit.shiftedBy(t.durationFrom(orbit.getDate())).getPosition(); + final GeodeticPoint gp = earth.transform(pInert, eme2000, t); + return earth2Inert.transformVector(gp.getEast()).normalize(); + }, + 4.0e-10, 1.0e-10); + } + + @Test + public void testAlignmentsStationEastField() { + final Binary64Field field = Binary64Field.getInstance(); + doTestAlignment(field, + Vector3D.PLUS_K, new GroundPointTarget(station), + Vector3D.PLUS_I, PredefinedTarget.EAST, + t -> earth.getBodyFrame().getTransformTo(eme2000, t).transformPosition(station), + t -> { + final FieldTransform earth2Inert = earth.getBodyFrame().getTransformTo(eme2000, t); + final FieldVector3D pInert = getOrbit(field).shiftedBy(t.durationFrom(orbit.getDate())).getPosition(); + final FieldGeodeticPoint gp = earth.transform(pInert, eme2000, t); + return earth2Inert.transformVector(gp.getEast()).normalize(); + }, + 4.0e-10, 1.0e-15); + } + + @Test + public void testDerivativesStationEast() { + doTestDerivatives(Vector3D.PLUS_K, new GroundPointTarget(station), + Vector3D.PLUS_I, PredefinedTarget.EAST, + 7.0e-13); + } + + private void doTestAlignment(final Vector3D primarySat, final TargetProvider primaryTarget, + final Vector3D secondarySat, final TargetProvider secondaryTarget, + final Function referencePrimaryProvider, + final Function referenceSecondaryProvider, + final double primaryTolerance, final double secondaryTolerance) { + + final Propagator propagator = new KeplerianPropagator(orbit); + propagator.setAttitudeProvider(new AlignedAndConstrained(primarySat, primaryTarget, + secondarySat, secondaryTarget, + sun, earth)); + + propagator.getMultiplexer().add(60.0, state -> { + final Vector3D satP = state.getPVCoordinates().getPosition(); + final Vector3D primaryP = referencePrimaryProvider.apply(state.getDate()); + final Vector3D secondaryP = referenceSecondaryProvider.apply(state.getDate()); + final Transform inertToSat = state.toTransform(); + final Vector3D primaryDir; + if (FastMath.abs(primaryP.getNorm() - 1.0) < 1.0e-10) { + // reference is a unit vector + primaryDir = primaryP; + } else { + // reference is a position + primaryDir = primaryP.subtract(satP); + } + final Vector3D secondaryDir; + if (FastMath.abs(secondaryP.getNorm() - 1.0) < 1.0e-10) { + // reference is a unit vector + secondaryDir = secondaryP; + } else { + // reference is a position + secondaryDir = secondaryP.subtract(satP); + } + Assertions.assertEquals(0, + Vector3D.angle(inertToSat.transformVector(primaryDir), primarySat), + primaryTolerance); + Assertions.assertEquals(0, + Vector3D.angle(inertToSat.transformVector(Vector3D.crossProduct(primaryDir, + secondaryDir)), + Vector3D.crossProduct(primarySat, secondarySat)), + secondaryTolerance); + }); + propagator.propagate(propagator.getInitialState().getDate().shiftedBy(3600)); + + } + + private > void doTestAlignment(final Field field, + final Vector3D primarySat, final TargetProvider primaryTarget, + final Vector3D secondarySat, final TargetProvider secondaryTarget, + final Function, FieldVector3D> referencePrimaryProvider, + final Function, FieldVector3D> referenceSecondaryProvider, + final double primaryTolerance, final double secondaryTolerance) { + + final FieldVector3D primarySatF = new FieldVector3D<>(field, primarySat); + final FieldVector3D secondarySatF = new FieldVector3D<>(field, secondarySat); + + final FieldPropagator propagator = new FieldKeplerianPropagator<>(getOrbit(field)); + propagator.setAttitudeProvider(new AlignedAndConstrained(primarySat, primaryTarget, + secondarySat, secondaryTarget, + sun, earth)); + + propagator.getMultiplexer().add(field.getZero().newInstance(60.0), state -> { + final FieldVector3D satP = state.getPVCoordinates().getPosition(); + final FieldVector3D primaryP = referencePrimaryProvider.apply(state.getDate()); + final FieldVector3D secondaryP = referenceSecondaryProvider.apply(state.getDate()); + final FieldTransform inertToSat = state.toTransform(); + final FieldVector3D primaryDir; + if (FastMath.abs(primaryP.getNorm().getReal() - 1.0) < 1.0e-10) { + // reference is a unit vector + primaryDir = primaryP; + } else { + // reference is a position + primaryDir = primaryP.subtract(satP); + } + final FieldVector3D secondaryDir; + if (FastMath.abs(secondaryP.getNorm().getReal() - 1.0) < 1.0e-10) { + // reference is a unit vector + secondaryDir = secondaryP; + } else { + // reference is a position + secondaryDir = secondaryP.subtract(satP); + } + Assertions.assertEquals(0, + FieldVector3D.angle(inertToSat.transformVector(primaryDir), primarySatF).getReal(), + primaryTolerance); + Assertions.assertEquals(0, + FieldVector3D.angle(inertToSat.transformVector(FieldVector3D.crossProduct(primaryDir, + secondaryDir)), + FieldVector3D.crossProduct(primarySatF, secondarySatF)).getReal(), + secondaryTolerance); + }); + propagator.propagate(propagator.getInitialState().getDate().shiftedBy(3600)); + + } + + private void doTestDerivatives(final Vector3D primarySat, final TargetProvider primaryTarget, + final Vector3D secondarySat, final TargetProvider secondaryTarget, + final double tolerance) { + + final AlignedAndConstrained aac = new AlignedAndConstrained(primarySat, primaryTarget, + secondarySat, secondaryTarget, + sun, earth); + + // evaluate quaternion derivatives using finite differences + final UnivariateVectorFunction q = dt -> { + final Attitude attitude = aac.getAttitude(orbit.shiftedBy(dt), orbit.getDate().shiftedBy(dt), eme2000); + final Rotation rotation = attitude.getRotation(); + return new double[] { + rotation.getQ0(), rotation.getQ1(), rotation.getQ2(), rotation.getQ3() + }; + }; + final UnivariateDerivative1[] qDot = new FiniteDifferencesDifferentiator(8, 0.1). + differentiate(q). + value(new UnivariateDerivative1(0.0, 1.0)); + + // evaluate quaternions derivatives using internal model + final FieldRotation r = aac. + getAttitude(orbit, orbit.getDate(), eme2000). + getOrientation(). + toUnivariateDerivative1Rotation(); + + Assertions.assertEquals(qDot[0].getDerivative(1), r.getQ0().getDerivative(1), tolerance); + Assertions.assertEquals(qDot[1].getDerivative(1), r.getQ1().getDerivative(1), tolerance); + Assertions.assertEquals(qDot[2].getDerivative(1), r.getQ2().getDerivative(1), tolerance); + Assertions.assertEquals(qDot[3].getDerivative(1), r.getQ3().getDerivative(1), tolerance); + + } + + private > FieldOrbit getOrbit(final Field field) { + return orbit.getType().convertToFieldOrbit(field, orbit); + } + + @ParameterizedTest + @EnumSource(value = PredefinedTarget.class) + void testGetAttitudeRotation(final PredefinedTarget target) { + // GIVEN + final GroundPointTarget groundPointTarget = new GroundPointTarget(new Vector3D(1., 2.)); + final AlignedAndConstrained alignedAndConstrained = new AlignedAndConstrained(Vector3D.PLUS_I, target, + Vector3D.MINUS_J, groundPointTarget, sun, earth); + // WHEN + final Rotation actualRotation = alignedAndConstrained.getAttitudeRotation(orbit, orbit.getDate(), orbit.getFrame()); + // THEN + final Rotation expectedRotation = alignedAndConstrained.getAttitude(orbit, orbit.getDate(), orbit.getFrame()).getRotation(); + Assertions.assertEquals(0., Rotation.distance(expectedRotation, actualRotation), 5e-9); + } + + @ParameterizedTest + @EnumSource(value = PredefinedTarget.class) + void testFieldGetAttitudeRotation(final PredefinedTarget target) { + // GIVEN + final GroundPointTarget groundPointTarget = new GroundPointTarget(new Vector3D(1., 2.)); + final AlignedAndConstrained alignedAndConstrained = new AlignedAndConstrained(Vector3D.PLUS_I, target, + Vector3D.MINUS_J, groundPointTarget, sun, earth); + final FieldOrbit fieldOrbit = new FieldCartesianOrbit<>(Binary64Field.getInstance(), orbit); + // WHEN + final FieldRotation actualRotation = alignedAndConstrained.getAttitudeRotation(fieldOrbit, fieldOrbit.getDate(), fieldOrbit.getFrame()); + // THEN + final FieldRotation expectedRotation = alignedAndConstrained.getAttitude(fieldOrbit, fieldOrbit.getDate(), fieldOrbit.getFrame()).getRotation(); + Assertions.assertEquals(0., Rotation.distance(expectedRotation.toRotation(), actualRotation.toRotation()), 5e-9); + } + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data"); + eme2000 = FramesFactory.getEME2000(); + orbit = new KeplerianOrbit(new PVCoordinates(new Vector3D(28812595.32012577, 5948437.4640250085, 0), + new Vector3D(0, 0, 3680.853673522056)), + eme2000, + new AbsoluteDate(2003, 3, 2, 13, 17, 7.865, TimeScalesFactory.getUTC()), + 3.986004415e14); + earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getITRF(IERSConventions.IERS_2010, true)); + sun = CelestialBodyFactory.getSun(); + station = earth.transform(new GeodeticPoint(FastMath.toRadians(0.33871), FastMath.toRadians(6.73054), 0.0)); + } + + @AfterEach + public void tearDown() { + eme2000 = null; + orbit = null; + earth = null; + sun = null; + station = null; + } + + private Frame eme2000; + private Orbit orbit; + private CelestialBody sun; + private OneAxisEllipsoid earth; + private Vector3D station; + +} diff --git a/src/test/java/org/orekit/attitudes/AttitudesSequenceTest.java b/src/test/java/org/orekit/attitudes/AttitudesSequenceTest.java index 85766d44d7..dc8ac2f18b 100644 --- a/src/test/java/org/orekit/attitudes/AttitudesSequenceTest.java +++ b/src/test/java/org/orekit/attitudes/AttitudesSequenceTest.java @@ -36,7 +36,6 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; import org.orekit.Utils; import org.orekit.bodies.CelestialBodyFactory; import org.orekit.bodies.GeodeticPoint; @@ -81,6 +80,9 @@ import org.orekit.utils.PVCoordinates; import org.orekit.utils.PVCoordinatesProvider; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + public class AttitudesSequenceTest { private AbsoluteDate lastChange; @@ -650,7 +652,7 @@ void testGetAttitudeRotation() { attitudesSequence.resetActiveProvider(attitudeProvider); final Frame frame = FramesFactory.getGCRF(); final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; - final PVCoordinatesProvider mockPvCoordinatesProvider = Mockito.mock(PVCoordinatesProvider.class); + final PVCoordinatesProvider mockPvCoordinatesProvider = mock(PVCoordinatesProvider.class); // WHEN final Rotation actualRotation = attitudesSequence.getAttitudeRotation(mockPvCoordinatesProvider, date, frame); // THEN @@ -669,8 +671,8 @@ void testGetAttitudeRotationFieldTest() { attitudesSequence.resetActiveProvider(attitudeProvider); final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(field, date); - final FieldPVCoordinatesProvider pvCoordinatesProvider = Mockito.mock(FieldPVCoordinatesProvider.class); - final Frame mockFrame = Mockito.mock(Frame.class); + final FieldPVCoordinatesProvider pvCoordinatesProvider = mock(FieldPVCoordinatesProvider.class); + final Frame mockFrame = mock(Frame.class); // WHEN final FieldRotation actualRotation = attitudesSequence.getAttitudeRotation(pvCoordinatesProvider, fieldDate, mockFrame); // THEN @@ -679,6 +681,34 @@ void testGetAttitudeRotationFieldTest() { Assertions.assertEquals(0., Rotation.distance(expectedRotation.toRotation(), actualRotation.toRotation())); } + /** Issue 1387. */ + @Test + public void testGetSwitches() { + // GIVEN + final double transitionTime = 1; + final AttitudeProvider mockPastAttitudeProvider = mock(AttitudeProvider.class); + final AttitudeProvider mockFutureAttitudeProvider = mock(AttitudeProvider.class); + final boolean switchOnIncrease = true; + final boolean switchOnDecrease = true; + final AngularDerivativesFilter filter = AngularDerivativesFilter.USE_R; + final EventDetector mockSwitchEvent = mock(EventDetector.class); + when(mockSwitchEvent.getThreshold()).thenReturn(transitionTime - 0.5); + + // Create attitudes sequence + final AttitudesSequence attitudesSequence = new AttitudesSequence(); + attitudesSequence.addSwitchingCondition(mockPastAttitudeProvider, mockFutureAttitudeProvider, mockSwitchEvent, + switchOnIncrease, switchOnDecrease, transitionTime, filter, null); + + + // WHEN + final List switches1 = attitudesSequence.getSwitches(); + final List switches2 = attitudesSequence.getSwitches(); + + // THEN + Assertions.assertEquals(1, switches1.size()); + Assertions.assertNotSame(switches1, switches2); + } + private static class TestAttitudeProvider implements AttitudeProvider { TestAttitudeProvider() { diff --git a/src/test/java/org/orekit/attitudes/FixedRateTest.java b/src/test/java/org/orekit/attitudes/FixedRateTest.java index 0d73c0366e..ca04c21fe1 100644 --- a/src/test/java/org/orekit/attitudes/FixedRateTest.java +++ b/src/test/java/org/orekit/attitudes/FixedRateTest.java @@ -22,6 +22,7 @@ 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.Binary64; import org.hipparchus.util.Binary64Field; import org.hipparchus.util.FastMath; import org.junit.jupiter.api.Assertions; @@ -30,11 +31,7 @@ import org.orekit.Utils; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; -import org.orekit.orbits.FieldKeplerianOrbit; -import org.orekit.orbits.FieldOrbit; -import org.orekit.orbits.KeplerianOrbit; -import org.orekit.orbits.Orbit; -import org.orekit.orbits.PositionAngleType; +import org.orekit.orbits.*; import org.orekit.propagation.FieldPropagator; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.Propagator; @@ -271,6 +268,43 @@ private > void doTestSpin(final Field field } + @Test + void testGetAttitudeRotation() { + // GIVEN + final PVCoordinates pv = + new PVCoordinates(new Vector3D(28812595.32012577, 5948437.4640250085, 0), + new Vector3D(0, 0, 3680.853673522056)); + final Orbit orbit = new CartesianOrbit(pv, FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, 3.986004415e14); + final Attitude attitude = new Attitude(orbit.getDate().shiftedBy(-100.), FramesFactory.getEME2000(), + new Rotation(0.48, 0.64, 0.36, 0.48, false), + Vector3D.ZERO, Vector3D.ZERO); + final FixedRate fixedRate = new FixedRate(attitude); + // WHEN + final Rotation actualRotation = fixedRate.getAttitudeRotation(orbit, orbit.getDate(), orbit.getFrame()); + // THEN + final Rotation expectedRotation = fixedRate.getAttitude(orbit, orbit.getDate(), orbit.getFrame()).getRotation(); + Assertions.assertEquals(0., Rotation.distance(expectedRotation, actualRotation), 2e-7); + } + + @Test + void testFieldGetAttitudeRotation() { + // GIVEN + final PVCoordinates pv = + new PVCoordinates(new Vector3D(28812595.32012577, 5948437.4640250085, 0), + new Vector3D(0, 0, 3680.853673522056)); + final Orbit orbit = new CartesianOrbit(pv, FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, 3.986004415e14); + final FieldOrbit fieldOrbit = new FieldCartesianOrbit<>(Binary64Field.getInstance(), orbit); + final Attitude attitude = new Attitude(orbit.getDate().shiftedBy(-100.), FramesFactory.getEME2000(), + new Rotation(0.48, 0.64, 0.36, 0.48, false), + Vector3D.ZERO, Vector3D.ZERO); + final FixedRate fixedRate = new FixedRate(attitude); + // WHEN + final FieldRotation actualRotation = fixedRate.getAttitudeRotation(fieldOrbit, fieldOrbit.getDate(), orbit.getFrame()); + // THEN + final FieldRotation expectedRotation = fixedRate.getAttitude(fieldOrbit, fieldOrbit.getDate(), orbit.getFrame()).getRotation(); + Assertions.assertEquals(0., Rotation.distance(expectedRotation.toRotation(), actualRotation.toRotation()), 2e-7); + } + @BeforeEach public void setUp() { Utils.setDataRoot("regular-data"); diff --git a/src/test/java/org/orekit/attitudes/LofOffsetPointingTest.java b/src/test/java/org/orekit/attitudes/LofOffsetPointingTest.java index a6ddcecff8..95841a1a33 100644 --- a/src/test/java/org/orekit/attitudes/LofOffsetPointingTest.java +++ b/src/test/java/org/orekit/attitudes/LofOffsetPointingTest.java @@ -227,7 +227,7 @@ private > void checkField(final Field field final TimeStampedFieldPVCoordinates pvF = provider.getTargetPV(orbitF, dateF, frame); Assertions.assertEquals(0.0, Vector3D.distance(pvD.getPosition(), pvF.getPosition().toVector3D()), 6.0e-9); - Assertions.assertEquals(0.0, Vector3D.distance(pvD.getVelocity(), pvF.getVelocity().toVector3D()), 5.0e-13); + Assertions.assertEquals(0.0, Vector3D.distance(pvD.getVelocity(), pvF.getVelocity().toVector3D()), 3.0e-8); Assertions.assertEquals(0.0, Vector3D.distance(pvD.getAcceleration(), pvF.getAcceleration().toVector3D()), 2.0e-6); } diff --git a/src/test/java/org/orekit/attitudes/TargetProviderTest.java b/src/test/java/org/orekit/attitudes/TargetProviderTest.java new file mode 100644 index 0000000000..ad2f819e5f --- /dev/null +++ b/src/test/java/org/orekit/attitudes/TargetProviderTest.java @@ -0,0 +1,62 @@ +package org.orekit.attitudes; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2; +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.ExtendedPositionProvider; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.TimeStampedFieldPVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; + +class TargetProviderTest { + + @Test + void testGetTargetDirection() { + // GIVEN + final TestTargetProvider testTargetProvider = new TestTargetProvider(); + final TimeStampedPVCoordinates pvCoordinates = new TimeStampedPVCoordinates(AbsoluteDate.ARBITRARY_EPOCH, + new PVCoordinates()); + // WHEN + final Vector3D targetVector = testTargetProvider.getTargetDirection(null, null, pvCoordinates, null); + // THEN + final Vector3D expectedVector = testTargetProvider.getDerivative2TargetDirection(null, null, + pvCoordinates, null).toVector3D(); + Assertions.assertEquals(expectedVector, targetVector); + } + + @Test + void testFieldGetTargetDirection() { + // GIVEN + final TestTargetProvider testTargetProvider = new TestTargetProvider(); + final TimeStampedPVCoordinates pvCoordinates = new TimeStampedPVCoordinates(AbsoluteDate.ARBITRARY_EPOCH, + new PVCoordinates()); + final TimeStampedFieldPVCoordinates fieldPVCoordinates = new TimeStampedFieldPVCoordinates(ComplexField.getInstance(), + pvCoordinates); + // WHEN + final FieldVector3D targetVector = testTargetProvider.getTargetDirection(null, null, fieldPVCoordinates, null); + // THEN + final FieldVector3D> ud2Vector = testTargetProvider.getDerivative2TargetDirection(null, null, + fieldPVCoordinates, null); + final FieldVector3D expectedVector = new FieldVector3D<>(ud2Vector.getX().getValue(), ud2Vector.getY().getValue(), + ud2Vector.getZ().getValue()); + Assertions.assertEquals(expectedVector, targetVector); + } + + private static class TestTargetProvider implements TargetProvider { + + @Override + public > FieldVector3D getTargetDirection(ExtendedPositionProvider sun, OneAxisEllipsoid earth, + TimeStampedFieldPVCoordinates pv, Frame frame) { + return new FieldVector3D<>(pv.getDate().getField(), pv.getPosition().toVector3D()); + } + } + +} diff --git a/src/test/java/org/orekit/bodies/AnalyticalSolarPositionProviderTest.java b/src/test/java/org/orekit/bodies/AnalyticalSolarPositionProviderTest.java new file mode 100644 index 0000000000..2b524c61cc --- /dev/null +++ b/src/test/java/org/orekit/bodies/AnalyticalSolarPositionProviderTest.java @@ -0,0 +1,166 @@ +/* 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.analysis.differentiation.UnivariateDerivative1; +import org.hipparchus.analysis.differentiation.UnivariateDerivative1Field; +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaIntegrator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.orekit.Utils; +import org.orekit.data.DataContext; +import org.orekit.forces.gravity.ThirdBodyAttraction; +import org.orekit.frames.Frame; +import org.orekit.frames.Frames; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.EquinoctialOrbit; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.numerical.NumericalPropagator; +import org.orekit.time.*; +import org.orekit.utils.Constants; +import org.orekit.utils.ExtendedPositionProvider; +import org.orekit.utils.TimeStampedFieldPVCoordinates; + +class AnalyticalSolarPositionProviderTest { + + @BeforeEach + void setUp() { + Utils.setDataRoot("regular-data"); + } + + @ParameterizedTest + @ValueSource(ints = {1, 2, 3, 4, 5}) + void testGetPosition(final int month) { + // GIVEN + final AbsoluteDate date = new AbsoluteDate(new DateTimeComponents(2000, month, 1, 0, 0, 0), + TimeScalesFactory.getUTC()); + final AnalyticalSolarPositionProvider solarPositionProvider = new AnalyticalSolarPositionProvider(); + final Frame frame = FramesFactory.getGCRF(); + // WHEN + final Vector3D actualPosition = solarPositionProvider.getPosition(date, frame); + // THEN + final CelestialBody celestialBody = CelestialBodyFactory.getSun(); + final Vector3D expectedPosition = celestialBody.getPosition(date, frame); + Assertions.assertEquals(0., Vector3D.angle(expectedPosition, actualPosition), 5e-5); + Assertions.assertEquals(0., expectedPosition.subtract(actualPosition).getNorm(), 1e7); + } + + @Test + void testGetPositionFieldVersusNonField() { + // GIVEN + final AbsoluteDate date = new AbsoluteDate(new DateTimeComponents(2025, 1, 1, 0, 0, 0), + TimeScalesFactory.getUTC()); + final DataContext dataContext = DataContext.getDefault(); + final Frames frames = dataContext.getFrames(); + final Frame frame = frames.getEME2000(); + final AnalyticalSolarPositionProvider solarPositionProvider = new AnalyticalSolarPositionProvider(dataContext); + final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(ComplexField.getInstance(), date); + // WHEN + final FieldVector3D fieldPosition = solarPositionProvider.getPosition(fieldDate, frame); + // THEN + final Vector3D expectedPosition = solarPositionProvider.getPosition(date, frame); + Assertions.assertEquals(expectedPosition, fieldPosition.toVector3D()); + } + + @Test + void testGetPositionFieldDate() { + // GIVEN + final AbsoluteDate date = new AbsoluteDate(new DateTimeComponents(2025, 1, 1, 0, 0, 0), + TimeScalesFactory.getUTC()); + final DataContext dataContext = DataContext.getDefault(); + final Frames frames = dataContext.getFrames(); + final Frame frame = frames.getGCRF(); + final AnalyticalSolarPositionProvider solarPositionProvider = new AnalyticalSolarPositionProvider(dataContext); + final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(UnivariateDerivative1Field.getInstance(), + date).shiftedBy(new UnivariateDerivative1(0., 1.)); + // WHEN + final FieldVector3D fieldPosition = solarPositionProvider.getPosition(fieldDate, frame); + // THEN + Assertions.assertNotEquals(0., fieldPosition.getX().getFirstDerivative()); + } + + @Test + void testPropagation() { + // GIVEN + final CelestialBody sun = CelestialBodyFactory.getSun(); + final ExtendedPositionProvider positionProvider = new AnalyticalSolarPositionProvider(); + final CelestialBody body = createCelestialBody(sun.getGM(), positionProvider); + // WHEN + final NumericalPropagator propagator = createPropagator(body); + final AbsoluteDate terminalDate = propagator.getInitialState().getDate().shiftedBy(1e6); + final SpacecraftState actualState = propagator.propagate(terminalDate); + // THEN + final NumericalPropagator propagator2 = createPropagator(sun); + final SpacecraftState expectedState = propagator2.propagate(terminalDate); + Assertions.assertEquals(0., expectedState.getPosition().subtract(actualState.getPosition()).getNorm(), 1e-1); + } + + private static CelestialBody createCelestialBody(final double mu, final ExtendedPositionProvider positionProvider) { + return new CelestialBody() { + @Override + public Frame getInertiallyOrientedFrame() { + return null; + } + + @Override + public Frame getBodyOrientedFrame() { + return null; + } + + @Override + public String getName() { + return ""; + } + + @Override + public double getGM() { + return mu; + } + + @Override + public Vector3D getPosition(AbsoluteDate date, Frame frame) { + return positionProvider.getPosition(date, frame); + } + + @Override + public > TimeStampedFieldPVCoordinates getPVCoordinates(FieldAbsoluteDate date, Frame frame) { + return null; + } + }; + } + + private static NumericalPropagator createPropagator(final CelestialBody celestialBody) { + final NumericalPropagator propagator = new NumericalPropagator(new ClassicalRungeKuttaIntegrator(100)); + final AbsoluteDate date = new AbsoluteDate(new DateTimeComponents(2000, 1, 1, 0, 0, 0), + TimeScalesFactory.getUTC()); + final EquinoctialOrbit orbit = new EquinoctialOrbit(Constants.EGM96_EARTH_EQUATORIAL_RADIUS + 1000.e3, + 0.1, 0.2, 0.3, 0.4, 5., PositionAngleType.ECCENTRIC, FramesFactory.getGCRF(), date, Constants.EGM96_EARTH_MU); + propagator.setInitialState(new SpacecraftState(orbit)); + propagator.addForceModel(new ThirdBodyAttraction(celestialBody)); + return propagator; + } + } diff --git a/src/test/java/org/orekit/bodies/FieldGeodeticPointTest.java b/src/test/java/org/orekit/bodies/FieldGeodeticPointTest.java index dd8e8bb018..a3a5504d86 100644 --- a/src/test/java/org/orekit/bodies/FieldGeodeticPointTest.java +++ b/src/test/java/org/orekit/bodies/FieldGeodeticPointTest.java @@ -17,6 +17,9 @@ package org.orekit.bodies; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.Binary64; import org.hipparchus.util.Binary64Field; @@ -144,4 +147,18 @@ public void testToString() { Assertions.assertEquals("{lat: 30 deg, lon: 60 deg, alt: 90}", actual); } + @Test + void testToGeodeticPoint() { + // GIVEN + final GeodeticPoint geodeticPoint = new GeodeticPoint(1., 2., 3.); + final ComplexField field = ComplexField.getInstance(); + final FieldGeodeticPoint fieldGeodeticPoint = new FieldGeodeticPoint<>(field, geodeticPoint); + // WHEN + final GeodeticPoint actualPoint = fieldGeodeticPoint.toGeodeticPoint(); + // THEN + Assertions.assertEquals(geodeticPoint.getAltitude(), actualPoint.getAltitude()); + Assertions.assertEquals(geodeticPoint.getLatitude(), actualPoint.getLatitude()); + Assertions.assertEquals(geodeticPoint.getLongitude(), actualPoint.getLongitude()); + } + } diff --git a/src/test/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointEquationTermTest.java b/src/test/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointEquationTermTest.java new file mode 100644 index 0000000000..71118ab762 --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointEquationTermTest.java @@ -0,0 +1,108 @@ +/* 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.complex.Complex; +import org.hipparchus.complex.ComplexField; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.MathArrays; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; + +class AbstractCartesianAdjointEquationTermTest { + + @Test + void testBuildGradientCartesianVector() { + // GIVEN + final double[] variables = {1, 2, 3, 4, 5, 6}; + // WHEN + final Gradient[] gradients = AbstractCartesianAdjointEquationTerm.buildGradientCartesianVector(variables); + // THEN + for (int i = 0; i < gradients.length; i++) { + Assertions.assertEquals(6, gradients[i].getFreeParameters()); + Assertions.assertEquals(gradients[i].getValue(), variables[i]); + Assertions.assertEquals(1, gradients[i].getGradient()[i]); + } + } + + @Test + void testBuildFieldGradientCartesianVector() { + // GIVEN + final Complex[] variables = MathArrays.buildArray(ComplexField.getInstance(), 6); + for (int i = 0; i < variables.length; i++) { + variables[i] = Complex.ONE.multiply(i); + } + // WHEN + final FieldGradient[] gradients = AbstractCartesianAdjointEquationTerm.buildFieldGradientCartesianVector(variables); + // THEN + for (int i = 0; i < gradients.length; i++) { + Assertions.assertEquals(6, gradients[i].getFreeParameters()); + Assertions.assertEquals(gradients[i].getValue(), variables[i]); + Assertions.assertEquals(Complex.ONE, gradients[i].getGradient()[i]); + } + } + + @Test + void testGetFieldRatesContribution() { + // GIVEN + final Complex[] fieldState = MathArrays.buildArray(ComplexField.getInstance(), 6); + final Complex[] fieldAdjoint = MathArrays.buildArray(ComplexField.getInstance(), 6); + for (int i = 0; i < fieldAdjoint.length; i++) { + fieldState[i] = Complex.MINUS_ONE; + fieldAdjoint[i] = Complex.ONE.multiply(i); + } + final TestAdjointTerm adjointTerm = new TestAdjointTerm(); + final FieldAbsoluteDate fieldDate = FieldAbsoluteDate.getArbitraryEpoch(ComplexField.getInstance()); + final Frame frame = Mockito.mock(Frame.class); + // WHEN + final Complex[] fieldRatesContribution = adjointTerm.getFieldRatesContribution(fieldDate, fieldState, fieldAdjoint, frame); + // THEN + final double[] states = new double[fieldAdjoint.length]; + final double[] adjoint = new double[fieldAdjoint.length]; + for (int i = 0; i < adjoint.length; i++) { + states[i] = fieldState[i].getReal(); + adjoint[i] = fieldAdjoint[i].getReal(); + } + final double[] ratesContribution = adjointTerm.getRatesContribution(fieldDate.toAbsoluteDate(), states, adjoint, frame); + for (int i = 0; i < fieldRatesContribution.length; i++) { + Assertions.assertEquals(fieldRatesContribution[i].getReal(), ratesContribution[i]); + } + } + + private static class TestAdjointTerm extends AbstractCartesianAdjointEquationTerm { + public TestAdjointTerm() {} + + @Override + protected Vector3D getAcceleration(AbsoluteDate date, double[] stateVariables, Frame frame) { + return new Vector3D(stateVariables[0], stateVariables[1], stateVariables[5]); + } + + @Override + protected > FieldVector3D getFieldAcceleration(FieldAbsoluteDate date, T[] stateVariables, Frame frame) { + return new FieldVector3D<>(date.getField(), + new Vector3D(stateVariables[0].getReal(), stateVariables[1].getReal(), stateVariables[5].getReal())); + } + } +} diff --git a/src/test/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointNewtonianTermTest.java b/src/test/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointNewtonianTermTest.java new file mode 100644 index 0000000000..bc8bfb3691 --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointNewtonianTermTest.java @@ -0,0 +1,58 @@ +/* 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.util.Binary64; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.MathArrays; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class AbstractCartesianAdjointNewtonianTermTest { + + @Test + void testGetVelocityAdjointContributionField() { + // GIVEN + final AbstractCartesianAdjointNewtonianTerm adjointNewtonianTerm = Mockito.mock(AbstractCartesianAdjointNewtonianTerm.class); + final Binary64Field field = Binary64Field.getInstance(); + final Binary64[] fieldAdjoint = MathArrays.buildArray(field, 6); + final Binary64[] fieldState = MathArrays.buildArray(field, 6); + for (int i = 0; i < fieldAdjoint.length; i++) { + fieldState[i] = field.getZero().newInstance(-i+1); + fieldAdjoint[i] = field.getZero().newInstance(i); + } + final double mu = 2.; + Mockito.when(adjointNewtonianTerm.getFieldNewtonianVelocityAdjointContribution(fieldState, fieldAdjoint)).thenCallRealMethod(); + // WHEN + final Binary64[] fieldContribution = adjointNewtonianTerm.getFieldNewtonianVelocityAdjointContribution(fieldState, fieldAdjoint); + // THEN + final double[] state = new double[fieldState.length]; + for (int i = 0; i < fieldState.length; i++) { + state[i] = fieldState[i].getReal(); + } + final double[] adjoint = new double[fieldAdjoint.length]; + for (int i = 0; i < fieldAdjoint.length; i++) { + adjoint[i] = fieldAdjoint[i].getReal(); + } + Mockito.when(adjointNewtonianTerm.getNewtonianVelocityAdjointContribution(state, adjoint)).thenCallRealMethod(); + final double[] contribution = adjointNewtonianTerm.getNewtonianVelocityAdjointContribution(state, adjoint); + for (int i = 0; i < contribution.length; i++) { + Assertions.assertEquals(fieldContribution[i].getReal(), contribution[i]); + } + } +} diff --git a/src/test/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointNonCentralBodyTermTest.java b/src/test/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointNonCentralBodyTermTest.java new file mode 100644 index 0000000000..65d0e08e17 --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/adjoint/AbstractCartesianAdjointNonCentralBodyTermTest.java @@ -0,0 +1,118 @@ +/* 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.Binary64; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.MathArrays; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.ExtendedPositionProvider; + +class AbstractCartesianAdjointNonCentralBodyTermTest { + + @Test + void testGetPositionAdjointFieldContribution() { + // GIVEN + final double mu = Constants.JPL_SSD_SUN_GM; + final Frame frame = FramesFactory.getGCRF(); + final Binary64Field field = Binary64Field.getInstance(); + final FieldAbsoluteDate fieldDate = FieldAbsoluteDate.getArbitraryEpoch(field); + final AbsoluteDate date = fieldDate.toAbsoluteDate(); + final ExtendedPositionProvider mockedPositionProvider = Mockito.mock(ExtendedPositionProvider.class); + final Vector3D position = new Vector3D(1e3, 2e4, -3e5); + Mockito.when(mockedPositionProvider.getPosition(date, frame)).thenReturn(position); + Mockito.when(mockedPositionProvider.getPosition(fieldDate, frame)).thenReturn(new FieldVector3D<>(field, position)); + final TestNonCentralBodyTerm centralBodyTerm = new TestNonCentralBodyTerm(mu, + mockedPositionProvider); + final Binary64[] fieldAdjoint = MathArrays.buildArray(field, 6); + final Binary64[] fieldState = MathArrays.buildArray(field, 6); + for (int i = 0; i < fieldAdjoint.length; i++) { + fieldState[i] = field.getZero().newInstance(100 * i); + fieldAdjoint[i] = field.getZero().newInstance(-i+3); + } + // WHEN + final Binary64[] fieldContribution = centralBodyTerm.getPositionAdjointFieldContribution(fieldDate, + fieldState, fieldAdjoint, frame); + // THEN + final double[] state = new double[fieldState.length]; + final double[] adjoint = new double[fieldAdjoint.length]; + for (int i = 0; i < fieldAdjoint.length; i++) { + state[i] = fieldState[i].getReal(); + adjoint[i] = fieldAdjoint[i].getReal(); + } + final double[] contribution = centralBodyTerm.getPositionAdjointContribution(date, state, adjoint, frame); + for (int i = 0; i < contribution.length; i++) { + Assertions.assertEquals(fieldContribution[i].getReal(), contribution[i]); + } + } + + @Test + void testGetPositionAdjointFieldContributionAgainstKeplerian() { + // GIVEN + final double mu = Constants.JPL_SSD_SUN_GM; + final Frame frame = FramesFactory.getGCRF(); + final Binary64Field field = Binary64Field.getInstance(); + final FieldAbsoluteDate fieldDate = FieldAbsoluteDate.getArbitraryEpoch(field); + final ExtendedPositionProvider mockedPositionProvider = Mockito.mock(ExtendedPositionProvider.class); + Mockito.when(mockedPositionProvider.getPosition(fieldDate, frame)).thenReturn(FieldVector3D.getZero(field)); + final TestNonCentralBodyTerm centralBodyTerm = new TestNonCentralBodyTerm(mu, + mockedPositionProvider); + final Binary64[] fieldAdjoint = MathArrays.buildArray(field, 6); + final Binary64[] fieldState = MathArrays.buildArray(field, 6); + for (int i = 0; i < fieldAdjoint.length; i++) { + fieldState[i] = field.getZero().newInstance(-i+1); + fieldAdjoint[i] = field.getZero().newInstance(i); + } + // WHEN + final Binary64[] fieldContribution = centralBodyTerm.getPositionAdjointFieldContribution(fieldDate, + fieldState, fieldAdjoint, frame); + // THEN + final CartesianAdjointKeplerianTerm keplerianTerm = new CartesianAdjointKeplerianTerm(mu); + final Binary64[] contribution = keplerianTerm.getPositionAdjointFieldContribution(fieldDate, fieldState, + fieldAdjoint, frame); + for (int i = 0; i < contribution.length; i++) { + Assertions.assertEquals(fieldContribution[i], contribution[i]); + } + } + + private static class TestNonCentralBodyTerm extends AbstractCartesianAdjointNonCentralBodyTerm { + + protected TestNonCentralBodyTerm(double mu, ExtendedPositionProvider bodyPositionProvider) { + super(mu, bodyPositionProvider); + } + + @Override + protected Vector3D getAcceleration(AbsoluteDate date, double[] stateVariables, Frame frame) { + return null; + } + + @Override + protected > FieldVector3D getFieldAcceleration(FieldAbsoluteDate date, T[] stateVariables, Frame frame) { + return null; + } + } +} diff --git a/src/test/java/org/orekit/control/indirect/adjoint/CartesianAdjointDerivativesProviderTest.java b/src/test/java/org/orekit/control/indirect/adjoint/CartesianAdjointDerivativesProviderTest.java new file mode 100644 index 0000000000..9b3c45386f --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/adjoint/CartesianAdjointDerivativesProviderTest.java @@ -0,0 +1,195 @@ +/* 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.Vector3D; +import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaIntegrator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.Mockito; +import org.orekit.control.indirect.adjoint.cost.CartesianCost; +import org.orekit.control.indirect.adjoint.cost.TestCost; +import org.orekit.control.indirect.adjoint.cost.UnboundedCartesianEnergyNeglectingMass; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.integration.CombinedDerivatives; +import org.orekit.propagation.numerical.NumericalPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.PVCoordinates; + +class CartesianAdjointDerivativesProviderTest { + + @Test + void testInitException() { + // GIVEN + final String name = "name"; + final double mu = Constants.EGM96_EARTH_MU; + final CartesianAdjointDerivativesProvider derivativesProvider = new CartesianAdjointDerivativesProvider( + new UnboundedCartesianEnergyNeglectingMass(name), new CartesianAdjointKeplerianTerm(mu)); + final SpacecraftState mockedState = Mockito.mock(SpacecraftState.class); + Mockito.when(mockedState.isOrbitDefined()).thenReturn(true); + final Orbit mockedOrbit = Mockito.mock(Orbit.class); + Mockito.when(mockedOrbit.getType()).thenReturn(OrbitType.EQUINOCTIAL); + Mockito.when(mockedState.getOrbit()).thenReturn(mockedOrbit); + // WHEN + final Exception exception = Assertions.assertThrows(OrekitException.class, + () -> derivativesProvider.init(mockedState, null)); + Assertions.assertEquals(OrekitMessages.WRONG_COORDINATES_FOR_ADJOINT_EQUATION.getSourceString(), + exception.getMessage()); + } + + @Test + void testIntegration() { + // GIVEN + final String name = "name"; + final double mu = Constants.EGM96_EARTH_MU; + final CartesianAdjointDerivativesProvider derivativesProvider = new CartesianAdjointDerivativesProvider( + new UnboundedCartesianEnergyNeglectingMass(name), new CartesianAdjointKeplerianTerm(mu)); + final NumericalPropagator propagator = new NumericalPropagator(new ClassicalRungeKuttaIntegrator(100.)); + final Orbit orbit = new CartesianOrbit(new PVCoordinates(new Vector3D(7e6, 1e3, 0), new Vector3D(10., 7e3, -200)), + FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, mu); + propagator.setOrbitType(OrbitType.CARTESIAN); + propagator.setInitialState(new SpacecraftState(orbit).addAdditionalState(name, new double[6])); + propagator.addAdditionalDerivativesProvider(derivativesProvider); + // WHEN + final SpacecraftState state = propagator.propagate(orbit.getDate().shiftedBy(1000.)); + // THEN + Assertions.assertTrue(propagator.isAdditionalStateManaged(name)); + final double[] finalAdjoint = state.getAdditionalState(name); + Assertions.assertEquals(0, finalAdjoint[0]); + Assertions.assertEquals(0, finalAdjoint[1]); + Assertions.assertEquals(0, finalAdjoint[2]); + Assertions.assertEquals(0, finalAdjoint[3]); + Assertions.assertEquals(0, finalAdjoint[4]); + Assertions.assertEquals(0, finalAdjoint[5]); + } + + @Test + void testGetCost() { + // GIVEN + final CartesianCost expectedCost = Mockito.mock(CartesianCost.class); + final CartesianAdjointDerivativesProvider derivativesProvider = new CartesianAdjointDerivativesProvider(expectedCost); + // WHEN + final CartesianCost actualCost = derivativesProvider.getCost(); + // THEN + Assertions.assertEquals(expectedCost, actualCost); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testEvaluateHamiltonian(final boolean withMassAdjoint) { + // GIVEN + final CartesianCost cost = new TestCost(); + final SpacecraftState state = getState(cost.getAdjointName(), withMassAdjoint); + final CartesianAdjointEquationTerm mockedTerm = Mockito.mock(CartesianAdjointEquationTerm.class); + final double[] cartesian = new double[6]; + OrbitType.CARTESIAN.mapOrbitToArray(state.getOrbit(), null, cartesian, null); + Mockito.when(mockedTerm.getHamiltonianContribution(state.getDate(), cartesian, + state.getAdditionalState(cost.getAdjointName()), state.getFrame())).thenReturn(0.); + final CartesianAdjointDerivativesProvider derivativesProvider = new CartesianAdjointDerivativesProvider(cost, + mockedTerm); + // WHEN + final double hamiltonian = derivativesProvider.evaluateHamiltonian(state); + // THEN + final Vector3D velocity = state.getPVCoordinates().getVelocity(); + Assertions.assertEquals(velocity.dotProduct(new Vector3D(1, 1, 1)), hamiltonian); + } + + @Test + void testCombinedDerivatives() { + // GIVEN + final CartesianCost cost = new TestCost(); + final CartesianAdjointDerivativesProvider derivativesProvider = new CartesianAdjointDerivativesProvider(cost); + final SpacecraftState state = getState(derivativesProvider.getName(), false); + // WHEN + final CombinedDerivatives combinedDerivatives = derivativesProvider.combinedDerivatives(state); + // THEN + final double[] increment = combinedDerivatives.getMainStateDerivativesIncrements(); + for (int i = 0; i < 3; i++) { + Assertions.assertEquals(0., increment[i]); + } + Assertions.assertEquals(1., increment[3]); + Assertions.assertEquals(2., increment[4]); + Assertions.assertEquals(3., increment[5]); + Assertions.assertEquals(-10. * state.getMass() * new Vector3D(1., 2., 3).getNorm(), increment[6], 1e-10); + } + + @Test + void testCombinedDerivativesWithEquationTerm() { + // GIVEN + final CartesianCost cost = new TestCost(); + final CartesianAdjointEquationTerm equationTerm = new TestAdjointTerm(); + final CartesianAdjointDerivativesProvider derivativesProvider = new CartesianAdjointDerivativesProvider(cost, equationTerm); + final SpacecraftState state = getState(derivativesProvider.getName(), false); + // WHEN + final CombinedDerivatives combinedDerivatives = derivativesProvider.combinedDerivatives(state); + // THEN + final double[] adjointDerivatives = combinedDerivatives.getAdditionalDerivatives(); + Assertions.assertEquals(1., adjointDerivatives[0]); + Assertions.assertEquals(10., adjointDerivatives[1]); + Assertions.assertEquals(100., adjointDerivatives[2]); + Assertions.assertEquals(-1, adjointDerivatives[3]); + Assertions.assertEquals(-1, adjointDerivatives[4]); + Assertions.assertEquals(-1, adjointDerivatives[5]); + } + + private static SpacecraftState getState(final String name, final boolean withMassAdjoint) { + final Orbit orbit = new CartesianOrbit(new PVCoordinates(Vector3D.MINUS_I, Vector3D.PLUS_K), + FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, 1.); + final SpacecraftState stateWithoutAdditional = new SpacecraftState(orbit); + final double[] adjoint = withMassAdjoint ? new double[7] : new double[6]; + for (int i = 0; i < 6; i++) { + adjoint[i] = 1; + } + return stateWithoutAdditional.addAdditionalState(name, adjoint); + } + + private static class TestAdjointTerm implements CartesianAdjointEquationTerm { + + @Override + public double[] getRatesContribution(AbsoluteDate date, double[] stateVariables, double[] adjointVariables, Frame frame) { + return new double[] { 1., 10., 100., 0., 0., 0. }; + } + + @Override + public > T[] getFieldRatesContribution(FieldAbsoluteDate date, T[] stateVariables, T[] adjointVariables, Frame frame) { + return null; + } + + @Override + public double getHamiltonianContribution(AbsoluteDate date, double[] stateVariables, double[] adjointVariables, Frame frame) { + return 0; + } + + @Override + public > T getFieldHamiltonianContribution(FieldAbsoluteDate date, T[] stateVariables, T[] adjointVariables, Frame frame) { + return date.getField().getZero(); + } + } + +} diff --git a/src/test/java/org/orekit/control/indirect/adjoint/CartesianAdjointInertialTermTest.java b/src/test/java/org/orekit/control/indirect/adjoint/CartesianAdjointInertialTermTest.java new file mode 100644 index 0000000000..f354baab0b --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/adjoint/CartesianAdjointInertialTermTest.java @@ -0,0 +1,176 @@ +/* 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.analysis.differentiation.Gradient; +import org.hipparchus.analysis.differentiation.GradientField; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.util.Binary64; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.MathArrays; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.Utils; +import org.orekit.errors.OrekitIllegalArgumentException; +import org.orekit.forces.inertia.InertialForces; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldAbsolutePVCoordinates; + +class CartesianAdjointInertialTermTest { + + @BeforeEach + void setUp() { + Utils.setDataRoot("regular-data"); + } + + @Test + void testConstructorException() { + // GIVEN + final Frame referenceFrame = Mockito.mock(Frame.class); + Mockito.when(referenceFrame.isPseudoInertial()).thenReturn(false); + // WHEN & THEN + Assertions.assertThrows(OrekitIllegalArgumentException.class, () -> new CartesianAdjointInertialTerm(referenceFrame)); + } + + @Test + void testGetRatesContribution() { + // GIVEN + final Frame referenceFrame = FramesFactory.getGCRF(); + final Frame propagationFrame = FramesFactory.getGTOD(true); + final CartesianAdjointInertialTerm inertialTerm = new CartesianAdjointInertialTerm(referenceFrame); + final double[] adjoint = new double[6]; + final double[] state = new double[6]; + for (int i = 0; i < adjoint.length; i++) { + state[i] = -i+1; + adjoint[i] = i; + } + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + // WHEN + final double[] contribution = inertialTerm.getRatesContribution(date, state, adjoint, propagationFrame); + // THEN + final InertialForces inertialForces = new InertialForces(referenceFrame); + final int dimension = 6; + final GradientField field = GradientField.getField(dimension); + final FieldVector3D fieldPosition = new FieldVector3D<>( + Gradient.variable(dimension, 0, state[0]), + Gradient.variable(dimension, 1, state[1]), + Gradient.variable(dimension, 2, state[2])); + final FieldVector3D fieldVelocity = new FieldVector3D<>( + Gradient.variable(dimension, 3, state[3]), + Gradient.variable(dimension, 4, state[4]), + Gradient.variable(dimension, 5, state[5])); + final FieldAbsolutePVCoordinates absolutePVCoordinates = new FieldAbsolutePVCoordinates<>(propagationFrame, + new FieldAbsoluteDate<>(field, date), fieldPosition, fieldVelocity); + final FieldSpacecraftState fieldState = new FieldSpacecraftState<>(absolutePVCoordinates); + final FieldVector3D acceleration = inertialForces.acceleration(fieldState, null); + Assertions.assertEquals(-contribution[0], acceleration.getX().getGradient()[0] * adjoint[3] + + acceleration.getY().getGradient()[0] * adjoint[4] + acceleration.getZ().getGradient()[0] * adjoint[5]); + Assertions.assertEquals(-contribution[1], acceleration.getX().getGradient()[1] * adjoint[3] + + acceleration.getY().getGradient()[1] * adjoint[4] + acceleration.getZ().getGradient()[1] * adjoint[5]); + Assertions.assertEquals(-contribution[2], acceleration.getX().getGradient()[2] * adjoint[3] + + acceleration.getY().getGradient()[2] * adjoint[4] + acceleration.getZ().getGradient()[2] * adjoint[5], + 1e-20); + Assertions.assertEquals(-contribution[3], acceleration.getX().getGradient()[3] * adjoint[3] + + acceleration.getY().getGradient()[3] * adjoint[4] + acceleration.getZ().getGradient()[3] * adjoint[5]); + Assertions.assertEquals(-contribution[4], acceleration.getX().getGradient()[4] * adjoint[3] + + acceleration.getY().getGradient()[4] * adjoint[4] + acceleration.getZ().getGradient()[4] * adjoint[5]); + Assertions.assertEquals(-contribution[5], acceleration.getX().getGradient()[5] * adjoint[3] + + acceleration.getY().getGradient()[5] * adjoint[4] + acceleration.getZ().getGradient()[5] * adjoint[5], + 1e-20); + } + + @Test + void testGetFieldRatesContribution() { + // GIVEN + final Frame referenceFrame = FramesFactory.getGCRF(); + final Frame propagationFrame = FramesFactory.getGTOD(true); + final CartesianAdjointInertialTerm inertialTerm = new CartesianAdjointInertialTerm(referenceFrame); + final Binary64Field field = Binary64Field.getInstance(); + final Binary64[] fieldAdjoint = MathArrays.buildArray(field, 6); + final Binary64[] fieldState = MathArrays.buildArray(field, 6); + for (int i = 0; i < fieldAdjoint.length; i++) { + fieldState[i] = field.getZero().newInstance(-i+1); + fieldAdjoint[i] = field.getZero().newInstance(i); + } + final FieldAbsoluteDate fieldDate = FieldAbsoluteDate.getArbitraryEpoch(field); + // WHEN + final Binary64[] fieldContribution = inertialTerm.getFieldRatesContribution(fieldDate, fieldState, fieldAdjoint, + propagationFrame); + // THEN + final double[] state = new double[fieldState.length]; + for (int i = 0; i < fieldState.length; i++) { + state[i] = fieldState[i].getReal(); + } + final double[] adjoint = new double[fieldAdjoint.length]; + for (int i = 0; i < fieldAdjoint.length; i++) { + adjoint[i] = fieldAdjoint[i].getReal(); + } + final double[] contribution = inertialTerm.getRatesContribution(fieldDate.toAbsoluteDate(), state, adjoint, + propagationFrame); + for (int i = 0; i < contribution.length; i++) { + Assertions.assertEquals(fieldContribution[i].getReal(), contribution[i], 1e-22); + } + } + + @Test + void testGetHamiltonianContribution() { + // GIVEN + final Frame referenceFrame = FramesFactory.getGCRF(); + final CartesianAdjointInertialTerm cartesianAdjointInertialTerm = new CartesianAdjointInertialTerm(referenceFrame); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final double[] adjoint = { -1, 2, -3, 4, -5, 6 }; + final double[] state = {1, 3, 5, 7, 9, 11}; + // WHEN + final double contribution = cartesianAdjointInertialTerm.getHamiltonianContribution(date, state, adjoint, referenceFrame); + // THEN + Assertions.assertEquals(0., contribution); + } + + @Test + void testGetFieldHamiltonianContribution() { + // GIVEN + final CartesianAdjointInertialTerm cartesianAdjointInertialTerm = new CartesianAdjointInertialTerm(FramesFactory.getGCRF()); + final Binary64Field field = Binary64Field.getInstance(); + final Binary64[] fieldAdjoint = MathArrays.buildArray(field, 6); + final Binary64[] fieldState = MathArrays.buildArray(field, 6); + for (int i = 0; i < fieldAdjoint.length; i++) { + fieldState[i] = field.getZero().newInstance(-i + 1); + fieldAdjoint[i] = field.getZero().newInstance(i); + } + final FieldAbsoluteDate fieldDate = FieldAbsoluteDate.getArbitraryEpoch(field); + final Frame frame = FramesFactory.getGTOD(true); + // WHEN + final Binary64 fieldContribution = cartesianAdjointInertialTerm.getFieldHamiltonianContribution(fieldDate, + fieldState, fieldAdjoint, frame); + // THEN + final double[] adjoint = new double[fieldAdjoint.length]; + final double[] state = adjoint.clone(); + for (int i = 0; i < fieldAdjoint.length; i++) { + state[i] = fieldState[i].getReal(); + adjoint[i] = fieldAdjoint[i].getReal(); + } + final double contribution = cartesianAdjointInertialTerm.getHamiltonianContribution(fieldDate.toAbsoluteDate(), + state, adjoint, frame); + Assertions.assertEquals(contribution, fieldContribution.getReal(), 1e-15); + } +} diff --git a/src/test/java/org/orekit/control/indirect/adjoint/CartesianAdjointJ2TermTest.java b/src/test/java/org/orekit/control/indirect/adjoint/CartesianAdjointJ2TermTest.java new file mode 100644 index 0000000000..d03974ea22 --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/adjoint/CartesianAdjointJ2TermTest.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.util.Binary64; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.MathArrays; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.Constants; + +class CartesianAdjointJ2TermTest { + + @Test + void testGetters() { + // GIVEN + final double expectedMu = 1.; + final double expectedrEq = 2.; + final double expectedJ2 = 3.; + final Frame frame = Mockito.mock(Frame.class); + final CartesianAdjointJ2Term cartesianAdjointJ2Term = new CartesianAdjointJ2Term(expectedMu, expectedrEq, + expectedJ2, frame); + // WHEN + final double actualMu = cartesianAdjointJ2Term.getMu(); + final double actualrEq = cartesianAdjointJ2Term.getrEq(); + final double actualJ2 = cartesianAdjointJ2Term.getJ2(); + // THEN + Assertions.assertEquals(expectedJ2, actualJ2); + Assertions.assertEquals(expectedMu, actualMu); + Assertions.assertEquals(expectedrEq, actualrEq); + } + + @Test + void testGetPositionAdjointContributionLinearity() { + // GIVEN + final Frame frame = FramesFactory.getGCRF(); + final CartesianAdjointJ2Term j2Term = new CartesianAdjointJ2Term(Constants.EGM96_EARTH_MU, + Constants.EGM96_EARTH_EQUATORIAL_RADIUS, -Constants.EGM96_EARTH_C20, frame); + final double[] adjoint = new double[] {1, 2, 3, 4, 5, 6}; + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final double[] positionVelocity = new double[] {1, 1, 1, 1, 1, 1}; + // WHEN + final double[] contribution = j2Term.getPositionAdjointContribution(date, positionVelocity, adjoint, frame); + // THEN + final double[] doubleAdjoint = new double[6]; + for (int i = 0; i < 6; i++) { + doubleAdjoint[i] = adjoint[i] * 2; + } + final double[] contributionDouble = j2Term.getPositionAdjointContribution(date, positionVelocity, doubleAdjoint, + frame); + for (int i = 0; i < contribution.length; i++) { + Assertions.assertEquals(contribution[i] * 2, contributionDouble[i]); + } + } + + @Test + void testGetPositionAdjointFieldContribution() { + // GIVEN + final Frame frame = FramesFactory.getGCRF(); + final CartesianAdjointJ2Term j2Term = new CartesianAdjointJ2Term(Constants.EGM96_EARTH_MU, + Constants.EGM96_EARTH_EQUATORIAL_RADIUS, -Constants.EGM96_EARTH_C20, frame); + final Binary64Field field = Binary64Field.getInstance(); + final Binary64[] fieldAdjoint = MathArrays.buildArray(field, 6); + final Binary64[] fieldState = MathArrays.buildArray(field, 6); + for (int i = 0; i < fieldAdjoint.length; i++) { + fieldState[i] = field.getZero().newInstance(-i+1); + fieldAdjoint[i] = field.getZero().newInstance(i); + } + final FieldAbsoluteDate fieldDate = FieldAbsoluteDate.getArbitraryEpoch(field); + // WHEN + final Binary64[] fieldContribution = j2Term.getPositionAdjointFieldContribution(fieldDate, fieldState, + fieldAdjoint, frame); + // THEN + final double[] state = new double[fieldState.length]; + for (int i = 0; i < fieldState.length; i++) { + state[i] = fieldState[i].getReal(); + } + final double[] adjoint = new double[fieldAdjoint.length]; + for (int i = 0; i < fieldAdjoint.length; i++) { + adjoint[i] = fieldAdjoint[i].getReal(); + } + final double[] contribution = j2Term.getPositionAdjointContribution(fieldDate.toAbsoluteDate(), state, adjoint, + frame); + for (int i = 0; i < contribution.length; i++) { + Assertions.assertEquals(fieldContribution[i].getReal(), contribution[i]); + } + } + + @Test + void testGetFieldHamiltonianContribution() { + // GIVEN + final Frame frame = FramesFactory.getGCRF(); + final CartesianAdjointJ2Term cartesianAdjointJ2Term = new CartesianAdjointJ2Term(1., 1., 0.001, frame); + final Binary64Field field = Binary64Field.getInstance(); + final Binary64[] fieldAdjoint = MathArrays.buildArray(field, 6); + final Binary64[] fieldState = MathArrays.buildArray(field, 6); + for (int i = 0; i < fieldAdjoint.length; i++) { + fieldState[i] = field.getZero().newInstance(-i + 1); + fieldAdjoint[i] = field.getZero().newInstance(i); + } + final FieldAbsoluteDate fieldDate = FieldAbsoluteDate.getArbitraryEpoch(field); + // WHEN + final Binary64 fieldContribution = cartesianAdjointJ2Term.getFieldHamiltonianContribution(fieldDate, + fieldState, fieldAdjoint, frame); + // THEN + final double[] adjoint = new double[fieldAdjoint.length]; + final double[] state = adjoint.clone(); + for (int i = 0; i < fieldAdjoint.length; i++) { + state[i] = fieldState[i].getReal(); + adjoint[i] = fieldAdjoint[i].getReal(); + } + final double contribution = cartesianAdjointJ2Term.getHamiltonianContribution(fieldDate.toAbsoluteDate(), + state, adjoint, frame); + Assertions.assertEquals(contribution, fieldContribution.getReal()); + } + +} diff --git a/src/test/java/org/orekit/control/indirect/adjoint/CartesianAdjointKeplerianTermTest.java b/src/test/java/org/orekit/control/indirect/adjoint/CartesianAdjointKeplerianTermTest.java new file mode 100644 index 0000000000..ae81c93bb3 --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/adjoint/CartesianAdjointKeplerianTermTest.java @@ -0,0 +1,103 @@ +/* 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.analysis.differentiation.Gradient; +import org.hipparchus.analysis.differentiation.GradientField; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.util.Binary64; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.MathArrays; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.forces.gravity.NewtonianAttraction; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.Constants; + +class CartesianAdjointKeplerianTermTest { + + @Test + void testGetPositionAdjointContribution() { + // GIVEN + final CartesianAdjointKeplerianTerm keplerianTerm = new CartesianAdjointKeplerianTerm(Constants.EGM96_EARTH_MU); + final double[] adjoint = new double[6]; + final double[] state = new double[6]; + for (int i = 0; i < adjoint.length; i++) { + state[i] = -i+1; + adjoint[i] = i; + } + // WHEN + final double[] contribution = keplerianTerm.getPositionAdjointContribution(Mockito.mock(AbsoluteDate.class), + state, adjoint, FramesFactory.getGCRF()); + // THEN + final NewtonianAttraction newtonianAttraction = new NewtonianAttraction(keplerianTerm.getMu()); + final int dimension = 3; + final GradientField field = GradientField.getField(dimension); + final FieldSpacecraftState mockedState = Mockito.mock(FieldSpacecraftState.class); + final FieldVector3D fieldPosition = new FieldVector3D<>( + Gradient.variable(dimension, 0, state[0]), + Gradient.variable(dimension, 1, state[1]), + Gradient.variable(dimension, 2, state[2])); + Mockito.when(mockedState.getPosition()).thenReturn(fieldPosition); + final Gradient[] fieldMu = MathArrays.buildArray(field, 1); + fieldMu[0] = Gradient.constant(dimension, keplerianTerm.getMu()); + final FieldVector3D acceleration = newtonianAttraction.acceleration(mockedState, fieldMu); + Assertions.assertEquals(-contribution[0], acceleration.getX().getGradient()[0] * adjoint[3] + + acceleration.getY().getGradient()[0] * adjoint[4] + acceleration.getZ().getGradient()[0] * adjoint[5]); + Assertions.assertEquals(-contribution[1], acceleration.getX().getGradient()[1] * adjoint[3] + + acceleration.getY().getGradient()[1] * adjoint[4] + acceleration.getZ().getGradient()[1] * adjoint[5]); + Assertions.assertEquals(-contribution[2], acceleration.getX().getGradient()[2] * adjoint[3] + + acceleration.getY().getGradient()[2] * adjoint[4] + acceleration.getZ().getGradient()[2] * adjoint[5]); + } + + @Test + void testGetPositionAdjointFieldContribution() { + // GIVEN + final CartesianAdjointKeplerianTerm keplerianTerm = new CartesianAdjointKeplerianTerm(Constants.EGM96_EARTH_MU); + final Binary64Field field = Binary64Field.getInstance(); + final Binary64[] fieldAdjoint = MathArrays.buildArray(field, 6); + final Binary64[] fieldState = MathArrays.buildArray(field, 6); + for (int i = 0; i < fieldAdjoint.length; i++) { + fieldState[i] = field.getZero().newInstance(-i+1); + fieldAdjoint[i] = field.getZero().newInstance(i); + } + final Frame frame = FramesFactory.getGCRF(); + final FieldAbsoluteDate fieldDate = FieldAbsoluteDate.getArbitraryEpoch(field); + // WHEN + final Binary64[] fieldContribution = keplerianTerm.getPositionAdjointFieldContribution(fieldDate, fieldState, + fieldAdjoint, frame); + // THEN + final double[] state = new double[fieldState.length]; + for (int i = 0; i < fieldState.length; i++) { + state[i] = fieldState[i].getReal(); + } + final double[] adjoint = new double[fieldAdjoint.length]; + for (int i = 0; i < fieldAdjoint.length; i++) { + adjoint[i] = fieldAdjoint[i].getReal(); + } + final double[] contribution = keplerianTerm.getPositionAdjointContribution(fieldDate.toAbsoluteDate(), state, + adjoint, frame); + for (int i = 0; i < contribution.length; i++) { + Assertions.assertEquals(fieldContribution[i].getReal(), contribution[i]); + } + } +} diff --git a/src/test/java/org/orekit/control/indirect/adjoint/CartesianAdjointSingleBodyTermTest.java b/src/test/java/org/orekit/control/indirect/adjoint/CartesianAdjointSingleBodyTermTest.java new file mode 100644 index 0000000000..389b499032 --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/adjoint/CartesianAdjointSingleBodyTermTest.java @@ -0,0 +1,214 @@ +/* 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.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.Binary64; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.MathArrays; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.Utils; +import org.orekit.bodies.CelestialBody; +import org.orekit.forces.gravity.SingleBodyAbsoluteAttraction; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.DateComponents; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.TimeStampedFieldPVCoordinates; + +class CartesianAdjointSingleBodyTermTest { + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data"); + } + + @Test + void testGetters() { + // GIVEN + final CelestialBody body = getCelestialBody(Vector3D.MINUS_I); + final Frame expectedFrame = FramesFactory.getGCRF(); + final CartesianAdjointSingleBodyTerm singleBodyTerm = new CartesianAdjointSingleBodyTerm(body.getGM(), body); + // WHEN + final double mu = singleBodyTerm.getMu(); + // THEN + Assertions.assertEquals(body.getGM(), mu); + } + + @Test + void testGetPositionAdjointContribution() { + // GIVEN + final CelestialBody body = getCelestialBody(Vector3D.MINUS_I); + final CartesianAdjointSingleBodyTerm singleBodyTerm = new CartesianAdjointSingleBodyTerm(body.getGM(), body); + final double[] adjoint = new double[6]; + final double[] state = new double[6]; + for (int i = 0; i < adjoint.length; i++) { + state[i] = -i+1; + adjoint[i] = i; + } + final AbsoluteDate date = new AbsoluteDate(new DateComponents(2015, 1, 1), TimeScalesFactory.getUTC()); + // WHEN + final double[] contribution = singleBodyTerm.getPositionAdjointContribution(date, state, adjoint, + FramesFactory.getGCRF()); + // THEN + final SingleBodyAbsoluteAttraction singleBodyAbsoluteAttraction = new SingleBodyAbsoluteAttraction(body); + final int dimension = 3; + final GradientField field = GradientField.getField(dimension); + final FieldSpacecraftState mockedState = Mockito.mock(FieldSpacecraftState.class); + final FieldVector3D fieldPosition = new FieldVector3D<>( + Gradient.variable(dimension, 0, state[0]), + Gradient.variable(dimension, 1, state[1]), + Gradient.variable(dimension, 2, state[2])); + Mockito.when(mockedState.getDate()).thenReturn(new FieldAbsoluteDate<>(field, date)); + Mockito.when(mockedState.getPosition()).thenReturn(fieldPosition); + final Gradient[] fieldMu = MathArrays.buildArray(field, 1); + fieldMu[0] = Gradient.constant(dimension, singleBodyTerm.getMu()); + final FieldVector3D acceleration = singleBodyAbsoluteAttraction.acceleration(mockedState, fieldMu); + final double tolerance = 1e-12; + Assertions.assertEquals(-contribution[0], acceleration.getX().getGradient()[0] * adjoint[3] + + acceleration.getX().getGradient()[1] * adjoint[4] + acceleration.getX().getGradient()[2] * adjoint[5], tolerance); + Assertions.assertEquals(-contribution[1], acceleration.getY().getGradient()[0] * adjoint[3] + + acceleration.getY().getGradient()[1] * adjoint[4] + acceleration.getY().getGradient()[2] * adjoint[5], tolerance); + Assertions.assertEquals(-contribution[2], acceleration.getZ().getGradient()[0] * adjoint[3] + + acceleration.getZ().getGradient()[1] * adjoint[4] + acceleration.getZ().getGradient()[2] * adjoint[5], tolerance); + } + + @Test + void testGetPositionAdjointFieldContribution() { + // GIVEN + final CelestialBody celestialBody = getCelestialBody(Vector3D.ZERO); + final CartesianAdjointSingleBodyTerm cartesianAdjointSingleBodyTerm = new CartesianAdjointSingleBodyTerm(celestialBody.getGM(), + celestialBody); + final Binary64Field field = Binary64Field.getInstance(); + final Binary64[] fieldAdjoint = MathArrays.buildArray(field, 6); + final Binary64[] fieldState = MathArrays.buildArray(field, 6); + for (int i = 0; i < fieldAdjoint.length; i++) { + fieldState[i] = field.getZero().newInstance(-i+1); + fieldAdjoint[i] = field.getZero().newInstance(i); + } + final Frame frame = celestialBody.getInertiallyOrientedFrame(); + final FieldAbsoluteDate fieldDate = FieldAbsoluteDate.getArbitraryEpoch(field); + // WHEN + final Binary64[] fieldContribution = cartesianAdjointSingleBodyTerm.getPositionAdjointFieldContribution(fieldDate, + fieldState, fieldAdjoint, frame); + // THEN + final CartesianAdjointKeplerianTerm keplerianTerm = new CartesianAdjointKeplerianTerm(celestialBody.getGM()); + final Binary64[] contribution = keplerianTerm.getPositionAdjointFieldContribution(fieldDate, fieldState, + fieldAdjoint, frame); + for (int i = 0; i < contribution.length; i++) { + Assertions.assertEquals(fieldContribution[i], contribution[i]); + } + } + + @Test + void testGetHamiltonianContribution() { + // GIVEN + final CelestialBody celestialBody = getCelestialBody(Vector3D.ZERO); + final CartesianAdjointSingleBodyTerm cartesianAdjointSingleBodyTerm = new CartesianAdjointSingleBodyTerm(celestialBody.getGM(), + celestialBody); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Frame frame = FramesFactory.getGCRF(); + final double[] adjoint = { -1, 2, -3, 4, -5, 6 }; + final double[] state = {1, 3, 5, 7, 9, 11}; + // WHEN + final double actualContribution = cartesianAdjointSingleBodyTerm.getHamiltonianContribution(date, state, adjoint, frame); + // THEN + final double expectedContribution = new CartesianAdjointKeplerianTerm(celestialBody.getGM()).getHamiltonianContribution(date, + state, adjoint, frame); + Assertions.assertEquals(expectedContribution, actualContribution); + } + + @Test + void testGetFieldHamiltonianContribution() { + // GIVEN + final CelestialBody celestialBody = getCelestialBody(Vector3D.ZERO); + final CartesianAdjointSingleBodyTerm cartesianAdjointSingleBodyTerm = new CartesianAdjointSingleBodyTerm(celestialBody.getGM(), + celestialBody); + final Binary64Field field = Binary64Field.getInstance(); + final Binary64[] fieldAdjoint = MathArrays.buildArray(field, 6); + final Binary64[] fieldState = MathArrays.buildArray(field, 6); + for (int i = 0; i < fieldAdjoint.length; i++) { + fieldState[i] = field.getZero().newInstance(-i + 1); + fieldAdjoint[i] = field.getZero().newInstance(i); + } + final Frame frame = celestialBody.getInertiallyOrientedFrame(); + final FieldAbsoluteDate fieldDate = FieldAbsoluteDate.getArbitraryEpoch(field); + // WHEN + final Binary64 fieldContribution = cartesianAdjointSingleBodyTerm.getFieldHamiltonianContribution(fieldDate, + fieldState, fieldAdjoint, frame); + // THEN + final double[] adjoint = new double[fieldAdjoint.length]; + final double[] state = adjoint.clone(); + for (int i = 0; i < fieldAdjoint.length; i++) { + state[i] = fieldState[i].getReal(); + adjoint[i] = fieldAdjoint[i].getReal(); + } + final double contribution = cartesianAdjointSingleBodyTerm.getHamiltonianContribution(fieldDate.toAbsoluteDate(), + state, adjoint, frame); + Assertions.assertEquals(contribution, fieldContribution.getReal()); + } + + private static CelestialBody getCelestialBody(final Vector3D position) { + return new CelestialBody() { + @Override + public Frame getInertiallyOrientedFrame() { + return FramesFactory.getGCRF(); + } + + @Override + public Frame getBodyOrientedFrame() { + return null; + } + + @Override + public String getName() { + return ""; + } + + @Override + public double getGM() { + return 1.; + } + + @Override + public Vector3D getPosition(AbsoluteDate date, Frame frame) { + return position; + } + + @Override + public > FieldVector3D getPosition(FieldAbsoluteDate date, Frame frame) { + return new FieldVector3D<>(date.getField(), getPosition(date.toAbsoluteDate(), frame)); + } + + @Override + public > TimeStampedFieldPVCoordinates getPVCoordinates(FieldAbsoluteDate date, Frame frame) { + return null; + } + }; + } + +} diff --git a/src/test/java/org/orekit/control/indirect/adjoint/CartesianAdjointThirdBodyTermTest.java b/src/test/java/org/orekit/control/indirect/adjoint/CartesianAdjointThirdBodyTermTest.java new file mode 100644 index 0000000000..fe5edba029 --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/adjoint/CartesianAdjointThirdBodyTermTest.java @@ -0,0 +1,130 @@ +/* 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.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.Binary64; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.MathArrays; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.bodies.CelestialBody; +import org.orekit.forces.gravity.ThirdBodyAttraction; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.ExtendedPositionProvider; +import org.orekit.utils.TimeStampedPVCoordinates; + +class CartesianAdjointThirdBodyTermTest { + + @Test + void testGetAcceleration() { + // GIVEN + final double mu = 2.; + final Frame frame = FramesFactory.getGCRF(); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final CelestialBody mockedPositionProvider = Mockito.mock(CelestialBody.class); + final Vector3D bodyPosition = new Vector3D(1, -2, 10); + Mockito.when(mockedPositionProvider.getPosition(date, frame)).thenReturn(bodyPosition); + final CartesianAdjointThirdBodyTerm thirdBodyTerm = new CartesianAdjointThirdBodyTerm(mu, + mockedPositionProvider); + final CartesianOrbit orbit = new CartesianOrbit(new TimeStampedPVCoordinates(date, Vector3D.MINUS_J, Vector3D.MINUS_K), + frame, mu); + final double[] state = new double[6]; + OrbitType.CARTESIAN.mapOrbitToArray(orbit, PositionAngleType.ECCENTRIC, state, null); + // WHEN + final Vector3D acceleration = thirdBodyTerm.getAcceleration(date, state, frame); + // THEN + final ThirdBodyAttraction thirdBodyAttraction = new ThirdBodyAttraction(mockedPositionProvider); + final Vector3D expectedAcceleration = thirdBodyAttraction.acceleration(new SpacecraftState(orbit), new double[] {mu}); + final double tolerance = 1e-15; + Assertions.assertEquals(expectedAcceleration.getX(), acceleration.getX(), tolerance); + Assertions.assertEquals(expectedAcceleration.getY(), acceleration.getY(), tolerance); + Assertions.assertEquals(expectedAcceleration.getZ(), acceleration.getZ(), tolerance); + } + + @Test + void testGetFieldAcceleration() { + // GIVEN + final double mu = 3e14; + final Frame frame = FramesFactory.getGCRF(); + final Binary64Field field = Binary64Field.getInstance(); + final FieldAbsoluteDate fieldDate = FieldAbsoluteDate.getArbitraryEpoch(field); + final ExtendedPositionProvider mockedPositionProvider = Mockito.mock(ExtendedPositionProvider.class); + final AbsoluteDate date = fieldDate.toAbsoluteDate(); + final Vector3D bodyPosition = new Vector3D(1e4, -2e5, 1e4); + Mockito.when(mockedPositionProvider.getPosition(date, frame)).thenReturn(bodyPosition); + Mockito.when(mockedPositionProvider.getPosition(fieldDate, frame)).thenReturn(new FieldVector3D<>(field, bodyPosition)); + final CartesianAdjointThirdBodyTerm thirdBodyTerm = new CartesianAdjointThirdBodyTerm(mu, + mockedPositionProvider); + final Binary64[] fieldAdjoint = MathArrays.buildArray(field, 6); + final Binary64[] fieldState = MathArrays.buildArray(field, 6); + for (int i = 0; i < fieldAdjoint.length; i++) { + fieldState[i] = field.getZero().newInstance(i*10); + fieldAdjoint[i] = field.getZero().newInstance(i*2+1); + } + // WHEN + final FieldVector3D fieldAcceleration = thirdBodyTerm.getFieldAcceleration(fieldDate, + fieldState, frame); + // THEN + final double[] state = new double[fieldState.length]; + for (int i = 0; i < fieldState.length; i++) { + state[i] = fieldState[i].getReal(); + } + final Vector3D acceleration = thirdBodyTerm.getAcceleration(date, state, frame); + Assertions.assertEquals(fieldAcceleration.getX().getReal(), acceleration.getX()); + Assertions.assertEquals(fieldAcceleration.getY().getReal(), acceleration.getY()); + Assertions.assertEquals(fieldAcceleration.getZ().getReal(), acceleration.getZ()); + } + + @Test + void testGetPositionAdjointFieldContributionAgainstKeplerian() { + // GIVEN + final double mu = 1.32712440017987E20; + final Frame frame = FramesFactory.getGCRF(); + final Binary64Field field = Binary64Field.getInstance(); + final FieldAbsoluteDate fieldDate = FieldAbsoluteDate.getArbitraryEpoch(field); + final ExtendedPositionProvider mockedPositionProvider = Mockito.mock(ExtendedPositionProvider.class); + Mockito.when(mockedPositionProvider.getPosition(fieldDate, frame)).thenReturn(FieldVector3D.getZero(field)); + final CartesianAdjointThirdBodyTerm thirdBodyTerm = new CartesianAdjointThirdBodyTerm(mu, + mockedPositionProvider); + final Binary64[] fieldAdjoint = MathArrays.buildArray(field, 6); + final Binary64[] fieldState = MathArrays.buildArray(field, 6); + for (int i = 0; i < fieldAdjoint.length; i++) { + fieldState[i] = field.getZero().newInstance(-i*20); + fieldAdjoint[i] = field.getZero().newInstance(i); + } + // WHEN + final Binary64[] fieldContribution = thirdBodyTerm.getPositionAdjointFieldContribution(fieldDate, + fieldState, fieldAdjoint, frame); + // THEN + final CartesianAdjointKeplerianTerm keplerianTerm = new CartesianAdjointKeplerianTerm(mu); + final Binary64[] contribution = keplerianTerm.getPositionAdjointFieldContribution(fieldDate, fieldState, + fieldAdjoint, frame); + for (int i = 0; i < contribution.length; i++) { + Assertions.assertEquals(fieldContribution[i], contribution[i]); + } + } +} diff --git a/src/test/java/org/orekit/control/indirect/adjoint/FieldCartesianAdjointDerivativesProviderTest.java b/src/test/java/org/orekit/control/indirect/adjoint/FieldCartesianAdjointDerivativesProviderTest.java new file mode 100644 index 0000000000..8e7f42e504 --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/adjoint/FieldCartesianAdjointDerivativesProviderTest.java @@ -0,0 +1,141 @@ +/* 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.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaFieldIntegrator; +import org.hipparchus.util.Binary64; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.MathArrays; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.Mockito; +import org.orekit.control.indirect.adjoint.cost.CartesianCost; +import org.orekit.control.indirect.adjoint.cost.TestCost; +import org.orekit.control.indirect.adjoint.cost.UnboundedCartesianEnergyNeglectingMass; +import org.orekit.errors.OrekitException; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.*; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.integration.FieldCombinedDerivatives; +import org.orekit.propagation.numerical.FieldNumericalPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.PVCoordinates; + +class FieldCartesianAdjointDerivativesProviderTest { + + @Test + void testInitException() { + // GIVEN + final String name = "name"; + final double mu = Constants.EGM96_EARTH_MU; + final FieldCartesianAdjointDerivativesProvider derivativesProvider = new FieldCartesianAdjointDerivativesProvider<>( + new UnboundedCartesianEnergyNeglectingMass(name), new CartesianAdjointKeplerianTerm(mu)); + final FieldSpacecraftState mockedState = Mockito.mock(FieldSpacecraftState.class); + Mockito.when(mockedState.isOrbitDefined()).thenReturn(true); + final FieldOrbit mockedOrbit = Mockito.mock(FieldOrbit.class); + Mockito.when(mockedOrbit.getType()).thenReturn(OrbitType.EQUINOCTIAL); + Mockito.when(mockedState.getOrbit()).thenReturn(mockedOrbit); + // WHEN + Assertions.assertThrows(OrekitException.class, () -> derivativesProvider.init(mockedState, null)); + } + + @Test + void testIntegration() { + // GIVEN + final String name = "name"; + final double mu = Constants.EGM96_EARTH_MU; + final Binary64Field field = Binary64Field.getInstance(); + final FieldCartesianAdjointDerivativesProvider derivativesProvider = new FieldCartesianAdjointDerivativesProvider<>( + new UnboundedCartesianEnergyNeglectingMass(name), new CartesianAdjointKeplerianTerm(mu)); + final ClassicalRungeKuttaFieldIntegrator integrator = new ClassicalRungeKuttaFieldIntegrator<>(field, + Binary64.ONE.multiply(100.)); + final FieldNumericalPropagator propagator = new FieldNumericalPropagator<>(field, integrator); + final Orbit orbit = new CartesianOrbit(new PVCoordinates(new Vector3D(7e6, 1e3, 0), new Vector3D(10., 7e3, -200)), + FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, mu); + final FieldSpacecraftState initialState = new FieldSpacecraftState<>(field, new SpacecraftState(orbit)); + propagator.setOrbitType(OrbitType.CARTESIAN); + propagator.setInitialState(initialState.addAdditionalState(name, MathArrays.buildArray(field, 6))); + propagator.addAdditionalDerivativesProvider(derivativesProvider); + // WHEN + final FieldSpacecraftState terminalState = propagator.propagate(initialState.getDate().shiftedBy(1000.)); + // THEN + Assertions.assertTrue(propagator.isAdditionalStateManaged(name)); + final Binary64[] adjoint = terminalState.getAdditionalState(name); + Assertions.assertEquals(0., adjoint[0].getReal()); + Assertions.assertEquals(0., adjoint[1].getReal()); + Assertions.assertEquals(0., adjoint[2].getReal()); + Assertions.assertEquals(0., adjoint[3].getReal()); + Assertions.assertEquals(0., adjoint[4].getReal()); + Assertions.assertEquals(0., adjoint[5].getReal()); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testEvaluateHamiltonian(final boolean withMassAdjoint) { + // GIVEN + final CartesianCost cost = new TestCost(); + final double mu = 1e-3; + final FieldCartesianAdjointDerivativesProvider derivativesProvider = new FieldCartesianAdjointDerivativesProvider<>(cost, + new CartesianAdjointKeplerianTerm(mu)); + final FieldSpacecraftState state = getState(derivativesProvider.getName(), withMassAdjoint); + // WHEN + final Binary64 hamiltonian = derivativesProvider.evaluateHamiltonian(state); + // THEN + final FieldVector3D velocity = state.getPVCoordinates().getVelocity(); + final FieldVector3D vector = new FieldVector3D<>(Binary64.ONE, Binary64.ONE, Binary64.ONE); + Assertions.assertEquals(velocity.dotProduct(vector).add(mu), hamiltonian); + } + + @Test + void testCombinedDerivatives() { + // GIVEN + final CartesianCost cost = new TestCost(); + final FieldCartesianAdjointDerivativesProvider derivativesProvider = new FieldCartesianAdjointDerivativesProvider<>( + cost); + final FieldSpacecraftState state = getState(derivativesProvider.getName(), false); + // WHEN + final FieldCombinedDerivatives combinedDerivatives = derivativesProvider.combinedDerivatives(state); + // THEN + final Binary64[] increment = combinedDerivatives.getMainStateDerivativesIncrements(); + for (int i = 0; i < 3; i++) { + Assertions.assertEquals(0., increment[i].getReal()); + } + Assertions.assertEquals(1., increment[3].getReal()); + Assertions.assertEquals(2., increment[4].getReal()); + Assertions.assertEquals(3., increment[5].getReal()); + Assertions.assertEquals(-10. * state.getMass().getReal() * new Vector3D(1., 2., 3).getNorm(), + increment[6].getReal(), 1e-10); + } + + private static FieldSpacecraftState getState(final String name, final boolean withMassAdjoint) { + final Orbit orbit = new CartesianOrbit(new PVCoordinates(Vector3D.MINUS_I, Vector3D.PLUS_K), + FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, 1.); + final FieldSpacecraftState stateWithoutAdditional = new FieldSpacecraftState<>(new FieldCartesianOrbit<>(Binary64Field.getInstance(), orbit)); + final Binary64[] adjoint = MathArrays.buildArray(stateWithoutAdditional.getDate().getField(), + withMassAdjoint ? 7 : 6); + for (int i = 0; i < 6; i++) { + adjoint[i] = Binary64.ONE; + } + return stateWithoutAdditional.addAdditionalState(name, adjoint); + } +} diff --git a/src/test/java/org/orekit/control/indirect/adjoint/cost/AbstractCartesianEnergyTest.java b/src/test/java/org/orekit/control/indirect/adjoint/cost/AbstractCartesianEnergyTest.java new file mode 100644 index 0000000000..a6d9034c05 --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/adjoint/cost/AbstractCartesianEnergyTest.java @@ -0,0 +1,47 @@ +/* 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.cost; + +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +import org.hipparchus.util.MathArrays; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class AbstractCartesianEnergyTest { + + @Test + void testGetFieldAdjointVelocityNorm() { + // GIVEN + final Complex[] fieldAdjoint = MathArrays.buildArray(ComplexField.getInstance(), 6); + for (int i = 0; i < fieldAdjoint.length; i++) { + fieldAdjoint[i] = Complex.ONE.multiply(i); + } + final double[] adjoint = new double[fieldAdjoint.length]; + for (int i = 0; i < fieldAdjoint.length; i++) { + adjoint[i] = fieldAdjoint[i].getReal(); + } + final AbstractCartesianEnergy cartesianEnergy = Mockito.mock(AbstractCartesianEnergy.class); + Mockito.when(cartesianEnergy.getFieldAdjointVelocityNorm(fieldAdjoint)).thenCallRealMethod(); + Mockito.when(cartesianEnergy.getAdjointVelocityNorm(adjoint)).thenCallRealMethod(); + // WHEN + final Complex fieldNorm = cartesianEnergy.getFieldAdjointVelocityNorm(fieldAdjoint); + // THEN + Assertions.assertEquals(cartesianEnergy.getAdjointVelocityNorm(adjoint), fieldNorm.getReal()); + } +} diff --git a/src/test/java/org/orekit/control/indirect/adjoint/cost/AbstractCartesianEnergyWithMassTest.java b/src/test/java/org/orekit/control/indirect/adjoint/cost/AbstractCartesianEnergyWithMassTest.java new file mode 100644 index 0000000000..3584d4698d --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/adjoint/cost/AbstractCartesianEnergyWithMassTest.java @@ -0,0 +1,21 @@ +/* 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.cost; + +class AbstractCartesianEnergyWithMassTest { + +} diff --git a/src/test/java/org/orekit/control/indirect/adjoint/cost/BoundedCartesianEnergyTest.java b/src/test/java/org/orekit/control/indirect/adjoint/cost/BoundedCartesianEnergyTest.java new file mode 100644 index 0000000000..eaa471c310 --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/adjoint/cost/BoundedCartesianEnergyTest.java @@ -0,0 +1,150 @@ +/* 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.cost; + +import org.hipparchus.Field; +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.ode.events.Action; +import org.hipparchus.util.MathArrays; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.orekit.propagation.events.EventDetector; +import org.orekit.propagation.events.FieldEventDetector; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +class BoundedCartesianEnergyTest { + + @Test + void getEventDetectorsSizeAndActionTest() { + // GIVEN + final double maximumThrustMagnitude = 1.; + final double massFlowRateFactor = 2.; + final BoundedCartesianEnergy boundedCartesianEnergy = new BoundedCartesianEnergy("", massFlowRateFactor, + maximumThrustMagnitude); + // WHEN + final Stream eventDetectorStream = boundedCartesianEnergy.getEventDetectors(); + // THEN + final List eventDetectors = eventDetectorStream.collect(Collectors.toList()); + Assertions.assertEquals(2, eventDetectors.size()); + for (final EventDetector eventDetector : eventDetectors) { + Assertions.assertInstanceOf(CartesianEnergyConsideringMass.SingularityDetector.class, eventDetector); + final CartesianEnergyConsideringMass.SingularityDetector singularityDetector = + (CartesianEnergyConsideringMass.SingularityDetector) eventDetector; + Assertions.assertEquals(Action.RESET_DERIVATIVES, singularityDetector.getHandler().eventOccurred(null, null, true)); + } + } + + @Test + void getFieldEventDetectorsSizeAndActionTest() { + // GIVEN + final double maximumThrustMagnitude = 1.; + final double massFlowRateFactor = 2.; + final BoundedCartesianEnergy boundedCartesianEnergy = new BoundedCartesianEnergy("", massFlowRateFactor, + maximumThrustMagnitude); + final Field field = ComplexField.getInstance(); + // WHEN + final Stream> eventDetectorStream = boundedCartesianEnergy.getFieldEventDetectors(field); + // THEN + final List> eventDetectors = eventDetectorStream.collect(Collectors.toList()); + Assertions.assertEquals(2, eventDetectors.size()); + for (final FieldEventDetector eventDetector : eventDetectors) { + Assertions.assertInstanceOf(CartesianEnergyConsideringMass.FieldSingularityDetector.class, eventDetector); + final CartesianEnergyConsideringMass.FieldSingularityDetector singularityDetector = + (CartesianEnergyConsideringMass.FieldSingularityDetector) eventDetector; + Assertions.assertEquals(Action.RESET_DERIVATIVES, singularityDetector.getHandler().eventOccurred(null, null, true)); + } + } + + @ParameterizedTest + @ValueSource(doubles = {1e-3, 1e-1, 1e2}) + void testGetFieldThrustAccelerationVectorFieldFactor(final double massReal) { + // GIVEN + final double massRateFactor = 1.; + final BoundedCartesianEnergy boundedCartesianEnergy = new BoundedCartesianEnergy("", massRateFactor, + 1.); + final ComplexField field = ComplexField.getInstance(); + final Complex[] fieldAdjoint = MathArrays.buildArray(field, 7); + fieldAdjoint[3] = new Complex(1.0, 0.0); + fieldAdjoint[4] = new Complex(2.0, 0.0); + fieldAdjoint[5] = new Complex(3.0, 0.0); + fieldAdjoint[6] = new Complex(4.0, 0.0); + final Complex mass = new Complex(massReal, 0.); + // WHEN + final FieldVector3D fieldThrustVector = boundedCartesianEnergy.getFieldThrustAccelerationVector(fieldAdjoint, mass); + // THEN + final double[] adjoint = new double[7]; + for (int i = 0; i < adjoint.length; i++) { + adjoint[i] = fieldAdjoint[i].getReal(); + } + final Vector3D thrustVector = boundedCartesianEnergy.getThrustAccelerationVector(adjoint, mass.getReal()); + Assertions.assertEquals(thrustVector, fieldThrustVector.toVector3D()); + } + + @ParameterizedTest + @ValueSource(doubles = {1e-4, 1e2}) + void testFieldUpdateDerivatives(final double mass) { + // GIVEN + final double massRateFactor = 1.; + final BoundedCartesianEnergy boundedCartesianEnergy = new BoundedCartesianEnergy("", massRateFactor, + 2.); + final ComplexField field = ComplexField.getInstance(); + final Complex[] fieldAdjoint = MathArrays.buildArray(field, 7); + fieldAdjoint[3] = new Complex(1.0e-3, 0.0); + fieldAdjoint[4] = new Complex(2.0e-3, 0.0); + fieldAdjoint[6] = Complex.ONE; + final Complex fieldMass = new Complex(mass, 0.); + final Complex[] fieldDerivatives = MathArrays.buildArray(field, 7); + // WHEN + boundedCartesianEnergy.updateFieldAdjointDerivatives(fieldAdjoint, fieldMass, fieldDerivatives); + // THEN + final double[] adjoint = new double[7]; + for (int i = 0; i < fieldAdjoint.length; i++) { + adjoint[i] = fieldAdjoint[i].getReal(); + } + final double[] derivatives = new double[7]; + boundedCartesianEnergy.updateAdjointDerivatives(adjoint, fieldMass.getReal(), derivatives); + Assertions.assertEquals(derivatives[6], fieldDerivatives[6].getReal()); + } + + @Test + void testGetFieldThrustAccelerationVectorFieldVersusUnbounded() { + // GIVEN + final double massRateFactor = 1.; + final double maximumThrustMagnitude = 1.; + final BoundedCartesianEnergy boundedCartesianEnergy = new BoundedCartesianEnergy("", massRateFactor, + maximumThrustMagnitude); + final ComplexField field = ComplexField.getInstance(); + final Complex[] fieldAdjoint = MathArrays.buildArray(field, 7); + fieldAdjoint[3] = new Complex(1.0e-3, 0.0); + fieldAdjoint[4] = new Complex(2.0e-3, 0.0); + final Complex mass = new Complex(3.e-3, 0.); + // WHEN + final FieldVector3D fieldThrustVector = boundedCartesianEnergy.getFieldThrustAccelerationVector(fieldAdjoint, mass); + // THEN + final FieldVector3D expectedThrustVector = new UnboundedCartesianEnergy("", massRateFactor) + .getFieldThrustAccelerationVector(fieldAdjoint, mass).scalarMultiply(maximumThrustMagnitude); + Assertions.assertEquals(expectedThrustVector, fieldThrustVector); + } +} diff --git a/src/test/java/org/orekit/control/indirect/adjoint/cost/TestCost.java b/src/test/java/org/orekit/control/indirect/adjoint/cost/TestCost.java new file mode 100644 index 0000000000..76b92aba1a --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/adjoint/cost/TestCost.java @@ -0,0 +1,80 @@ +/* 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.cost; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.propagation.events.EventDetector; +import org.orekit.propagation.events.FieldEventDetector; + +import java.util.stream.Stream; + +public class TestCost implements CartesianCost { + + @Override + public String getAdjointName() { + return ""; + } + + @Override + public double getMassFlowRateFactor() { + return 10.; + } + + @Override + public Vector3D getThrustAccelerationVector(double[] adjointVariables, double mass) { + return new Vector3D(1, 2, 3); + } + + @Override + public > FieldVector3D getFieldThrustAccelerationVector(T[] adjointVariables, T mass) { + return new FieldVector3D<>(mass.getField(), new Vector3D(1, 2, 3)); + } + + @Override + public void updateAdjointDerivatives(double[] adjointVariables, double mass, double[] adjointDerivatives) { + + } + + @Override + public > void updateFieldAdjointDerivatives(T[] adjointVariables, T mass, T[] adjointDerivatives) { + + } + + @Override + public double getHamiltonianContribution(double[] adjointVariables, double mass) { + return 0; + } + + @Override + public > T getFieldHamiltonianContribution(T[] adjointVariables, T mass) { + return mass.getField().getZero(); + } + + @Override + public Stream getEventDetectors() { + return null; + } + + @Override + public > Stream> getFieldEventDetectors(Field field) { + return null; + } + +} diff --git a/src/test/java/org/orekit/control/indirect/adjoint/cost/UnboundedCartesianEnergyNeglectingMassTest.java b/src/test/java/org/orekit/control/indirect/adjoint/cost/UnboundedCartesianEnergyNeglectingMassTest.java new file mode 100644 index 0000000000..d44c7f229b --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/adjoint/cost/UnboundedCartesianEnergyNeglectingMassTest.java @@ -0,0 +1,83 @@ +/* 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.cost; + +import org.hipparchus.Field; +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.Binary64; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.MathArrays; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class UnboundedCartesianEnergyNeglectingMassTest { + + @Test + void testGetHamiltonianContribution() { + // GIVEN + final UnboundedCartesianEnergy mockedEnergy = Mockito.mock(UnboundedCartesianEnergy.class); + final Vector3D vector = new Vector3D(1.0, 2.0, 3.0); + final double mass = 1.; + final double[] adjoint = new double[6]; + Mockito.when(mockedEnergy.getThrustAccelerationVector(adjoint, mass)).thenReturn(vector); + Mockito.when(mockedEnergy.getHamiltonianContribution(adjoint, mass)).thenCallRealMethod(); + // WHEN + final double contribution = mockedEnergy.getHamiltonianContribution(adjoint, mass); + // THEN + Assertions.assertEquals(-vector.getNormSq() * 0.5, contribution); + } + + @Test + void testGetFieldHamiltonianContribution() { + // GIVEN + final UnboundedCartesianEnergy mockedEnergy = Mockito.mock(UnboundedCartesianEnergy.class); + final Vector3D vector = new Vector3D(1.0, 2.0, 3.0); + final Field field = ComplexField.getInstance(); + final FieldVector3D fieldVector3D = new FieldVector3D<>(field, vector); + final Complex[] fieldAdjoint = MathArrays.buildArray(field, 6); + final Complex fieldMass = Complex.ONE; + final double mass = fieldMass.getReal(); + final double[] adjoint = new double[fieldAdjoint.length]; + Mockito.when(mockedEnergy.getFieldThrustAccelerationVector(fieldAdjoint, fieldMass)).thenReturn(fieldVector3D); + Mockito.when(mockedEnergy.getThrustAccelerationVector(adjoint, mass)).thenReturn(vector); + Mockito.when(mockedEnergy.getFieldHamiltonianContribution(fieldAdjoint, fieldMass)).thenCallRealMethod(); + Mockito.when(mockedEnergy.getHamiltonianContribution(adjoint, mass)).thenCallRealMethod(); + // WHEN + final Complex fieldContribution = mockedEnergy.getFieldHamiltonianContribution(fieldAdjoint, fieldMass); + // THEN + final double contribution = mockedEnergy.getHamiltonianContribution(adjoint, mass); + Assertions.assertEquals(contribution, fieldContribution.getReal()); + } + + @Test + void testGetFieldThrustAccelerationVector() { + // GIVEN + final UnboundedCartesianEnergyNeglectingMass energyNeglectingMass = new UnboundedCartesianEnergyNeglectingMass(""); + final Binary64[] adjoint = MathArrays.buildArray(Binary64Field.getInstance(), 6); + adjoint[3] = Binary64.ONE; + // WHEN + final FieldVector3D fieldThrustVector = energyNeglectingMass.getFieldThrustAccelerationVector(adjoint, Binary64.ONE); + // THEN + final Vector3D thrustVector = energyNeglectingMass.getThrustAccelerationVector(new double[] { 0., 0., 0., 1., 0., 0.}, 1.); + Assertions.assertEquals(thrustVector, fieldThrustVector.toVector3D()); + } + +} diff --git a/src/test/java/org/orekit/control/indirect/adjoint/cost/UnboundedCartesianEnergyTest.java b/src/test/java/org/orekit/control/indirect/adjoint/cost/UnboundedCartesianEnergyTest.java new file mode 100644 index 0000000000..c2b670ab6c --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/adjoint/cost/UnboundedCartesianEnergyTest.java @@ -0,0 +1,171 @@ +/* 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.cost; + +import org.hipparchus.Field; +import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.analysis.differentiation.GradientField; +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.ode.events.Action; +import org.hipparchus.util.MathArrays; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.orekit.control.indirect.adjoint.CartesianAdjointDerivativesProvider; +import org.orekit.control.indirect.adjoint.FieldCartesianAdjointDerivativesProvider; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.EventDetector; +import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.propagation.integration.CombinedDerivatives; +import org.orekit.propagation.integration.FieldCombinedDerivatives; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.PVCoordinates; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +class UnboundedCartesianEnergyTest { + + @Test + void testGetMassFlowRateFactor() { + // GIVEN + final double expectedRateFactor = 1.; + final UnboundedCartesianEnergy unboundedCartesianEnergy = new UnboundedCartesianEnergy("", expectedRateFactor); + // WHEN + final double actualRateFactor = unboundedCartesianEnergy.getMassFlowRateFactor(); + // THEN + Assertions.assertEquals(expectedRateFactor, actualRateFactor); + } + + @Test + void testGetFieldThrustAccelerationVectorFieldFactor() { + // GIVEN + final double massRateFactor = 1.; + final UnboundedCartesianEnergy unboundedCartesianEnergy = new UnboundedCartesianEnergy("", massRateFactor); + final ComplexField field = ComplexField.getInstance(); + final Complex[] fieldAdjoint = MathArrays.buildArray(field, 7); + fieldAdjoint[3] = new Complex(1.0, 0.0); + fieldAdjoint[4] = new Complex(2.0, 0.0); + final Complex mass = new Complex(3., 0.); + // WHEN + final FieldVector3D fieldThrustVector = unboundedCartesianEnergy.getFieldThrustAccelerationVector(fieldAdjoint, mass); + // THEN + final double[] adjoint = new double[7]; + for (int i = 0; i < adjoint.length; i++) { + adjoint[i] = fieldAdjoint[i].getReal(); + } + final Vector3D thrustVector = unboundedCartesianEnergy.getThrustAccelerationVector(adjoint, mass.getReal()); + Assertions.assertEquals(thrustVector, fieldThrustVector.toVector3D()); + } + + @ParameterizedTest + @ValueSource(doubles = {1e-1, 1e10}) + void testFieldDerivatives(final double mass) { + // GIVEN + final String name = "a"; + final UnboundedCartesianEnergy energy = new UnboundedCartesianEnergy(name, 1.); + final FieldCartesianAdjointDerivativesProvider fieldAdjointDerivativesProvider = new FieldCartesianAdjointDerivativesProvider<>(energy); + final Orbit orbit = new CartesianOrbit(new PVCoordinates(new Vector3D(7e6, 1e3, 0), new Vector3D(10., 7e3, -200)), + FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, Constants.EGM96_EARTH_MU); + final SpacecraftState state = new SpacecraftState(orbit, mass).addAdditionalState(name, 1., 2., 3., 4., 5., 6., 7.); + final FieldSpacecraftState fieldState = new FieldSpacecraftState<>(GradientField.getField(1), state); + // WHEN + final FieldCombinedDerivativesfieldCombinedDerivatives = fieldAdjointDerivativesProvider.combinedDerivatives(fieldState); + // THEN + final CartesianAdjointDerivativesProvider adjointDerivativesProvider = new CartesianAdjointDerivativesProvider(energy); + final CombinedDerivatives combinedDerivatives = adjointDerivativesProvider.combinedDerivatives(state); + for (int i = 0; i < 7; i++) { + Assertions.assertEquals(combinedDerivatives.getMainStateDerivativesIncrements()[i], + fieldCombinedDerivatives.getMainStateDerivativesIncrements()[i].getReal(), 1e-12); + } + for (int i = 0; i < 7; i++) { + Assertions.assertEquals(combinedDerivatives.getAdditionalDerivatives()[i], + fieldCombinedDerivatives.getAdditionalDerivatives()[i].getReal(), 1e-10); + } + } + + + @Test + void getEventDetectorsSizeAndActionTest() { + // GIVEN + final double massFlowRateFactor = 1.; + final UnboundedCartesianEnergy unboundedCartesianEnergy = new UnboundedCartesianEnergy("", massFlowRateFactor); + // WHEN + final Stream eventDetectorStream = unboundedCartesianEnergy.getEventDetectors(); + // THEN + final List eventDetectors = eventDetectorStream.collect(Collectors.toList()); + Assertions.assertEquals(1, eventDetectors.size()); + Assertions.assertInstanceOf(CartesianEnergyConsideringMass.SingularityDetector.class, eventDetectors.get(0)); + final CartesianEnergyConsideringMass.SingularityDetector singularityDetector = + (CartesianEnergyConsideringMass.SingularityDetector) eventDetectors.get(0); + Assertions.assertEquals(Action.RESET_DERIVATIVES, singularityDetector.getHandler().eventOccurred(null, null, false)); + } + + @Test + void getFieldEventDetectorsSizeAndActionTest() { + // GIVEN + final double massFlowRateFactor = 2.; + final UnboundedCartesianEnergy unboundedCartesianEnergy = new UnboundedCartesianEnergy("", massFlowRateFactor); + final Field field = ComplexField.getInstance(); + // WHEN + final Stream> eventDetectorStream = unboundedCartesianEnergy.getFieldEventDetectors(field); + // THEN + final List> eventDetectors = eventDetectorStream.collect(Collectors.toList()); + Assertions.assertEquals(1, eventDetectors.size()); + Assertions.assertInstanceOf(CartesianEnergyConsideringMass.FieldSingularityDetector.class, eventDetectors.get(0)); + final CartesianEnergyConsideringMass.FieldSingularityDetector singularityDetector = + (CartesianEnergyConsideringMass.FieldSingularityDetector) eventDetectors.get(0); + Assertions.assertEquals(Action.RESET_DERIVATIVES, singularityDetector.getHandler().eventOccurred(null, null, false)); + } + + @Test + void getFieldEventDetectorsTest() { + // GIVEN + final double massFlowRateFactor = 3.; + final String name = "a"; + final UnboundedCartesianEnergy unboundedCartesianEnergy = new UnboundedCartesianEnergy(name, massFlowRateFactor); + final Field field = ComplexField.getInstance(); + final Orbit orbit = new CartesianOrbit(new PVCoordinates(new Vector3D(7e6, 1e3, 0), new Vector3D(10., 7e3, -200)), + FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, Constants.EGM96_EARTH_MU); + final SpacecraftState state = new SpacecraftState(orbit, 10.).addAdditionalState(name, 1., 2., 3., 4., 5., 6., 7.); + // WHEN + final Stream> fieldEventDetectorStream = unboundedCartesianEnergy.getFieldEventDetectors(field); + // THEN + final List> fieldEventDetectors = fieldEventDetectorStream.collect(Collectors.toList()); + Assertions.assertEquals(1, fieldEventDetectors.size()); + Assertions.assertInstanceOf(CartesianEnergyConsideringMass.FieldSingularityDetector.class, fieldEventDetectors.get(0)); + final CartesianEnergyConsideringMass.FieldSingularityDetector fieldSingularityDetector = + (CartesianEnergyConsideringMass.FieldSingularityDetector) fieldEventDetectors.get(0); + final Complex gValue = fieldSingularityDetector.g(new FieldSpacecraftState<>(field, state)); + final List eventDetectors = unboundedCartesianEnergy.getEventDetectors().collect(Collectors.toList()); + final CartesianEnergyConsideringMass.SingularityDetector singularityDetector = + (CartesianEnergyConsideringMass.SingularityDetector) eventDetectors.get(0); + final double expectedG = singularityDetector.g(state); + Assertions.assertEquals(expectedG, gValue.getReal()); + } + +} diff --git a/src/test/java/org/orekit/control/indirect/shooting/AbstractFixedBoundaryCartesianSingleShootingTest.java b/src/test/java/org/orekit/control/indirect/shooting/AbstractFixedBoundaryCartesianSingleShootingTest.java new file mode 100644 index 0000000000..34aa459814 --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/shooting/AbstractFixedBoundaryCartesianSingleShootingTest.java @@ -0,0 +1,143 @@ +/* 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.shooting; + +import org.hipparchus.Field; +import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.analysis.differentiation.GradientField; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.control.indirect.adjoint.cost.CartesianCost; +import org.orekit.control.indirect.adjoint.cost.UnboundedCartesianEnergyNeglectingMass; +import org.orekit.control.indirect.shooting.boundary.CartesianBoundaryConditionChecker; +import org.orekit.control.indirect.shooting.boundary.FixedTimeBoundaryOrbits; +import org.orekit.control.indirect.shooting.boundary.FixedTimeCartesianBoundaryStates; +import org.orekit.control.indirect.shooting.boundary.NormBasedCartesianConditionChecker; +import org.orekit.control.indirect.shooting.propagation.CartesianAdjointDynamicsProvider; +import org.orekit.control.indirect.shooting.propagation.ShootingPropagationSettings; +import org.orekit.control.indirect.shooting.propagation.ClassicalRungeKuttaIntegrationSettings; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.EventDetector; +import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.propagation.numerical.FieldNumericalPropagator; +import org.orekit.propagation.numerical.NumericalPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.AbsolutePVCoordinates; +import org.orekit.utils.PVCoordinates; + +import java.util.ArrayList; +import java.util.stream.Stream; + +class AbstractFixedBoundaryCartesianSingleShootingTest { + + @Test + void testSolveZeroTimeOfFlight() { + // GIVEN + final String adjointName = "adjoint"; + final AbsolutePVCoordinates initialCondition = new AbsolutePVCoordinates(FramesFactory.getGCRF(), + AbsoluteDate.ARBITRARY_EPOCH, Vector3D.PLUS_I, Vector3D.MINUS_J); + final FixedTimeCartesianBoundaryStates boundaryStates = new FixedTimeCartesianBoundaryStates(initialCondition, + initialCondition); + final CartesianCost cartesianCost = new UnboundedCartesianEnergyNeglectingMass(adjointName); + final TestSingleShooting testSingleShooting = new TestSingleShooting(buildPropagationSettings(cartesianCost), boundaryStates, + new NormBasedCartesianConditionChecker(10, 1, 1)); + final double[] initialGuess = new double[6]; + // WHEN + final ShootingBoundaryOutput boundarySolution = testSingleShooting.solve(1., initialGuess); + // THEN + Assertions.assertArrayEquals(initialGuess, boundarySolution.getInitialState().getAdditionalState(adjointName)); + } + + private static ShootingPropagationSettings buildPropagationSettings(final CartesianCost cost) { + final ClassicalRungeKuttaIntegrationSettings integrationSettings = new ClassicalRungeKuttaIntegrationSettings(10.); + final CartesianAdjointDynamicsProvider derivativesProvider = new CartesianAdjointDynamicsProvider(cost); + return new ShootingPropagationSettings(new ArrayList<>(), derivativesProvider, integrationSettings); + } + + @Test + void testBuildFieldPropagator() { + // GIVEN + final Field field = GradientField.getField(1); + final FixedTimeBoundaryOrbits boundaryOrbits = createBoundaries(); + final CartesianCost mockedCost = mockCost(field); + final TestSingleShooting testShooting = new TestSingleShooting(buildPropagationSettings(mockedCost), + boundaryOrbits, null); + final SpacecraftState state = new SpacecraftState(boundaryOrbits.getInitialOrbit()); + final FieldSpacecraftState fieldState = new FieldSpacecraftState<>(field, state); + // WHEN + final FieldNumericalPropagator fieldPropagator = testShooting.buildFieldPropagator(fieldState); + // THEN + final NumericalPropagator propagator = testShooting.buildPropagator(state); + final int actualSize = fieldPropagator.getEventsDetectors().size(); + Assertions.assertNotEquals(0, actualSize); + Assertions.assertEquals(propagator.getEventsDetectors().size(), actualSize); + } + + @SuppressWarnings("unchecked") + private static CartesianCost mockCost(final Field field) { + final CartesianCost mockedCost = Mockito.mock(CartesianCost.class); + Mockito.when(mockedCost.getAdjointName()).thenReturn(""); + Mockito.when(mockedCost.getEventDetectors()).thenReturn(Stream.of(Mockito.mock(EventDetector.class))); + Mockito.when(mockedCost.getFieldEventDetectors(field)).thenReturn(Stream.of(Mockito.mock(FieldEventDetector.class))); + return mockedCost; + } + + @Test + void testSolveImpossibleTolerance() { + // GIVEN + final String adjointName = "adjoint"; + final double impossibleTolerance = 0.; + final CartesianCost cartesianCost = new UnboundedCartesianEnergyNeglectingMass(adjointName); + final TestSingleShooting testSingleShooting = new TestSingleShooting(buildPropagationSettings(cartesianCost), + createBoundaries(), new NormBasedCartesianConditionChecker(10, impossibleTolerance, impossibleTolerance)); + final double[] initialGuess = new double[6]; + // WHEN + final ShootingBoundaryOutput output = testSingleShooting.solve(1., initialGuess); + // THEN + Assertions.assertFalse(output.isConverged()); + Assertions.assertEquals(testSingleShooting.getPropagationSettings(), output.getShootingPropagationSettings()); + } + + private static FixedTimeBoundaryOrbits createBoundaries() { + final Orbit initialCondition = new CartesianOrbit(new PVCoordinates(Vector3D.PLUS_I, + Vector3D.MINUS_J), FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, 1.); + return new FixedTimeBoundaryOrbits(initialCondition, initialCondition); + } + + private static class TestSingleShooting extends AbstractFixedBoundaryCartesianSingleShooting { + + protected TestSingleShooting(ShootingPropagationSettings propagationSettings, FixedTimeCartesianBoundaryStates boundaryConditions, CartesianBoundaryConditionChecker convergenceChecker) { + super(propagationSettings, boundaryConditions, convergenceChecker); + } + + protected TestSingleShooting(ShootingPropagationSettings propagationSettings, FixedTimeBoundaryOrbits boundaryConditions, CartesianBoundaryConditionChecker convergenceChecker) { + super(propagationSettings, boundaryConditions, convergenceChecker); + } + + @Override + protected double[] updateAdjoint(double[] originalInitialAdjoint, FieldSpacecraftState fieldTerminalState) { + return originalInitialAdjoint; + } + } + +} diff --git a/src/test/java/org/orekit/control/indirect/shooting/AbstractIndirectShootingTest.java b/src/test/java/org/orekit/control/indirect/shooting/AbstractIndirectShootingTest.java new file mode 100644 index 0000000000..fed59cd2f0 --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/shooting/AbstractIndirectShootingTest.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.shooting; + +import org.hipparchus.Field; +import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.analysis.differentiation.GradientField; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.control.indirect.shooting.propagation.AdjointDynamicsProvider; +import org.orekit.control.indirect.shooting.propagation.ClassicalRungeKuttaIntegrationSettings; +import org.orekit.control.indirect.shooting.propagation.ShootingPropagationSettings; +import org.orekit.forces.ForceModel; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.OrbitType; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.integration.AdditionalDerivativesProvider; +import org.orekit.propagation.integration.FieldAdditionalDerivativesProvider; +import org.orekit.propagation.numerical.FieldNumericalPropagator; +import org.orekit.propagation.numerical.NumericalPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.AbsolutePVCoordinates; +import org.orekit.utils.Constants; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; + +import java.util.ArrayList; +import java.util.List; + +class AbstractIndirectShootingTest { + + @Test + void testBuildFieldPropagatorOrbit() { + // GIVEN + final Field field = GradientField.getField(1); + final TestShooting testShooting = new TestShooting(createSettings(field)); + final SpacecraftState state = new SpacecraftState(createOrbit()); + final FieldSpacecraftState fieldState = new FieldSpacecraftState<>(field, state); + // WHEN + final FieldNumericalPropagator fieldPropagator = testShooting.buildFieldPropagator(fieldState); + // THEN + final NumericalPropagator propagator = testShooting.buildPropagator(state); + comparePropagators(propagator, fieldPropagator); + final OrbitType orbitType = fieldPropagator.getOrbitType(); + Assertions.assertEquals(OrbitType.CARTESIAN, orbitType); + } + + @Test + void testBuildFieldPropagator() { + // GIVEN + final Field field = GradientField.getField(1); + final TestShooting testShooting = new TestShooting(createSettings(field)); + final CartesianOrbit orbit = createOrbit(); + final SpacecraftState state = new SpacecraftState(new AbsolutePVCoordinates(orbit.getFrame(), + orbit.getDate(), orbit.getPVCoordinates())); + final FieldSpacecraftState fieldState = new FieldSpacecraftState<>(field, state); + // WHEN + final FieldNumericalPropagator fieldPropagator = testShooting.buildFieldPropagator(fieldState); + // THEN + final NumericalPropagator propagator = testShooting.buildPropagator(state); + comparePropagators(propagator, fieldPropagator); + } + + private void comparePropagators(final NumericalPropagator propagator, + final FieldNumericalPropagator fieldPropagator) { + final List forceModelList = fieldPropagator.getAllForceModels(); + Assertions.assertEquals(1, forceModelList.size()); + Assertions.assertEquals(propagator.getAllForceModels().size(), forceModelList.size()); + Assertions.assertEquals(propagator.getAllForceModels().get(0), forceModelList.get(0)); + Assertions.assertEquals(propagator.getAttitudeProvider(), fieldPropagator.getAttitudeProvider()); + Assertions.assertEquals(propagator.getFrame(), fieldPropagator.getFrame()); + Assertions.assertEquals(propagator.getEventsDetectors().size(), fieldPropagator.getEventsDetectors().size()); + Assertions.assertEquals(propagator.getOrbitType(), fieldPropagator.getOrbitType()); + } + + @SuppressWarnings("unchecked") + private ShootingPropagationSettings createSettings(final Field field) { + final List forceModelList = new ArrayList<>(); + final ForceModel mockedForceModel = Mockito.mock(ForceModel.class); + forceModelList.add(mockedForceModel); + final AdjointDynamicsProvider adjointDynamicsProvider = Mockito.mock(AdjointDynamicsProvider.class); + Mockito.when(adjointDynamicsProvider.buildAdditionalDerivativesProvider()) + .thenReturn(Mockito.mock(AdditionalDerivativesProvider.class)); + Mockito.when(adjointDynamicsProvider.buildFieldAdditionalDerivativesProvider(field)) + .thenReturn(Mockito.mock(FieldAdditionalDerivativesProvider.class)); + return new ShootingPropagationSettings(forceModelList, adjointDynamicsProvider, + new ClassicalRungeKuttaIntegrationSettings(1.)); + } + + private static CartesianOrbit createOrbit() { + return new CartesianOrbit(new TimeStampedPVCoordinates(AbsoluteDate.ARBITRARY_EPOCH, + new PVCoordinates(Vector3D.MINUS_J.scalarMultiply(1e6), Vector3D.MINUS_K.scalarMultiply(10))), + FramesFactory.getGCRF(), Constants.EGM96_EARTH_MU); + } + + private static class TestShooting extends AbstractIndirectShooting { + + public TestShooting(ShootingPropagationSettings propagationSettings) { + super(propagationSettings); + } + + @Override + public ShootingBoundaryOutput solve(double initialMass, double[] initialGuess) { + return null; + } + } + +} diff --git a/src/test/java/org/orekit/control/indirect/shooting/NewtonFixedBoundaryCartesianSingleShootingTest.java b/src/test/java/org/orekit/control/indirect/shooting/NewtonFixedBoundaryCartesianSingleShootingTest.java new file mode 100644 index 0000000000..b9fe195097 --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/shooting/NewtonFixedBoundaryCartesianSingleShootingTest.java @@ -0,0 +1,423 @@ +/* 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.shooting; + +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.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.Mockito; +import org.orekit.Utils; +import org.orekit.control.indirect.adjoint.CartesianAdjointJ2Term; +import org.orekit.control.indirect.adjoint.CartesianAdjointKeplerianTerm; +import org.orekit.control.indirect.adjoint.cost.BoundedCartesianEnergy; +import org.orekit.control.indirect.adjoint.cost.CartesianCost; +import org.orekit.control.indirect.adjoint.cost.UnboundedCartesianEnergy; +import org.orekit.control.indirect.adjoint.cost.UnboundedCartesianEnergyNeglectingMass; +import org.orekit.control.indirect.shooting.boundary.CartesianBoundaryConditionChecker; +import org.orekit.control.indirect.shooting.boundary.FixedTimeBoundaryOrbits; +import org.orekit.control.indirect.shooting.boundary.FixedTimeCartesianBoundaryStates; +import org.orekit.control.indirect.shooting.boundary.NormBasedCartesianConditionChecker; +import org.orekit.control.indirect.shooting.propagation.*; +import org.orekit.forces.ForceModel; +import org.orekit.forces.gravity.J2OnlyPerturbation; +import org.orekit.forces.gravity.NewtonianAttraction; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.*; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.analytical.KeplerianPropagator; +import org.orekit.propagation.events.EventDetectionSettings; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.DateTimeComponents; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.*; + +import java.util.ArrayList; +import java.util.List; + +class NewtonFixedBoundaryCartesianSingleShootingTest { + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data"); + } + + @Test + void testUpdateAdjointZeroDefects() { + // GIVEN + final double[] originalAdjoint = new double[] { 1, 2, 3, 4, 5, 6 }; + final GradientField field = GradientField.getField(6); + final TimeStampedPVCoordinates targetPV = new TimeStampedPVCoordinates(AbsoluteDate.ARBITRARY_EPOCH, + new PVCoordinates(new Vector3D(10., 1., 0.1), new Vector3D(0.001, 0.1, -0.0001))); + final TimeStampedFieldPVCoordinates fieldPVCoordinates = new TimeStampedFieldPVCoordinates<>(field, + targetPV); + final FieldCartesianOrbit fieldOrbit = new FieldCartesianOrbit<>( + new FieldPVCoordinates<>(fieldPVCoordinates.getPosition().add(getFieldVector3D(field, 0, 1, 2)), + fieldPVCoordinates.getVelocity().add(getFieldVector3D(field, 3, 4, 5))), + FramesFactory.getGCRF(), new FieldAbsoluteDate<>(field, targetPV.getDate()), field.getOne()); + final FieldSpacecraftState fieldState = new FieldSpacecraftState<>(fieldOrbit); + final NewtonFixedBoundaryCartesianSingleShooting shooting = Mockito.mock(NewtonFixedBoundaryCartesianSingleShooting.class); + final double one = 1; + Mockito.when(shooting.getScalePositionDefects()).thenReturn(one); + Mockito.when(shooting.getScaleVelocityDefects()).thenReturn(one); + Mockito.when(shooting.updateAdjoint(originalAdjoint, fieldState)).thenCallRealMethod(); + Mockito.when(shooting.getTerminalCartesianState()).thenReturn(targetPV); + // WHEN + final double[] adjoint = shooting.updateAdjoint(originalAdjoint, fieldState); + // THEN + Assertions.assertArrayEquals(originalAdjoint, adjoint); + } + + private static FieldVector3D getFieldVector3D(final GradientField field, final int i1, + final int i2, final int i3) { + final int parametersNumber = field.getOne().getFreeParameters(); + return new FieldVector3D<>(Gradient.variable(parametersNumber, i1, 0), + Gradient.variable(parametersNumber, i2, 0), Gradient.variable(parametersNumber, i3, 0)); + } + + @ParameterizedTest + @ValueSource(doubles = {5e5, 8e5, 1.5e6}) + void testSolveOrbitVersusAbsolutePV(final double approximateAltitude) { + // GIVEN + final double tolerancePosition = 1e0; + final double toleranceVelocity = 1e-4; + final CartesianBoundaryConditionChecker conditionChecker = new NormBasedCartesianConditionChecker(10, + tolerancePosition, toleranceVelocity); + final Orbit initialOrbit = createSomeInitialOrbit(approximateAltitude); + final double timeOfFlight = 1e4; + final Orbit terminalOrbit = createTerminalBoundary(initialOrbit, timeOfFlight); + final FixedTimeBoundaryOrbits boundaryOrbits = new FixedTimeBoundaryOrbits(initialOrbit, terminalOrbit); + final CartesianCost cartesianCost = new UnboundedCartesianEnergyNeglectingMass("adjoint"); + final ShootingPropagationSettings propagationSettings = createShootingSettings(initialOrbit, cartesianCost, + new ClassicalRungeKuttaIntegrationSettings(60.)); + final NewtonFixedBoundaryCartesianSingleShooting shooting = new NewtonFixedBoundaryCartesianSingleShooting(propagationSettings, + boundaryOrbits, conditionChecker); + shooting.setScalePositionDefects(1.); + shooting.setScaleVelocityDefects(1.); + final double mass = 1e3; + final double[] guess = new double[6]; + // WHEN + final ShootingBoundaryOutput output = shooting.solve(mass, guess); + // THEN + Assertions.assertTrue(output.isConverged()); + Assertions.assertNotEquals(0, output.getIterationCount()); + final PVCoordinates expectedPV = terminalOrbit.getPVCoordinates(); + final PVCoordinates actualPV = output.getTerminalState().getPVCoordinates(); + Assertions.assertEquals(0., expectedPV.getPosition().subtract(actualPV.getPosition()).getNorm(), + tolerancePosition); + Assertions.assertEquals(0., expectedPV.getVelocity().subtract(actualPV.getVelocity()).getNorm(), + toleranceVelocity); + compareToAbsolutePV(mass, guess, propagationSettings, boundaryOrbits, conditionChecker, 0., + output); + } + + private static ShootingPropagationSettings createShootingSettings(final Orbit initialOrbit, + final CartesianCost cartesianCost, + final ShootingIntegrationSettings integrationSettings) { + final NewtonianAttraction newtonianAttraction = new NewtonianAttraction(initialOrbit.getMu()); + final Frame J2Frame = initialOrbit.getFrame(); // approximation for speed + final J2OnlyPerturbation j2OnlyPerturbation = new J2OnlyPerturbation(initialOrbit.getMu(), + Constants.EGM96_EARTH_EQUATORIAL_RADIUS, -Constants.EGM96_EARTH_C20, J2Frame); + final List forceModelList = new ArrayList<>(); + forceModelList.add(newtonianAttraction); + forceModelList.add(j2OnlyPerturbation); + final CartesianAdjointKeplerianTerm keplerianTerm = new CartesianAdjointKeplerianTerm(initialOrbit.getMu()); + final CartesianAdjointJ2Term j2Term = new CartesianAdjointJ2Term(j2OnlyPerturbation.getMu(), j2OnlyPerturbation.getrEq(), + j2OnlyPerturbation.getJ2(initialOrbit.getDate()), j2OnlyPerturbation.getFrame()); + final AdjointDynamicsProvider adjointDynamicsProvider = new CartesianAdjointDynamicsProvider(cartesianCost, + keplerianTerm, j2Term); + return new ShootingPropagationSettings(forceModelList, adjointDynamicsProvider, integrationSettings); + } + + private static Orbit createSomeInitialOrbit(final double approximateAltitude) { + return new KeplerianOrbit(Constants.EGM96_EARTH_EQUATORIAL_RADIUS + approximateAltitude, 1e-4, 1., 2., 3., 4., PositionAngleType.ECCENTRIC, + FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, Constants.EGM96_EARTH_MU); + } + + private static Orbit createTerminalBoundary(final Orbit initialOrbit, final double timeOfFlight) { + final KeplerianPropagator propagator = new KeplerianPropagator(initialOrbit); + final AbsoluteDate epoch = initialOrbit.getDate(); + return propagator.propagate(epoch.shiftedBy(timeOfFlight)).getOrbit(); + } + + private void compareToAbsolutePV(final double mass, final double[] guess, + final ShootingPropagationSettings propagationSettings, + final FixedTimeBoundaryOrbits boundaryOrbits, + final CartesianBoundaryConditionChecker conditionChecker, + final double toleranceMassAdjoint, + final ShootingBoundaryOutput output) { + final Orbit initialOrbit = boundaryOrbits.getInitialOrbit(); + final Orbit terminalOrbit = boundaryOrbits.getTerminalOrbit(); + final FixedTimeCartesianBoundaryStates boundaryStates = new FixedTimeCartesianBoundaryStates(convertToAbsolutePVCoordinates(initialOrbit), + convertToAbsolutePVCoordinates(terminalOrbit)); + final NewtonFixedBoundaryCartesianSingleShooting shooting = new NewtonFixedBoundaryCartesianSingleShooting(propagationSettings, + boundaryStates, conditionChecker); + shooting.setToleranceMassAdjoint(toleranceMassAdjoint); + final ShootingBoundaryOutput otherOutput = shooting.solve(mass, guess); + Assertions.assertEquals(otherOutput.getIterationCount(), output.getIterationCount()); + Assertions.assertEquals(otherOutput.getTerminalState().getPosition(), otherOutput.getTerminalState().getPosition()); + final String adjointName = propagationSettings.getAdjointDynamicsProvider().getAdjointName(); + Assertions.assertArrayEquals(output.getInitialState().getAdditionalState(adjointName), + otherOutput.getInitialState().getAdditionalState(adjointName)); + } + + private static AbsolutePVCoordinates convertToAbsolutePVCoordinates(final Orbit orbit) { + return new AbsolutePVCoordinates(orbit.getFrame(), orbit.getDate(), orbit.getPVCoordinates()); + } + + @Test + void testSolveSequential() { + // GIVEN + final double tolerancePosition = 1e-0; + final double toleranceVelocity = 1e-4; + final CartesianBoundaryConditionChecker conditionChecker = new NormBasedCartesianConditionChecker(10, + tolerancePosition, toleranceVelocity); + final Orbit initialOrbit = createSomeInitialOrbit(1e6); + final double timeOfFlight = 1e4; + final Orbit terminalOrbit = createTerminalBoundary(initialOrbit, timeOfFlight); + final FixedTimeBoundaryOrbits boundaryOrbits = new FixedTimeBoundaryOrbits(initialOrbit, terminalOrbit); + final double flowRateFactor = 1e-3; + final CartesianCost cartesianCost = new UnboundedCartesianEnergy("adjoint", flowRateFactor); + final ShootingPropagationSettings propagationSettings = createShootingSettings(initialOrbit, cartesianCost, + new DormandPrince54IntegrationSettings(1e-1, 1e2, 1e-3, 1e-6)); + final NewtonFixedBoundaryCartesianSingleShooting shooting = new NewtonFixedBoundaryCartesianSingleShooting(propagationSettings, + boundaryOrbits, conditionChecker); + final double toleranceMassAdjoint = 1e-10; + shooting.setToleranceMassAdjoint(toleranceMassAdjoint); + final double mass = 1e3; + final double[] guess = new double[]{-2.305656141544546E-6, -6.050107349447073E-6, -4.484389270662034E-6, + -9.635757291472267E-4, -0.0026076008704216066, 8.621848929368622E-5, 0.}; + // WHEN + final ShootingBoundaryOutput output = shooting.solve(mass, guess); + // THEN + final double thrustBound = 1e5; + final ShootingPropagationSettings propagationSettingsBoundedEnergy = createShootingSettings(initialOrbit, + new BoundedCartesianEnergy(cartesianCost.getAdjointName(), flowRateFactor, thrustBound), + propagationSettings.getIntegrationSettings()); + final NewtonFixedBoundaryCartesianSingleShooting shootingBoundedEnergy = new NewtonFixedBoundaryCartesianSingleShooting(propagationSettingsBoundedEnergy, + boundaryOrbits, conditionChecker); + final double[] unboundedEnergyAdjoint = output.getInitialState().getAdditionalState(cartesianCost.getAdjointName()); + double[] guessBoundedEnergy = unboundedEnergyAdjoint.clone(); + final ShootingBoundaryOutput outputBoundedEnergy = shootingBoundedEnergy.solve(mass, guessBoundedEnergy); + Assertions.assertTrue(outputBoundedEnergy.isConverged()); + Assertions.assertEquals(0, outputBoundedEnergy.getIterationCount()); + } + + @Test + void testSolveRegression() { + // GIVEN + final double massFlowRateFactor = 2e-6; + final CartesianCost cartesianCost = new UnboundedCartesianEnergy("adjoint", massFlowRateFactor); + final Orbit initialOrbit = createSomeInitialOrbit(1e6); + final double timeOfFlight = initialOrbit.getKeplerianPeriod() * 5; + final Orbit terminalOrbit = createTerminalBoundary(initialOrbit, timeOfFlight); + final NewtonFixedBoundaryCartesianSingleShooting shooting = getShootingMethod(cartesianCost, + new FixedTimeBoundaryOrbits(initialOrbit, terminalOrbit), + new ClassicalRungeKuttaIntegrationSettings(100.)); + final double toleranceMassAdjoint = 1e-10; + shooting.setToleranceMassAdjoint(toleranceMassAdjoint); + final double mass = 1.; + final double[] guess = {-1.3440754763650783E-6, -6.346307866897998E-6, -4.25736594074492E-6, + -4.54324936872417E-4, -0.0020329894350755227, -8.358161689612435E-4, 0.}; + // WHEN + final ShootingBoundaryOutput output = shooting.solve(mass, guess); + // THEN + Assertions.assertTrue(output.isConverged()); + final double[] initialAdjoint = output.getInitialState().getAdditionalState(cartesianCost.getAdjointName()); + final double[] expectedAdjoint = new double[] {-1.3432883741256684E-6, -6.343244627959342E-6, -4.2552646864846415E-6, + -4.540374638007354E-4, -0.002031906384904598, -8.355018662664441E-4, -1.0320210230861449}; + final double tolerance = 1e-8; + for (int i = 0; i < expectedAdjoint.length; i++) { + Assertions.assertEquals(expectedAdjoint[i], initialAdjoint[i], tolerance); + } + Assertions.assertNotEquals(1., output.getTerminalState().getMass()); + } + + private static NewtonFixedBoundaryCartesianSingleShooting getShootingMethod(final CartesianCost cartesianCost, + final FixedTimeBoundaryOrbits fixedTimeBoundaryOrbits, + final ShootingIntegrationSettings integrationSettings) { + final double tolerancePosition = 1e-0; + final double toleranceVelocity = 1e-4; + final CartesianBoundaryConditionChecker conditionChecker = new NormBasedCartesianConditionChecker(10, + tolerancePosition, toleranceVelocity); + final FixedTimeBoundaryOrbits boundaryOrbits = new FixedTimeBoundaryOrbits(fixedTimeBoundaryOrbits.getInitialOrbit(), + fixedTimeBoundaryOrbits.getTerminalOrbit()); + final ShootingPropagationSettings propagationSettings = createShootingSettings(fixedTimeBoundaryOrbits.getInitialOrbit(), + cartesianCost, integrationSettings); + final NewtonFixedBoundaryCartesianSingleShooting shooting = new NewtonFixedBoundaryCartesianSingleShooting(propagationSettings, + boundaryOrbits, conditionChecker); + shooting.setScalePositionDefects(1e3); + shooting.setScaleVelocityDefects(1.); + return shooting; + } + + @Test + void testSolveForwardBackward() { + // GIVEN + final CartesianCost cartesianCost = new UnboundedCartesianEnergyNeglectingMass("adjoint"); + final Orbit initialOrbit = createGeoInitialOrbit(); + final double timeOfFlight = initialOrbit.getKeplerianPeriod() * 3; + final Orbit terminalOrbit = createTerminalBoundary(initialOrbit, timeOfFlight); + final ShootingIntegrationSettings integrationSettings = new ClassicalRungeKuttaIntegrationSettings(100.); + final NewtonFixedBoundaryCartesianSingleShooting shooting = getShootingMethod(cartesianCost, + new FixedTimeBoundaryOrbits(initialOrbit, terminalOrbit), integrationSettings); + final double toleranceMassAdjoint = 1e-10; + final double initialMass = 3e3; + shooting.setToleranceMassAdjoint(toleranceMassAdjoint); + final double[] guess = new double[] {-1.429146468892837E-10, 4.5022870335769276E-11, -1.318194179536703E-12, + -1.098381235039422E-6, -1.6798876678906052E-6, -3.207856651454041E-9}; + // WHEN + final ShootingBoundaryOutput forwardOutput = shooting.solve(initialMass, guess); + // THEN + final SpacecraftState terminalState = forwardOutput.getTerminalState(); + final String adjointName = cartesianCost.getAdjointName(); + final double[] terminalAdjointForward = terminalState.getAdditionalState(adjointName); + final NewtonFixedBoundaryCartesianSingleShooting backwardShooting = getShootingMethod(cartesianCost, + new FixedTimeBoundaryOrbits(terminalOrbit, initialOrbit), integrationSettings); + final ShootingBoundaryOutput backwardOutput = backwardShooting.solve(terminalState.getMass(), terminalAdjointForward); + Assertions.assertTrue(backwardOutput.isConverged()); + Assertions.assertEquals(0, backwardOutput.getIterationCount()); + final double[] initialAdjointForward = forwardOutput.getInitialState().getAdditionalState(adjointName); + final double[] terminalAdjointBackward = backwardOutput.getTerminalState().getAdditionalState(adjointName); + for (int i = 0; i < initialAdjointForward.length; i++) { + Assertions.assertEquals(initialAdjointForward[i], terminalAdjointBackward[i], 1e-13); + } + } + + private static Orbit createGeoInitialOrbit() { + return new KeplerianOrbit(Constants.EGM96_EARTH_EQUATORIAL_RADIUS + 35000e3, 1e-5, 0.001, 2., 3., 4., PositionAngleType.ECCENTRIC, + FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, Constants.EGM96_EARTH_MU); + } + + @ParameterizedTest + @ValueSource(doubles = {1e-4, 1e-3, 1e-2}) + void testSolveUnboundedCartesianEnergy(final double flowRateFactor) { + // GIVEN + final double tolerancePosition = 1e1; + final double toleranceVelocity = 1e-3; + final CartesianBoundaryConditionChecker conditionChecker = new NormBasedCartesianConditionChecker(10, + tolerancePosition, toleranceVelocity); + final FixedTimeBoundaryOrbits boundaryOrbits = createBoundaryForKeplerianSettings(); + final CartesianCost cartesianCost = new UnboundedCartesianEnergy("adjoint", flowRateFactor); + final DormandPrince54IntegrationSettings integrationSettings = new DormandPrince54IntegrationSettings(1e-2, 2e2, 1e-3, 1e-6); + final ShootingPropagationSettings propagationSettings = createKeplerianShootingSettings(boundaryOrbits.getInitialOrbit(), + cartesianCost, integrationSettings); + final NewtonFixedBoundaryCartesianSingleShooting shooting = new NewtonFixedBoundaryCartesianSingleShooting(propagationSettings, + boundaryOrbits, conditionChecker); + final double toleranceMassAdjoint = 1e-8; + shooting.setToleranceMassAdjoint(toleranceMassAdjoint); + final double mass = 1e3; + final double[] guess = guessWithoutMass(cartesianCost.getAdjointName(), mass, integrationSettings, boundaryOrbits, + conditionChecker); + // WHEN + final ShootingBoundaryOutput output = shooting.solve(mass, guess); + // THEN + Assertions.assertTrue(output.isConverged()); + } + + private static FixedTimeBoundaryOrbits createBoundaryForKeplerianSettings() { + final Orbit initialOrbit = createSomeInitialOrbit(2e6); + final PVCoordinates templatePV = initialOrbit.getPVCoordinates(); + final Orbit modifiedOrbit = new CartesianOrbit(new PVCoordinates(templatePV.getPosition().add(new Vector3D(1e3, 2e3, 3e3)), templatePV.getVelocity()), + initialOrbit.getFrame(), initialOrbit.getDate(), initialOrbit.getMu()); + final double timeOfFlight = 1e5; + final Orbit terminalOrbit = createTerminalBoundary(modifiedOrbit, timeOfFlight); + return new FixedTimeBoundaryOrbits(modifiedOrbit, terminalOrbit); + } + + private static ShootingPropagationSettings createKeplerianShootingSettings(final Orbit initialOrbit, + final CartesianCost cartesianCost, + final ShootingIntegrationSettings integrationSettings) { + final NewtonianAttraction newtonianAttraction = new NewtonianAttraction(initialOrbit.getMu()); + final List forceModelList = new ArrayList<>(); + forceModelList.add(newtonianAttraction); + final CartesianAdjointKeplerianTerm keplerianTerm = new CartesianAdjointKeplerianTerm(initialOrbit.getMu()); + final AdjointDynamicsProvider adjointDynamicsProvider = new CartesianAdjointDynamicsProvider(cartesianCost, + keplerianTerm); + return new ShootingPropagationSettings(forceModelList, adjointDynamicsProvider, integrationSettings); + } + + private static double[] guessWithoutMass(final String adjointName, final double mass, + final ShootingIntegrationSettings integrationSettings, + final FixedTimeBoundaryOrbits boundaryOrbits, + final CartesianBoundaryConditionChecker conditionChecker) { + final ShootingPropagationSettings propagationSettings = createKeplerianShootingSettings(boundaryOrbits.getInitialOrbit(), + new UnboundedCartesianEnergyNeglectingMass(adjointName), integrationSettings); + final NewtonFixedBoundaryCartesianSingleShooting shooting = new NewtonFixedBoundaryCartesianSingleShooting(propagationSettings, + boundaryOrbits, conditionChecker); + final ShootingBoundaryOutput output = shooting.solve(mass, new double[6]); + final double squaredMass = mass * mass; + final double[] adjoint = output.getInitialState().getAdditionalState(adjointName); + final double[] adjointWithMass = new double[7]; + for (int i = 0; i < adjoint.length; i++) { + adjointWithMass[i] = adjoint[i] * squaredMass; + } + adjointWithMass[adjointWithMass.length - 1] = -1.; + return adjointWithMass; + } + + @Test + void testSolveHeliocentric() { + // GIVEN + final double tolerancePosition = 1e5; + final double toleranceVelocity = 1e0; + final CartesianBoundaryConditionChecker conditionChecker = new NormBasedCartesianConditionChecker(10, + tolerancePosition, toleranceVelocity); + final FixedTimeBoundaryOrbits boundaryOrbits = getHeliocentricBoundary(); + final DormandPrince54IntegrationSettings integrationSettings = new DormandPrince54IntegrationSettings(2e2, 1e5, 1e5, 1e-1); + final EventDetectionSettings detectionSettings = new EventDetectionSettings(1e5, 1e3, EventDetectionSettings.DEFAULT_MAX_ITER); + final CartesianCost cartesianCost = new UnboundedCartesianEnergy("adjoint", 1. / (4000. * Constants.G0_STANDARD_GRAVITY), + detectionSettings); + final ShootingPropagationSettings propagationSettings = createKeplerianShootingSettings(boundaryOrbits.getInitialOrbit(), + cartesianCost, integrationSettings); + final NewtonFixedBoundaryCartesianSingleShooting shooting = new NewtonFixedBoundaryCartesianSingleShooting(propagationSettings, + boundaryOrbits, conditionChecker); + final double toleranceMassAdjoint = 1e-7; + shooting.setToleranceMassAdjoint(toleranceMassAdjoint); + final double mass = 2e3; + final double[] guess = guessWithoutMass(cartesianCost.getAdjointName(), mass, integrationSettings, boundaryOrbits, + conditionChecker); + // WHEN + final ShootingBoundaryOutput output = shooting.solve(mass, guess); + // THEN + Assertions.assertTrue(output.isConverged()); + } + + private static FixedTimeBoundaryOrbits getHeliocentricBoundary() { + final double mu = Constants.JPL_SSD_SUN_GM; + final Frame frame = FramesFactory.getGCRF(); + final AbsoluteDate date = new AbsoluteDate(new DateTimeComponents(2035, 1, 1, 0, 0, 0), + TimeScalesFactory.getUTC()); + final Vector3D position = new Vector3D(269630575634.1845, -317928797663.87445, -117503661424.1842); + final Vector3D velocity = new Vector3D(12803.992418160833, 12346.009014593829, 2789.3378661767967); + final Orbit initialOrbit = new CartesianOrbit(new TimeStampedPVCoordinates(date, position, velocity), frame, mu); + final AbsoluteDate terminalDate = new AbsoluteDate(new DateTimeComponents(2038, 4, 20, 7, 48, 0), + TimeScalesFactory.getUTC()); + final Vector3D terminalPosition = new Vector3D(-254040098474.26975, 292309940514.6629, 61765199864.609174); + final Vector3D terminalVelocity = new Vector3D(-15342.352873059252, -10427.635262141607, -7365.033285214819); + final Orbit terminalOrbit = new CartesianOrbit(new TimeStampedPVCoordinates(terminalDate, terminalPosition, terminalVelocity), frame, mu); + return new FixedTimeBoundaryOrbits(initialOrbit, terminalOrbit); + } +} diff --git a/src/test/java/org/orekit/control/indirect/shooting/boundary/NormBasedCartesianConditionCheckerTest.java b/src/test/java/org/orekit/control/indirect/shooting/boundary/NormBasedCartesianConditionCheckerTest.java new file mode 100644 index 0000000000..9366a6b1bd --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/shooting/boundary/NormBasedCartesianConditionCheckerTest.java @@ -0,0 +1,65 @@ +/* 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.shooting.boundary; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.utils.PVCoordinates; + +class NormBasedCartesianConditionCheckerTest { + + @Test + void testIsConvergedTrivial() { + // GIVEN + final int expectedMaximumCount = 10; + final double toleranceDistance = 0.; + final double toleranceSpeed = 0.; + final NormBasedCartesianConditionChecker convergenceChecker = new NormBasedCartesianConditionChecker(expectedMaximumCount, + toleranceDistance, toleranceSpeed); + final PVCoordinates pvCoordinates = new PVCoordinates(Vector3D.MINUS_J, Vector3D.MINUS_K); + // WHEN & THEN + Assertions.assertFalse(convergenceChecker.isConverged(pvCoordinates, pvCoordinates)); + Assertions.assertEquals(expectedMaximumCount, convergenceChecker.getMaximumIterationCount()); + } + + @Test + void testIsConvergedPosition() { + // GIVEN + final int expectedMaximumCount = 10; + final double toleranceDistance = 10.; + final double toleranceSpeed = Double.POSITIVE_INFINITY; + final NormBasedCartesianConditionChecker convergenceChecker = new NormBasedCartesianConditionChecker(expectedMaximumCount, + toleranceDistance, toleranceSpeed); + final PVCoordinates pvCoordinates = new PVCoordinates(Vector3D.MINUS_J.scalarMultiply(5.), Vector3D.ZERO); + // WHEN & THEN + Assertions.assertTrue(convergenceChecker.isConverged(pvCoordinates, new PVCoordinates())); + } + + @Test + void testIsConvergedVelocity() { + // GIVEN + final int expectedMaximumCount = 10; + final double toleranceDistance = 10.; + final double toleranceSpeed = 1.; + final NormBasedCartesianConditionChecker convergenceChecker = new NormBasedCartesianConditionChecker(expectedMaximumCount, + toleranceDistance, toleranceSpeed); + final PVCoordinates pvCoordinates = new PVCoordinates(Vector3D.ZERO, Vector3D.PLUS_I.scalarMultiply(2.)); + // WHEN & THEN + Assertions.assertFalse(convergenceChecker.isConverged(pvCoordinates, new PVCoordinates())); + } +} diff --git a/src/test/java/org/orekit/control/indirect/shooting/propagation/CartesianAdjointDynamicsProviderTest.java b/src/test/java/org/orekit/control/indirect/shooting/propagation/CartesianAdjointDynamicsProviderTest.java new file mode 100644 index 0000000000..25d39657de --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/shooting/propagation/CartesianAdjointDynamicsProviderTest.java @@ -0,0 +1,44 @@ +/* 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.shooting.propagation; + +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.control.indirect.adjoint.cost.CartesianCost; +import org.orekit.propagation.integration.AdditionalDerivativesProvider; +import org.orekit.propagation.integration.FieldAdditionalDerivativesProvider; + +class CartesianAdjointDynamicsProviderTest { + + @Test + void testBuildAdditionalDerivativesProvider() { + // GIVEN + final CartesianCost mockedCost = Mockito.mock(CartesianCost.class); + Mockito.when(mockedCost.getAdjointDimension()).thenReturn(1); + Mockito.when(mockedCost.getAdjointName()).thenReturn("1"); + final CartesianAdjointDynamicsProvider dynamicsProvider = new CartesianAdjointDynamicsProvider(mockedCost); + // WHEN + final AdditionalDerivativesProvider derivativesProvider = dynamicsProvider.buildAdditionalDerivativesProvider(); + // THEN + final FieldAdditionalDerivativesProvider fieldDerivativesProvider = dynamicsProvider.buildFieldAdditionalDerivativesProvider(ComplexField.getInstance()); + Assertions.assertEquals(derivativesProvider.getDimension(), fieldDerivativesProvider.getDimension()); + Assertions.assertEquals(derivativesProvider.getName(), fieldDerivativesProvider.getName()); + } +} diff --git a/src/test/java/org/orekit/control/indirect/shooting/propagation/ClassicalRungeKuttaIntegrationSettingsTest.java b/src/test/java/org/orekit/control/indirect/shooting/propagation/ClassicalRungeKuttaIntegrationSettingsTest.java new file mode 100644 index 0000000000..32c8afb68e --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/shooting/propagation/ClassicalRungeKuttaIntegrationSettingsTest.java @@ -0,0 +1,73 @@ +/* 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.shooting.propagation; + +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +import org.hipparchus.ode.AbstractFieldIntegrator; +import org.hipparchus.ode.AbstractIntegrator; +import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaFieldIntegrator; +import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaIntegrator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.orbits.FieldOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.propagation.conversion.ClassicalRungeKuttaFieldIntegratorBuilder; +import org.orekit.propagation.conversion.ClassicalRungeKuttaIntegratorBuilder; +import org.orekit.propagation.conversion.FieldODEIntegratorBuilder; +import org.orekit.propagation.conversion.ODEIntegratorBuilder; + +class ClassicalRungeKuttaIntegrationSettingsTest { + + @Test + void testGetIntegratorBuilder() { + // GIVEN + final double expectedStep = 1.; + final ClassicalRungeKuttaIntegrationSettings integrationSettings = new ClassicalRungeKuttaIntegrationSettings(expectedStep); + // WHEN + final ODEIntegratorBuilder builder = integrationSettings.getIntegratorBuilder(); + // THEN + Assertions.assertInstanceOf(ClassicalRungeKuttaIntegratorBuilder.class, builder); + final AbstractIntegrator integrator = builder.buildIntegrator(Mockito.mock(Orbit.class), + Mockito.mock(OrbitType.class)); + Assertions.assertEquals(expectedStep, ((ClassicalRungeKuttaIntegrator) integrator).getDefaultStep()); + } + + @Test + void testFieldGetIntegratorBuilder() { + // GIVEN + final double expectedStep = 1.; + final ClassicalRungeKuttaIntegrationSettings integrationSettings = new ClassicalRungeKuttaIntegrationSettings(expectedStep); + // WHEN + final FieldODEIntegratorBuilder builder = integrationSettings.getFieldIntegratorBuilder(ComplexField.getInstance()); + // THEN + Assertions.assertInstanceOf(ClassicalRungeKuttaFieldIntegratorBuilder.class, builder); + final FieldOrbit mockedFieldOrbit = mockFieldOrbit(); + final AbstractFieldIntegrator integrator = builder.buildIntegrator(mockedFieldOrbit, Mockito.mock(OrbitType.class)); + Assertions.assertEquals(expectedStep, ((ClassicalRungeKuttaFieldIntegrator) integrator).getDefaultStep().getReal()); + } + + @SuppressWarnings("unchecked") + private static FieldOrbit mockFieldOrbit() { + final FieldOrbit mockedFieldOrbit = Mockito.mock(FieldOrbit.class); + Mockito.when(mockedFieldOrbit.getA()).thenReturn(Complex.ONE); + return mockedFieldOrbit; + } + +} diff --git a/src/test/java/org/orekit/control/indirect/shooting/propagation/DormandPrince54IntegrationSettingsTest.java b/src/test/java/org/orekit/control/indirect/shooting/propagation/DormandPrince54IntegrationSettingsTest.java new file mode 100644 index 0000000000..9bfb0aff8a --- /dev/null +++ b/src/test/java/org/orekit/control/indirect/shooting/propagation/DormandPrince54IntegrationSettingsTest.java @@ -0,0 +1,49 @@ +/* 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.shooting.propagation; + +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.ode.FieldODEIntegrator; +import org.hipparchus.ode.nonstiff.DormandPrince54FieldIntegrator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.frames.FramesFactory; +import org.orekit.propagation.conversion.DormandPrince54FieldIntegratorBuilder; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldAbsolutePVCoordinates; + +class DormandPrince54IntegrationSettingsTest { + + @Test + void testFieldGetIntegratorBuilder() { + // GIVEN + final DormandPrince54IntegrationSettings integrationSettings = new DormandPrince54IntegrationSettings(1., 10., + 1e-6, 1e-9); + final ComplexField field = ComplexField.getInstance(); + // WHEN + final DormandPrince54FieldIntegratorBuilder builder = integrationSettings.getFieldIntegratorBuilder(field); + // THEN + final FieldODEIntegrator fieldIntegrator = builder.buildIntegrator(new FieldAbsolutePVCoordinates<>(FramesFactory.getGCRF(), + FieldAbsoluteDate.getArbitraryEpoch(field), new FieldVector3D<>(field, Vector3D.PLUS_I), + new FieldVector3D<>(field, Vector3D.MINUS_J))); + Assertions.assertInstanceOf(DormandPrince54FieldIntegrator.class, fieldIntegrator); + } + +} diff --git a/src/test/java/org/orekit/errors/OrekitMessagesTest.java b/src/test/java/org/orekit/errors/OrekitMessagesTest.java index ffa5eac8b5..26788c0309 100644 --- a/src/test/java/org/orekit/errors/OrekitMessagesTest.java +++ b/src/test/java/org/orekit/errors/OrekitMessagesTest.java @@ -17,22 +17,22 @@ package org.orekit.errors; -import org.hipparchus.exception.UTF8Control; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - import java.text.MessageFormat; import java.util.Enumeration; import java.util.Locale; import java.util.ResourceBundle; +import org.hipparchus.exception.UTF8Control; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + public class OrekitMessagesTest { private final String[] LANGUAGES_LIST = { "ca", "da", "de", "el", "en", "es", "fr", "gl", "it", "no", "ro" }; @Test public void testMessageNumber() { - Assertions.assertEquals(296, OrekitMessages.values().length); + Assertions.assertEquals(299, OrekitMessages.values().length); } @Test diff --git a/src/test/java/org/orekit/estimation/leastsquares/BrouwerLyddaneBatchLSEstimatorTest.java b/src/test/java/org/orekit/estimation/leastsquares/BrouwerLyddaneBatchLSEstimatorTest.java index c818b30926..7813de6bde 100644 --- a/src/test/java/org/orekit/estimation/leastsquares/BrouwerLyddaneBatchLSEstimatorTest.java +++ b/src/test/java/org/orekit/estimation/leastsquares/BrouwerLyddaneBatchLSEstimatorTest.java @@ -16,6 +16,9 @@ */ package org.orekit.estimation.leastsquares; +import java.util.ArrayList; +import java.util.List; + import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.linear.RealMatrix; import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem.Evaluation; @@ -32,8 +35,8 @@ import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.estimation.measurements.PVMeasurementCreator; import org.orekit.estimation.measurements.Range; -import org.orekit.estimation.measurements.TwoWayRangeMeasurementCreator; import org.orekit.estimation.measurements.RangeRateMeasurementCreator; +import org.orekit.estimation.measurements.TwoWayRangeMeasurementCreator; import org.orekit.estimation.measurements.modifiers.PhaseCentersRangeModifier; import org.orekit.frames.LOFType; import org.orekit.gnss.antenna.FrequencyPattern; @@ -43,9 +46,6 @@ import org.orekit.propagation.conversion.BrouwerLyddanePropagatorBuilder; import org.orekit.utils.ParameterDriversList; -import java.util.ArrayList; -import java.util.List; - public class BrouwerLyddaneBatchLSEstimatorTest { /** @@ -164,11 +164,11 @@ public void testKeplerRange() { estimator.setMaxIterations(10); estimator.setMaxEvaluations(20); - BrouwerLyddaneEstimationTestUtils.checkFit(context, estimator, 1, 2, - 0.0, 3.2e-2, - 0.0, 5.8e-2, - 0.0, 5e-3, - 0.0, 2.8e-6); + BrouwerLyddaneEstimationTestUtils.checkFit(context, estimator, 2, 5, + 0.0, 6.7e-2, + 0.0, 0.12, + 0.0, 9.9e-3, + 0.0, 1.5e-5); } @@ -210,11 +210,11 @@ public void testKeplerRangeWithOnBoardAntennaOffset() { estimator.setMaxIterations(10); estimator.setMaxEvaluations(20); - BrouwerLyddaneEstimationTestUtils.checkFit(context, estimator, 3, 4, - 0.0, 2.94e-2, - 0.0, 5.3e-2, - 0.0, 4.6e-3, - 0.0, 6.3e-6); + BrouwerLyddaneEstimationTestUtils.checkFit(context, estimator, 4, 5, + 0.0, 0.138, + 0.0, 0.248, + 0.0, 0.022, + 0.0, 3.0e-5); } @@ -255,11 +255,11 @@ public void testKeplerRangeRate() { estimator.setMaxIterations(10); estimator.setMaxEvaluations(20); - BrouwerLyddaneEstimationTestUtils.checkFit(context, estimator, 3,8, - 0.0, 4.9e-5, - 0.0, 1.2e-4, - 0.0, 3.2e-2, - 0.0, 4.4e-5); + BrouwerLyddaneEstimationTestUtils.checkFit(context, estimator, 2, 3, + 0.0, 5.1e-5, + 0.0, 8.0e-5, + 0.0, 1.6e-2, + 0.0, 1.9e-5); } @Test diff --git a/src/test/java/org/orekit/estimation/leastsquares/DSSTOrbitDeterminationTest.java b/src/test/java/org/orekit/estimation/leastsquares/DSSTOrbitDeterminationTest.java index 2c48206f9c..e9bf27e0d3 100644 --- a/src/test/java/org/orekit/estimation/leastsquares/DSSTOrbitDeterminationTest.java +++ b/src/test/java/org/orekit/estimation/leastsquares/DSSTOrbitDeterminationTest.java @@ -16,6 +16,14 @@ */ package org.orekit.estimation.leastsquares; +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.NoSuchElementException; + import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.hipparchus.exception.LocalizedCoreFormats; @@ -54,14 +62,6 @@ import org.orekit.utils.IERSConventions; import org.orekit.utils.ParameterDriver; -import java.io.File; -import java.io.IOException; -import java.net.URISyntaxException; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.List; -import java.util.NoSuchElementException; - public class DSSTOrbitDeterminationTest extends AbstractOrbitDetermination { /** Gravity field. */ @@ -238,8 +238,8 @@ public void testLageos2() //test //definition of the accuracy for the test - final double distanceAccuracy = 46.6; - final double velocityAccuracy = 1.13e-1; + final double distanceAccuracy = 46.51; + final double velocityAccuracy = 1.126e-1; //test on the convergence final int numberOfIte = 5; @@ -255,12 +255,14 @@ public void testLageos2() // Ref position from "lageos2_cpf_160213_5451.jax" final Vector3D refPos = new Vector3D(7526994.072, -9646309.832, 1464110.239); final Vector3D refVel = new Vector3D(3033.794, 1715.265, -4447.659); - Assertions.assertEquals(0.0, Vector3D.distance(refPos, estimatedPos), distanceAccuracy); - Assertions.assertEquals(0.0, Vector3D.distance(refVel, estimatedVel), velocityAccuracy); + final double dP = Vector3D.distance(refPos, estimatedPos); + final double dV = Vector3D.distance(refVel, estimatedVel); + Assertions.assertEquals(0.0, dP, distanceAccuracy); + Assertions.assertEquals(0.0, dV, velocityAccuracy); //test on statistic for the range residuals final long nbRange = 95; - final double[] RefStatRange = { -28.374, 58.620, 0.0, 14.877 }; + final double[] RefStatRange = { -28.316, 58.490, 0.0, 14.857 }; Assertions.assertEquals(nbRange, odLageos2.getRangeStat().getN()); MatcherAssert.assertThat(odLageos2.getRangeStat().getMin(), Matchers.greaterThan(RefStatRange[0])); @@ -268,8 +270,6 @@ public void testLageos2() Assertions.assertEquals(RefStatRange[1], odLageos2.getRangeStat().getMax(), 1.0e-3); Assertions.assertEquals(RefStatRange[2], odLageos2.getRangeStat().getMean(), 1.0e-3); Assertions.assertEquals(RefStatRange[3], odLageos2.getRangeStat().getStandardDeviation(), 1.0e-3); - - } /** @@ -313,7 +313,7 @@ public void testGNSS() //test //definition of the accuracy for the test - final double distanceAccuracy = 6.05; + final double distanceAccuracy = 6.052; final double velocityAccuracy = 2.48e-3; //test on the convergence @@ -333,13 +333,11 @@ public void testGNSS() //test on statistic for the range residuals final long nbRange = 4009; - final double[] RefStatRange = { -3.480, 2.609, 0.0, 0.836 }; + final double[] RefStatRange = { -3.480, 2.608, 0.0, 0.836 }; Assertions.assertEquals(nbRange, odGNSS.getRangeStat().getN()); Assertions.assertEquals(RefStatRange[0], odGNSS.getRangeStat().getMin(), 1.0e-3); Assertions.assertEquals(RefStatRange[1], odGNSS.getRangeStat().getMax(), 1.0e-3); Assertions.assertEquals(RefStatRange[2], odGNSS.getRangeStat().getMean(), 1.0e-3); Assertions.assertEquals(RefStatRange[3], odGNSS.getRangeStat().getStandardDeviation(), 1.0e-3); - } - } diff --git a/src/test/java/org/orekit/estimation/measurements/EstimatedMeasurementTest.java b/src/test/java/org/orekit/estimation/measurements/EstimatedMeasurementTest.java new file mode 100644 index 0000000000..ad9e31546e --- /dev/null +++ b/src/test/java/org/orekit/estimation/measurements/EstimatedMeasurementTest.java @@ -0,0 +1,36 @@ +package org.orekit.estimation.measurements; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.Mockito; +import org.orekit.propagation.SpacecraftState; +import org.orekit.utils.TimeStampedPVCoordinates; + +class EstimatedMeasurementTest { + + @ParameterizedTest + @EnumSource(EstimatedMeasurementBase.Status.class) + void testConstructor(final EstimatedMeasurementBase.Status expectedtStatus) { + // GIVEN + final Position mockedPosition = Mockito.mock(Position.class); + final int expectedIteration = 2; + final int expectedCount = 3; + final SpacecraftState[] states = new SpacecraftState[0]; + final double[] expectedEstimatedValue = new double[] {1., 2., 3.}; + final TimeStampedPVCoordinates[] timeStampedPVCoordinates = new TimeStampedPVCoordinates[0]; + final EstimatedMeasurementBase estimatedMeasurementBase = new EstimatedMeasurementBase<>(mockedPosition, + expectedIteration, expectedCount, states, timeStampedPVCoordinates); + estimatedMeasurementBase.setEstimatedValue(expectedEstimatedValue); + estimatedMeasurementBase.setStatus(expectedtStatus); + // WHEN + final EstimatedMeasurement estimatedMeasurement = new EstimatedMeasurement<>(estimatedMeasurementBase); + // THEN + Assertions.assertEquals(mockedPosition, estimatedMeasurement.getObservedMeasurement()); + Assertions.assertEquals(expectedCount, estimatedMeasurement.getCount()); + Assertions.assertEquals(expectedIteration, estimatedMeasurement.getIteration()); + Assertions.assertEquals(expectedtStatus, estimatedMeasurement.getStatus()); + Assertions.assertArrayEquals(expectedEstimatedValue, estimatedMeasurement.getEstimatedValue()); + } + +} diff --git a/src/test/java/org/orekit/estimation/sequential/BrouwerLyddaneKalmanEstimatorTest.java b/src/test/java/org/orekit/estimation/sequential/BrouwerLyddaneKalmanEstimatorTest.java index 9b63de1aa0..035d9e6454 100644 --- a/src/test/java/org/orekit/estimation/sequential/BrouwerLyddaneKalmanEstimatorTest.java +++ b/src/test/java/org/orekit/estimation/sequential/BrouwerLyddaneKalmanEstimatorTest.java @@ -81,12 +81,12 @@ public void testKeplerianPV() { // Filter the measurements and check the results final double expectedDeltaPos = 0.; - final double posEps = 2.70e-8; + final double posEps = 3.3e-8; final double expectedDeltaVel = 0.; - final double velEps = 6.59e-11; - final double[] expectedsigmasPos = {0.998881, 0.933800, 0.997357}; + final double velEps = 8.8e-11; + final double[] expectedsigmasPos = {0.998881, 0.933806, 0.997357}; final double sigmaPosEps = 1e-6; - final double[] expectedSigmasVel = {9.475737e-4, 9.904671e-4, 5.060183e-4}; + final double[] expectedSigmasVel = {9.475735e-4, 9.904680e-4, 5.060067e-4}; final double sigmaVelEps = 1e-10; BrouwerLyddaneEstimationTestUtils.checkKalmanFit(context, kalman, measurements, refOrbit, positionAngleType, diff --git a/src/test/java/org/orekit/estimation/sequential/KalmanEstimatorTest.java b/src/test/java/org/orekit/estimation/sequential/KalmanEstimatorTest.java index 8aa9a01d0f..24d84a79ee 100644 --- a/src/test/java/org/orekit/estimation/sequential/KalmanEstimatorTest.java +++ b/src/test/java/org/orekit/estimation/sequential/KalmanEstimatorTest.java @@ -31,6 +31,7 @@ import org.orekit.errors.OrekitMessages; import org.orekit.estimation.Context; import org.orekit.estimation.EstimationTestUtils; +import org.orekit.estimation.TLEEstimationTestUtils; import org.orekit.estimation.measurements.AngularAzElMeasurementCreator; import org.orekit.estimation.measurements.AngularRaDecMeasurementCreator; import org.orekit.estimation.measurements.InterSatellitesRangeMeasurementCreator; @@ -54,7 +55,11 @@ import org.orekit.propagation.BoundedPropagator; import org.orekit.propagation.EphemerisGenerator; import org.orekit.propagation.Propagator; +import org.orekit.propagation.analytical.tle.TLE; +import org.orekit.propagation.analytical.tle.TLEPropagator; +import org.orekit.propagation.analytical.tle.generation.FixedPointTleGenerationAlgorithm; import org.orekit.propagation.conversion.NumericalPropagatorBuilder; +import org.orekit.propagation.conversion.TLEPropagatorBuilder; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterDriversList; @@ -63,6 +68,31 @@ public class KalmanEstimatorTest { + @Test + void testEstimationStepWithBStarOnly() { + // GIVEN + TLEEstimationTestUtils.eccentricContext("regular-data:potential:tides"); + String line1 = "1 07276U 74026A 00055.48318287 .00000000 00000-0 22970+3 0 9994"; + String line2 = "2 07276 71.6273 78.7838 1248323 14.0598 3.8405 4.72707036231812"; + final TLE tle = new TLE(line1, line2); + final TLEPropagatorBuilder propagatorBuilder = new TLEPropagatorBuilder(tle, + PositionAngleType.TRUE, 1., new FixedPointTleGenerationAlgorithm()); + for (final ParameterDriver driver: propagatorBuilder.getOrbitalParametersDrivers().getDrivers()) { + driver.setSelected(false); + } + propagatorBuilder.getPropagationParametersDrivers().getDrivers().get(0).setSelected(true); + final KalmanEstimatorBuilder builder = new KalmanEstimatorBuilder(); + builder.addPropagationConfiguration(propagatorBuilder, + new ConstantProcessNoise(MatrixUtils.createRealMatrix(1, 1))); + final KalmanEstimator estimator = builder.build(); + final AbsoluteDate measurementDate = tle.getDate().shiftedBy(1.0); + final TLEPropagator propagator = TLEPropagator.selectExtrapolator(tle); + final Position positionMeasurement = new Position(measurementDate, propagator.getPosition(measurementDate, + propagator.getFrame()), 1., 1., new ObservableSatellite(0)); + // WHEN & THEN + Assertions.assertDoesNotThrow(() -> estimator.estimationStep(positionMeasurement)); + } + @Test public void testMissingPropagatorBuilder() { try { diff --git a/src/test/java/org/orekit/estimation/sequential/SequentialNumericalOrbitDeterminationTest.java b/src/test/java/org/orekit/estimation/sequential/SequentialNumericalOrbitDeterminationTest.java index 346c50d281..6fcdfcf8b4 100644 --- a/src/test/java/org/orekit/estimation/sequential/SequentialNumericalOrbitDeterminationTest.java +++ b/src/test/java/org/orekit/estimation/sequential/SequentialNumericalOrbitDeterminationTest.java @@ -422,76 +422,6 @@ public void testW3BExtended() throws URISyntaxException, IOException { predictionDistanceAccuracy, predictionVelocityAccuracy, false, false); } - @Test - public void testW3BUnscented() throws URISyntaxException, IOException { - // Batch LS result: -0.2154; - final double dragCoef = -0.0214; - - // Batch LS results: 8.002e-6 - final double leakAccelerationNorm0 = 5.954e-6; - - // Batch LS results: 3.058e-10 - final double leakAccelerationNorm1 = 1.619e-10; - - // Batch LS results - // final double[] CastleAzElBias = { 0.062701342, -0.003613508 }; - // final double CastleRangeBias = 11274.4677; - final double[] castleAzElBias = { 0.062344, -0.004106}; - final double castleRangeBias = 11333.0998; - - // Batch LS results - // final double[] FucAzElBias = { -0.053526137, 0.075483886 }; - // final double FucRangeBias = 13467.8256; - final double[] fucAzElBias = { -0.053870, 0.075641 }; - final double fucRangeBias = 13461.7291; - - // Batch LS results - // final double[] KumAzElBias = { -0.023574208, -0.054520756 }; - // final double KumRangeBias = 13512.57594; - final double[] kumAzElBias = { -0.023393, -0.055078 }; - final double kumRangeBias = 13515.6244; - - // Batch LS results - // final double[] PreAzElBias = { 0.030201539, 0.009747877 }; - // final double PreRangeBias = 13594.11889; - final double[] preAzElBias = { 0.030250, 0.010083 }; - final double preRangeBias = 13534.0334; - - // Batch LS results - // final double[] UraAzElBias = { 0.167814449, -0.12305252 }; - // final double UraRangeBias = 13450.26738; - final double[] uraAzElBias = { 0.167700, -0.122408 }; - final double uraRangeBias = 13417.6695; - - //statistics for the range residual (min, max, mean, std) - final double[] refStatRange = { -144.9733, 14.7486, -3.8990, 11.9050 }; - - //statistics for the azimuth residual (min, max, mean, std) - final double[] refStatAzi = { -0.041873, 0.018087, -0.004536, 0.008995 }; - - //statistics for the elevation residual (min, max, mean, std) - final double[] refStatEle = { -0.025583, 0.043560, 0.001857, 0.010625 }; - - // Expected covariance - final double dragVariance = 0.018813; - final double leakXVariance = 2.117E-13; - final double leakYVariance = 5.540E-13; - final double leakZVariance = 1.73244E-11; - - // Prediction position/velocity accuracies - // FIXME: debug - Comparison with batch LS is bad - final double predictionDistanceAccuracy = 285.31; - final double predictionVelocityAccuracy = 0.101; - - testW3B(dragCoef, leakAccelerationNorm0, leakAccelerationNorm1, - castleAzElBias, castleRangeBias, fucAzElBias, fucRangeBias, kumAzElBias, kumRangeBias, - preAzElBias, preRangeBias, uraAzElBias, uraRangeBias, - refStatRange, refStatAzi, refStatEle, dragVariance, - leakXVariance, leakYVariance, leakZVariance, - predictionDistanceAccuracy, predictionVelocityAccuracy, false, true); - } - - // Orbit determination for range, azimuth elevation measurements protected void testW3B(final double dragCoef, final double leakAccelerationNorm0, final double leakAccelerationNorm1, final double[] castleAzElBias, final double castleRangeBias, diff --git a/src/test/java/org/orekit/estimation/sequential/UnscentedSemiAnalyticalKalmanOrbitDeterminationTest.java b/src/test/java/org/orekit/estimation/sequential/UnscentedSemiAnalyticalKalmanOrbitDeterminationTest.java index 801d9797d1..aad3776e6c 100644 --- a/src/test/java/org/orekit/estimation/sequential/UnscentedSemiAnalyticalKalmanOrbitDeterminationTest.java +++ b/src/test/java/org/orekit/estimation/sequential/UnscentedSemiAnalyticalKalmanOrbitDeterminationTest.java @@ -49,8 +49,8 @@ import org.orekit.files.ilrs.CPF; import org.orekit.files.ilrs.CPF.CPFCoordinate; import org.orekit.files.ilrs.CPF.CPFEphemeris; -import org.orekit.files.rinex.HatanakaCompressFilter; import org.orekit.files.ilrs.CPFParser; +import org.orekit.files.rinex.HatanakaCompressFilter; import org.orekit.forces.drag.DragForce; import org.orekit.forces.drag.DragSensitive; import org.orekit.forces.drag.IsotropicDrag; @@ -165,8 +165,8 @@ public void testLageos() throws URISyntaxException, IOException { final StreamingStatistics statX = observer.getXStatistics(); final StreamingStatistics statY = observer.getYStatistics(); final StreamingStatistics statZ = observer.getZStatistics(); - Assertions.assertEquals(0.0, statX.getMean(), 1.37e-4); - Assertions.assertEquals(0.0, statY.getMean(), 4.93e-4); + Assertions.assertEquals(0.0, statX.getMean(), 1.365e-4); + Assertions.assertEquals(0.0, statY.getMean(), 4.931e-4); Assertions.assertEquals(0.0, statZ.getMean(), 3.80e-4); Assertions.assertEquals(0.0, statX.getMin(), 0.027); // Value is negative Assertions.assertEquals(0.0, statY.getMin(), 0.028); // Value is negative diff --git a/src/test/java/org/orekit/files/rinex/navigation/NavigationFileParserTest.java b/src/test/java/org/orekit/files/rinex/navigation/NavigationFileParserTest.java index 20827135aa..bbf2c55a2b 100644 --- a/src/test/java/org/orekit/files/rinex/navigation/NavigationFileParserTest.java +++ b/src/test/java/org/orekit/files/rinex/navigation/NavigationFileParserTest.java @@ -21,7 +21,6 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.net.URISyntaxException; import java.util.Arrays; import java.util.List; @@ -92,7 +91,7 @@ public void testWorkAroundWrongFormatNumber() throws IOException { } @Test - public void testGpsRinex301Truncated() throws URISyntaxException, IOException { + public void testGpsRinex301Truncated() throws IOException { // Parse file final String ex = "/gnss/navigation/Example_GPS_Rinex301.n"; @@ -107,7 +106,7 @@ public void testGpsRinex301Truncated() throws URISyntaxException, IOException { } @Test - public void testGpsRinex301() throws URISyntaxException, IOException { + public void testGpsRinex301() throws IOException { // Parse file final String ex = "/gnss/navigation/Example_GPS_Rinex301.n"; @@ -195,7 +194,7 @@ public void testGpsRinex301() throws URISyntaxException, IOException { } @Test - public void testGpsRinex400() throws URISyntaxException, IOException { + public void testGpsRinex400() throws IOException { // Parse file final String ex = "/gnss/navigation/Example_GPS_Rinex400.n"; @@ -253,7 +252,7 @@ public void testGpsRinex400() throws URISyntaxException, IOException { } @Test - public void testSBASRinex301() throws URISyntaxException, IOException { + public void testSBASRinex301() throws IOException { // Parse file final String ex = "/gnss/navigation/Example_SBAS_Rinex301.n"; @@ -320,7 +319,7 @@ public void testSBASRinex301() throws URISyntaxException, IOException { } @Test - public void testBeidouRinex302() throws URISyntaxException, IOException { + public void testBeidouRinex302() throws IOException { final String ex = "/gnss/navigation/Example_Beidou_Rinex302.n"; final RinexNavigation file = new RinexNavigationParser(). @@ -390,7 +389,7 @@ public void testBeidouRinex302() throws URISyntaxException, IOException { } @Test - public void testBeidouRinex400() throws URISyntaxException, IOException { + public void testBeidouRinex400() throws IOException { // Parse file final String ex = "/gnss/navigation/Example_Beidou_Rinex400.n"; @@ -479,7 +478,7 @@ public void testBeidouRinex400() throws URISyntaxException, IOException { } @Test - public void testGalileoRinex302() throws URISyntaxException, IOException { + public void testGalileoRinex302() throws IOException { // Parse file final String ex = "/gnss/navigation/Example_Galileo_Rinex302.n"; @@ -565,7 +564,7 @@ public void testGalileoRinex302() throws URISyntaxException, IOException { } @Test - public void testGalileoRinex400() throws URISyntaxException, IOException { + public void testGalileoRinex400() throws IOException { // Parse file final String ex = "/gnss/navigation/Example_Galileo_Rinex400.n"; @@ -603,7 +602,7 @@ public void testGalileoRinex400() throws URISyntaxException, IOException { } @Test - public void testQZSSRinex302() throws URISyntaxException, IOException { + public void testQZSSRinex302() throws IOException { final String ex = "/gnss/navigation/Example_QZSS_Rinex302.n"; final RinexNavigation file = new RinexNavigationParser(). @@ -692,7 +691,7 @@ public void testQZSSRinex302() throws URISyntaxException, IOException { } @Test - public void testQZSSRinex400() throws URISyntaxException, IOException { + public void testQZSSRinex400() throws IOException { // Parse file final String ex = "/gnss/navigation/Example_QZSS_Rinex400.n"; @@ -745,7 +744,7 @@ public void testQZSSRinex400() throws URISyntaxException, IOException { } @Test - public void testGLONASSRinex303() throws URISyntaxException, IOException { + public void testGLONASSRinex303() throws IOException { // Parse file final String ex = "/gnss/navigation/Example_Glonass_Rinex303.n"; @@ -806,7 +805,7 @@ public void testGLONASSRinex303() throws URISyntaxException, IOException { } @Test - public void testIRNSSRinex303() throws URISyntaxException, IOException { + public void testIRNSSRinex303() throws IOException { final String ex = "/gnss/navigation/Example_IRNSS_Rinex303.n"; final RinexNavigation file = new RinexNavigationParser(). @@ -876,7 +875,7 @@ public void testIRNSSRinex303() throws URISyntaxException, IOException { } @Test - public void testIRNSSRinex400() throws URISyntaxException, IOException { + public void testIRNSSRinex400() throws IOException { // Parse file final String ex = "/gnss/navigation/Example_IRNSS_Rinex400.n"; @@ -912,7 +911,7 @@ public void testIRNSSRinex400() throws URISyntaxException, IOException { } @Test - public void testMixedRinex304() throws URISyntaxException, IOException { + public void testMixedRinex304() throws IOException { // Parse file final String ex = "/gnss/navigation/Example_Mixed_Rinex304.n"; @@ -973,7 +972,7 @@ public void testMixedRinex304() throws URISyntaxException, IOException { } @Test - public void testMixedRinex305() throws URISyntaxException, IOException { + public void testMixedRinex305() throws IOException { // Parse file final String ex = "/gnss/navigation/Example_Mixed_Rinex305.n"; @@ -1033,7 +1032,7 @@ public void testMixedRinex305() throws URISyntaxException, IOException { } @Test - public void testQZSSRinex304() throws URISyntaxException, IOException { + public void testQZSSRinex304() throws IOException { final String ex = "/gnss/navigation/Example_QZSS_Rinex304.n"; final RinexNavigation file = new RinexNavigationParser(). @@ -1104,7 +1103,7 @@ public void testQZSSRinex304() throws URISyntaxException, IOException { } @Test - public void testGpsRinex304() throws URISyntaxException, IOException { + public void testGpsRinex304() throws IOException { // Parse file final String ex = "/gnss/navigation/Example_GPS_Rinex304.n"; @@ -1194,7 +1193,7 @@ public void testGpsRinex304() throws URISyntaxException, IOException { } @Test - public void testGalileoRinex304() throws URISyntaxException, IOException { + public void testGalileoRinex304() throws IOException { // Parse file final String ex = "/gnss/navigation/Example_Galileo_Rinex304.n"; @@ -1273,7 +1272,7 @@ public void testGalileoRinex304() throws URISyntaxException, IOException { } @Test - public void testSBASRinex304() throws URISyntaxException, IOException { + public void testSBASRinex304() throws IOException { // Parse file final String ex = "/gnss/navigation/Example_SBAS_Rinex304.n"; @@ -1340,7 +1339,7 @@ public void testSBASRinex304() throws URISyntaxException, IOException { } @Test - public void testSBASRinex400() throws URISyntaxException, IOException { + public void testSBASRinex400() throws IOException { // Parse file final String ex = "/gnss/navigation/Example_SBAS_Rinex400.n"; @@ -1377,7 +1376,7 @@ public void testSBASRinex400() throws URISyntaxException, IOException { } @Test - public void testIRNSSRinex304() throws URISyntaxException, IOException { + public void testIRNSSRinex304() throws IOException { final String ex = "/gnss/navigation/Example_IRNSS_Rinex304.n"; final RinexNavigation file = new RinexNavigationParser(). @@ -1447,7 +1446,7 @@ public void testIRNSSRinex304() throws URISyntaxException, IOException { } @Test - public void testBeidouRinex304() throws URISyntaxException, IOException { + public void testBeidouRinex304() throws IOException { final String ex = "/gnss/navigation/Example_Beidou_Rinex304.n"; final RinexNavigation file = new RinexNavigationParser(). @@ -1517,7 +1516,7 @@ public void testBeidouRinex304() throws URISyntaxException, IOException { } @Test - public void testStoRinex400() throws URISyntaxException, IOException { + public void testStoRinex400() throws IOException { // Parse file final String ex = "/gnss/navigation/Example_Sto_Rinex400.n"; @@ -1575,7 +1574,13 @@ public void testStoRinex400() throws URISyntaxException, IOException { Assertions.assertEquals(TimeSystem.BEIDOU, list.get( 2).getDefinedTimeSystem()); Assertions.assertEquals(TimeSystem.GPS, list.get( 2).getReferenceTimeSystem()); Assertions.assertEquals(TimeSystem.BEIDOU, list.get( 3).getDefinedTimeSystem()); + Assertions.assertEquals("BDT", list.get( 3).getDefinedTimeSystem().getKey()); + Assertions.assertEquals("BD", list.get( 3).getDefinedTimeSystem().getTwoLettersCode()); + Assertions.assertEquals("C", list.get( 3).getDefinedTimeSystem().getOneLetterCode()); Assertions.assertEquals(TimeSystem.UTC, list.get( 3).getReferenceTimeSystem()); + Assertions.assertEquals("UTC", list.get( 3).getReferenceTimeSystem().getKey()); + Assertions.assertEquals("UT", list.get( 3).getReferenceTimeSystem().getTwoLettersCode()); + Assertions.assertNull( list.get( 3).getReferenceTimeSystem().getOneLetterCode()); Assertions.assertEquals(UtcId.NTSC, list.get( 3).getUtcId()); Assertions.assertEquals(TimeSystem.GALILEO, list.get( 4).getDefinedTimeSystem()); Assertions.assertEquals(TimeSystem.GPS, list.get( 4).getReferenceTimeSystem()); @@ -1612,7 +1617,7 @@ public void testStoRinex400() throws URISyntaxException, IOException { } @Test - public void testEopRinex400() throws URISyntaxException, IOException { + public void testEopRinex400() throws IOException { // Parse file final String ex = "/gnss/navigation/Example_Eop_Rinex400.n"; @@ -1704,7 +1709,7 @@ public void testEopRinex400() throws URISyntaxException, IOException { } @Test - public void testIonRinex400() throws URISyntaxException, IOException { + public void testIonRinex400() throws IOException { // Parse file final String ex = "/gnss/navigation/Example_Ion_Rinex400.n"; @@ -1804,7 +1809,7 @@ public void testIonRinex400() throws URISyntaxException, IOException { } @Test - public void testGPSRinex2() throws URISyntaxException, IOException { + public void testGPSRinex2() throws IOException { // Parse file final String ex = "/gnss/navigation/brdc0130.22n"; @@ -1862,7 +1867,7 @@ public void testGPSRinex2() throws URISyntaxException, IOException { } @Test - public void testGlonassRinex2() throws URISyntaxException, IOException { + public void testGlonassRinex2() throws IOException { // Parse file final String ex = "/gnss/navigation/brdc0130.22g"; @@ -1940,7 +1945,7 @@ public void testUnknownRinexVersion() throws IOException { Assertions.fail("an exception should have been thrown"); } catch (OrekitException oe) { Assertions.assertEquals(OrekitMessages.UNSUPPORTED_FILE_FORMAT_VERSION, oe.getSpecifier()); - Assertions.assertEquals(9.99, ((Double) oe.getParts()[0]).doubleValue(), 1.0e-10); + Assertions.assertEquals(9.99, (Double) oe.getParts()[0], 1.0e-10); Assertions.assertEquals(ex, oe.getParts()[1]); } } @@ -1999,7 +2004,7 @@ public void testDefensiveProgrammingExceptions() { m.invoke(sbasParserField.get(null), "", parseInfo); Assertions.fail("an exception should have been thrown"); } catch (InvocationTargetException e) { - Assertions.assertTrue(e.getCause() instanceof OrekitInternalError); + Assertions.assertInstanceOf(OrekitInternalError.class, e.getCause()); } } diff --git a/src/test/java/org/orekit/files/rinex/observation/RinexObservationWriterTest.java b/src/test/java/org/orekit/files/rinex/observation/RinexObservationWriterTest.java index 63a2de1fc5..6a66695e85 100644 --- a/src/test/java/org/orekit/files/rinex/observation/RinexObservationWriterTest.java +++ b/src/test/java/org/orekit/files/rinex/observation/RinexObservationWriterTest.java @@ -65,6 +65,23 @@ public void testWriteHeaderTwice() throws IOException { } } + @Test + public void testTooLongAgency() throws IOException { + final RinexObservation robs = load("rinex/bbbb0000.00o"); + robs.getHeader().setAgencyName("much too long agency name exceeding 40 characters"); + final CharArrayWriter caw = new CharArrayWriter(); + try (RinexObservationWriter writer = new RinexObservationWriter(caw, "dummy")) { + writer.setReceiverClockModel(robs.extractClockModel(2)); + writer.prepareComments(robs.getComments()); + writer.writeHeader(robs.getHeader()); + Assertions.fail("an exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.FIELD_TOO_LONG, oe.getSpecifier()); + Assertions.assertEquals("much too long agency name exceeding 40 characters", oe.getParts()[0]); + Assertions.assertEquals(40, (Integer) oe.getParts()[1]); + } + } + @Test public void testNoWriteHeader() throws IOException { final RinexObservation robs = load("rinex/aiub0000.00o"); diff --git a/src/test/java/org/orekit/files/sp3/SP3CoordinateHermiteInterpolatorTest.java b/src/test/java/org/orekit/files/sp3/SP3CoordinateHermiteInterpolatorTest.java index 262e3bc965..932e220c1a 100644 --- a/src/test/java/org/orekit/files/sp3/SP3CoordinateHermiteInterpolatorTest.java +++ b/src/test/java/org/orekit/files/sp3/SP3CoordinateHermiteInterpolatorTest.java @@ -16,8 +16,6 @@ */ package org.orekit.files.sp3; -import java.io.IOException; -import java.net.URISyntaxException; import java.util.List; import org.hipparchus.geometry.euclidean.threed.Vector3D; @@ -32,7 +30,7 @@ public class SP3CoordinateHermiteInterpolatorTest { @Test - public void testNoRates() throws IOException, URISyntaxException { + public void testNoRates() { final String ex = "/sp3/gbm18432.sp3.Z"; final SP3Parser parser = new SP3Parser(); @@ -79,7 +77,7 @@ public void testNoRates() throws IOException, URISyntaxException { } @Test - public void testRates() throws IOException, URISyntaxException { + public void testRates() { final String ex = "/sp3/issue895-minutes-increment.sp3"; final SP3Parser parser = new SP3Parser(); @@ -100,9 +98,7 @@ public void testRates() throws IOException, URISyntaxException { SP3TestUtils.checkEquals(pointA, interpolated); Assertions.assertEquals(5996.687746, interpolated.getVelocity().getNorm(), 1.0e-6); - Assertions.assertEquals(SP3Utils.DEFAULT_CLOCK_RATE_VALUE, - SP3Utils.CLOCK_RATE_UNIT.fromSI(interpolated.getClockRateChange()), - 1.0e-6); + Assertions.assertTrue(Double.isNaN(interpolated.getClockRateChange())); // check between sample points SP3Coordinate pointB = coordinates.get(55); diff --git a/src/test/java/org/orekit/files/sp3/SP3ParserTest.java b/src/test/java/org/orekit/files/sp3/SP3ParserTest.java index f663624b05..b14627cdd1 100644 --- a/src/test/java/org/orekit/files/sp3/SP3ParserTest.java +++ b/src/test/java/org/orekit/files/sp3/SP3ParserTest.java @@ -19,6 +19,7 @@ import java.util.Arrays; import java.util.List; +import org.hipparchus.analysis.polynomials.PolynomialFunction; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -684,8 +685,8 @@ public void testIssue803() { checkPVEntry(new PVCoordinates(new Vector3D(2228470.946, 7268265.924, 9581471.543), new Vector3D(-4485.6945000, 2432.1151000, -711.6222800)), coord); - Assertions.assertEquals(0.999999999999, coord.getClockCorrection(), 1.0e-12); - Assertions.assertEquals(9.99999999999e-5, coord.getClockRateChange(), 1.0e-16); + Assertions.assertTrue(Double.isNaN(coord.getClockCorrection())); + Assertions.assertTrue(Double.isNaN(coord.getClockRateChange())); } @@ -708,7 +709,7 @@ public void testIssue827() { // 2016 2 28 0 0 0.00000000 Assertions.assertEquals(new AbsoluteDate(2016, 2, 28, 0, 0, 0, - TimeScalesFactory.getUTC()), coord.getDate()); + TimeScalesFactory.getUTC()), coord.getDate()); // PL52 2228.470946 7268.265924 9581.471543 @@ -716,8 +717,8 @@ public void testIssue827() { checkPVEntry(new PVCoordinates(new Vector3D(2228470.946, 7268265.924, 9581471.543), new Vector3D(-4485.6945000, 2432.1151000, -711.6222800)), coord); - Assertions.assertEquals(0.999999999999, coord.getClockCorrection(), 1.0e-12); - Assertions.assertEquals(9.99999999999e-5, coord.getClockRateChange(), 1.0e-16); + Assertions.assertTrue(Double.isNaN(coord.getClockCorrection())); + Assertions.assertTrue(Double.isNaN(coord.getClockRateChange())); } @@ -1348,6 +1349,43 @@ public void testSpliceNoDrop() { Assertions.assertEquals("Z00", oe.getParts()[0]); } + Assertions.assertEquals(5, + SP3Utils.indexAccuracy(SP3Utils.POSITION_ACCURACY_UNIT, SP3Utils.POS_VEL_BASE_ACCURACY, + spliced.getHeader().getAccuracy("R23"))); + + } + + @Test + public void testIssue1552() { + final String ex = "/sp3/three-hours.sp3"; + final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); + final SP3 sp3 = new SP3Parser().parse(source); + + final AbsoluteDate start = new AbsoluteDate(2015, 5, 5, 0, 0, 0.0, TimeScalesFactory.getGPS()); + final AbsoluteDate end = new AbsoluteDate(2015, 5, 5, 2, 55, 0.0, TimeScalesFactory.getGPS()); + + checkPolynomialClock(sp3.getEphemeris("C01").extractClockModel(), start, end, + new PolynomialFunction(-4.34415678e-4, 3.15154873e-11, -2.19219167e-17), + 2.2e-10); + checkPolynomialClock(sp3.getEphemeris("C02").extractClockModel(), start, end, + new PolynomialFunction(-9.16586434e-4, 2.20479164e-11, 2.96650589e-17), + 2.8e-10); + + } + + private void checkPolynomialClock(final AggregatedClockModel clockModel, + final AbsoluteDate start, final AbsoluteDate end, + final PolynomialFunction polynomialFunction, final double tolerance) { + + Assertions.assertEquals(start, clockModel.getValidityStart()); + Assertions.assertEquals(end, clockModel.getValidityEnd()); + + for (double dt = 0; dt < end.durationFrom(start); dt += 1) { + final AbsoluteDate date = start.shiftedBy(dt); + final double offset = clockModel.getOffset(date).getOffset(); + Assertions.assertEquals(polynomialFunction.value(dt), offset, tolerance); + } + } private SP3 splice(final String name1, final String name2) { diff --git a/src/test/java/org/orekit/forces/gravity/AbstractBodyAttractionTest.java b/src/test/java/org/orekit/forces/gravity/AbstractBodyAttractionTest.java index 7500fd76b5..70bce09c8f 100644 --- a/src/test/java/org/orekit/forces/gravity/AbstractBodyAttractionTest.java +++ b/src/test/java/org/orekit/forces/gravity/AbstractBodyAttractionTest.java @@ -23,8 +23,13 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.orekit.bodies.CelestialBody; +import org.orekit.frames.Frame; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; class AbstractBodyAttractionTest { @@ -39,6 +44,18 @@ void testDependsOnPositionOnly() { Assertions.assertTrue(actualDependsOnPositionOnly); } + @Deprecated + @Test + void TestGetBody() { + // GIVEN + final CelestialBody mockedBody = Mockito.mock(CelestialBody.class); + final TestBodyAttraction testBodyAttraction = new TestBodyAttraction(mockedBody); + // WHEN + final CelestialBody actualName = testBodyAttraction.getBody(); + // THEN + Assertions.assertEquals(mockedBody, actualName); + } + @Test void TestGetBodyName() { // GIVEN @@ -52,6 +69,51 @@ void TestGetBodyName() { Assertions.assertEquals(expectedName, actualName); } + @Test + void testGetPV() { + // GIVEN + final CelestialBody mockedBody = Mockito.mock(CelestialBody.class); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Frame mockedFrame = Mockito.mock(Frame.class); + final TimeStampedPVCoordinates expectedPV = Mockito.mock(TimeStampedPVCoordinates.class); + Mockito.when(mockedBody.getPVCoordinates(date, mockedFrame)).thenReturn(expectedPV); + final TestBodyAttraction testBodyAttraction = new TestBodyAttraction(mockedBody); + // WHEN + final PVCoordinates actualPV = testBodyAttraction.getBodyPVCoordinates(date, mockedFrame); + // THEN + Assertions.assertEquals(expectedPV, actualPV); + } + + @Test + void testGetPosition() { + // GIVEN + final CelestialBody mockedBody = Mockito.mock(CelestialBody.class); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Frame mockedFrame = Mockito.mock(Frame.class); + final Vector3D expectedPosition = new Vector3D(1, 2, 3); + Mockito.when(mockedBody.getPosition(date, mockedFrame)).thenReturn(expectedPosition); + final TestBodyAttraction testBodyAttraction = new TestBodyAttraction(mockedBody); + // WHEN + final Vector3D actualPosition = testBodyAttraction.getBodyPosition(date, mockedFrame); + // THEN + Assertions.assertEquals(expectedPosition, actualPosition); + } + + @Test + void testGetFieldPosition() { + // GIVEN + final CelestialBody mockedBody = Mockito.mock(CelestialBody.class); + final FieldAbsoluteDate mockedDate = Mockito.mock(FieldAbsoluteDate.class); + final Frame mockedFrame = Mockito.mock(Frame.class); + final FieldVector3D expectedPosition = Mockito.mock(FieldVector3D.class); + Mockito.when(mockedBody.getPosition(mockedDate, mockedFrame)).thenReturn(expectedPosition); + final TestBodyAttraction testBodyAttraction = new TestBodyAttraction(mockedBody); + // WHEN + final FieldVector3D actualPosition = testBodyAttraction.getBodyPosition(mockedDate, mockedFrame); + // THEN + Assertions.assertEquals(expectedPosition, actualPosition); + } + private static class TestBodyAttraction extends AbstractBodyAttraction { protected TestBodyAttraction(CelestialBody body) { diff --git a/src/test/java/org/orekit/forces/gravity/potential/SHAFormatReaderTest.java b/src/test/java/org/orekit/forces/gravity/potential/SHAFormatReaderTest.java new file mode 100644 index 0000000000..120d305931 --- /dev/null +++ b/src/test/java/org/orekit/forces/gravity/potential/SHAFormatReaderTest.java @@ -0,0 +1,117 @@ +/* Contributed in the public domain. + * 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.forces.gravity.potential; + +import org.hipparchus.util.FastMath; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +import org.orekit.errors.OrekitException; +import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider.NormalizedSphericalHarmonics; +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics; +import org.orekit.time.AbsoluteDate; + +public class SHAFormatReaderTest { + + @Test + public void testReadNormalized() { + Utils.setDataRoot("potential"); + GravityFieldFactory.addPotentialCoefficientsReader(new SHAFormatReader("sha.grgm1200b_sigma_truncated_5x5", true)); + NormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getNormalizedProvider(5, 5); + NormalizedSphericalHarmonics harmonics = provider.onDate(AbsoluteDate.FUTURE_INFINITY); + Assertions.assertEquals(TideSystem.UNKNOWN, provider.getTideSystem()); + Assertions.assertEquals(-3.1973502105869101E-06, harmonics.getNormalizedCnm(3, 0), 1.0e-15); + Assertions.assertEquals( 3.1105527966439498E-06, harmonics.getNormalizedCnm(5, 5), 1.0e-15); + Assertions.assertEquals( 0.0, harmonics.getNormalizedSnm(4, 0), 1.0e-15); + Assertions.assertEquals( 3.9263792903879803E-06, harmonics.getNormalizedSnm(4, 4), 1.0e-15); + Assertions.assertEquals(2.7542657233402899E-06, harmonics.getNormalizedCnm(5, 4), 1.0e-15); + } + + @Test + public void testReadUnnormalized() { + Utils.setDataRoot("potential"); + GravityFieldFactory.addPotentialCoefficientsReader(new SHAFormatReader("sha.grgm1200b_sigma_truncated_5x5", true)); + UnnormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getUnnormalizedProvider(5, 5); + UnnormalizedSphericalHarmonics harmonics = provider.onDate(AbsoluteDate.FUTURE_INFINITY); + Assertions.assertEquals(TideSystem.UNKNOWN, provider.getTideSystem()); + int maxUlps = 1; + checkValue(harmonics.getUnnormalizedCnm(3, 0), 3, 0, -3.1973502105869101E-06, maxUlps); + checkValue(harmonics.getUnnormalizedCnm(5, 5), 5, 5, 3.1105527966439498E-06, maxUlps); + checkValue(harmonics.getUnnormalizedSnm(4, 0), 4, 0, 0.0, maxUlps); + checkValue(harmonics.getUnnormalizedSnm(4, 4), 4, 4, 3.9263792903879803E-06, maxUlps); + + double a = (2.7542657233402899E-06); + double b = 9*8*7*6*5*4*3*2; + double c = 2*11/b; + double result = a*FastMath.sqrt(c); + + Assertions.assertEquals(result, harmonics.getUnnormalizedCnm(5, 4), 1.0e-20); + + a = -6.0069538669876603E-06; + b = 8*7*6*5*4*3*2; + c=2*9/b; + result = a*FastMath.sqrt(c); + Assertions.assertEquals(result, harmonics.getUnnormalizedCnm(4, 4), 1.0e-20); + + Assertions.assertEquals(2.0321922328195912e-4, -harmonics.getUnnormalizedCnm(2, 0), 1.0e-20); + } + + @Test + public void testCorruptedFile1() { + Assertions.assertThrows(OrekitException.class, () -> { + Utils.setDataRoot("potential"); + GravityFieldFactory.addPotentialCoefficientsReader(new SHAFormatReader("corrupted-1_sha.grgm1200b_sigma_truncated_5x5", false)); + GravityFieldFactory.getUnnormalizedProvider(5, 5); + }); + } + + @Test + public void testCorruptedFile2() { + Assertions.assertThrows(OrekitException.class, () -> { + Utils.setDataRoot("potential"); + GravityFieldFactory.addPotentialCoefficientsReader(new SHAFormatReader("corrupted-2_sha.grgm1200b_sigma_truncated_5x5", false)); + GravityFieldFactory.getUnnormalizedProvider(5, 5); + }); + } + + @Test + public void testCorruptedFile3() { + Assertions.assertThrows(OrekitException.class, () -> { + Utils.setDataRoot("potential"); + GravityFieldFactory.addPotentialCoefficientsReader(new SHAFormatReader("corrupted-3_sha.grgm1200b_sigma_truncated_5x5", false)); + GravityFieldFactory.getUnnormalizedProvider(5, 5); + }); + } + + @Test + public void testCannotParseHeader() { + Assertions.assertThrows(OrekitException.class, () -> { + Utils.setDataRoot("potential"); + GravityFieldFactory.addPotentialCoefficientsReader(new SHAFormatReader("corrupted-4_sha.grgm1200b_sigma_truncated_5x5", false)); + GravityFieldFactory.getUnnormalizedProvider(2, 2); + }); + } + + private void checkValue(final double value, final int n, final int m, + final double constant, final int maxUlps) + { + double factor = GravityFieldFactory.getUnnormalizationFactors(n, m)[n][m]; + double normalized = factor * constant; + double epsilon = maxUlps * FastMath.ulp(normalized); + Assertions.assertEquals(normalized, value, epsilon); + } +} diff --git a/src/test/java/org/orekit/forces/maneuvers/ConfigurableLowThrustManeuverTest.java b/src/test/java/org/orekit/forces/maneuvers/ConfigurableLowThrustManeuverTest.java index b402f52756..0092a2feb1 100644 --- a/src/test/java/org/orekit/forces/maneuvers/ConfigurableLowThrustManeuverTest.java +++ b/src/test/java/org/orekit/forces/maneuvers/ConfigurableLowThrustManeuverTest.java @@ -51,17 +51,7 @@ import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.AbstractDetector; -import org.orekit.propagation.events.AdaptableInterval; -import org.orekit.propagation.events.BooleanDetector; -import org.orekit.propagation.events.DateDetector; -import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.events.FieldAbstractDetector; -import org.orekit.propagation.events.FieldAdaptableInterval; -import org.orekit.propagation.events.FieldDateDetector; -import org.orekit.propagation.events.FieldNegateDetector; -import org.orekit.propagation.events.NegateDetector; -import org.orekit.propagation.events.PositionAngleDetector; +import org.orekit.propagation.events.*; import org.orekit.propagation.events.handlers.ContinueOnEvent; import org.orekit.propagation.events.handlers.EventHandler; import org.orekit.propagation.events.handlers.FieldEventHandler; @@ -289,7 +279,7 @@ public DateIntervalFieldDetector(final FieldAbsoluteDate startDate, final Fie protected DateIntervalFieldDetector(final FieldAbsoluteDate startDate, final FieldAbsoluteDate endDate, final FieldAdaptableInterval maxCheck, final T threshold, final int maxIter, final FieldEventHandler handler) { - super(maxCheck, threshold, maxIter, handler); + super(new FieldEventDetectionSettings<>(maxCheck, threshold, maxIter), handler); this.startDate = startDate; this.endDate = endDate; if (startDate.durationFrom(endDate).getReal() >= 0) { diff --git a/src/test/java/org/orekit/forces/radiation/AbstractLightFluxModelTest.java b/src/test/java/org/orekit/forces/radiation/AbstractLightFluxModelTest.java index 9bd5e6d187..1334b25834 100644 --- a/src/test/java/org/orekit/forces/radiation/AbstractLightFluxModelTest.java +++ b/src/test/java/org/orekit/forces/radiation/AbstractLightFluxModelTest.java @@ -63,6 +63,26 @@ private SpacecraftState mockState(final Vector3D position, final AbsoluteDate da return mockedState; } + @Test + void testGetLightingRatioField() { + // GIVEN + final ComplexField field = ComplexField.getInstance(); + final FieldVector3D position = new FieldVector3D<>(field, new Vector3D(1.0, 2.0, 3.0)); + final FieldAbsoluteDate date = FieldAbsoluteDate.getArbitraryEpoch(field); + final Frame mockedFrame = Mockito.mock(Frame.class); + final FieldSpacecraftState mockedFieldState = mockState(position, date, mockedFrame); + final ExtendedPVCoordinatesProvider mockedProvider = Mockito.mock(ExtendedPVCoordinatesProvider.class); + final FieldVector3D sunPosition = FieldVector3D.getMinusJ(field); + Mockito.when(mockedProvider.getPosition(date, mockedFrame)).thenReturn(sunPosition); + final TestLightFluxModel testLightFluxModel = new TestLightFluxModel(mockedProvider); + // WHEN + final Complex fieldLightingRatio = testLightFluxModel.getLightingRatio(mockedFieldState); + // THEN + final SpacecraftState mockedState = mockState(position.toVector3D(), date.toAbsoluteDate(), mockedFrame); + final double expectedLightingRatio = testLightFluxModel.getLightingRatio(mockedState); + Assertions.assertEquals(expectedLightingRatio, fieldLightingRatio.getReal()); + } + @Test void testGetLightFluxVectorField() { // GIVEN diff --git a/src/test/java/org/orekit/forces/radiation/AbstractSolarLightFluxModelTest.java b/src/test/java/org/orekit/forces/radiation/AbstractSolarLightFluxModelTest.java new file mode 100644 index 0000000000..19e1fc946e --- /dev/null +++ b/src/test/java/org/orekit/forces/radiation/AbstractSolarLightFluxModelTest.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.forces.radiation; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.frames.Frame; +import org.orekit.propagation.events.*; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.ExtendedPositionProvider; + +import java.util.Collections; +import java.util.List; + +class AbstractSolarLightFluxModelTest { + + @Test + void testGetters() { + // GIVEN + final double expectedRadius = 0.5; + final ExtendedPositionProvider sun = Mockito.mock(ExtendedPositionProvider.class); + // WHEN + final TestSolarFlux model = new TestSolarFlux(Double.NaN, sun, expectedRadius); + // THEN + Assertions.assertEquals(expectedRadius, model.getOccultingBodyRadius()); + Assertions.assertEquals(sun, model.getOccultedBody()); + } + + @Test + void testGetUnoccultedFluxVector() { + // GIVEN + final ComplexField field = ComplexField.getInstance(); + final double occultingBodyRadius = 0.5; + final FieldVector3D sunPosition = FieldVector3D.getPlusI(field).scalarMultiply(10.); + final ExtendedPositionProvider sun = mockFieldProvider(sunPosition); + final TestSolarFlux model = new TestSolarFlux(Double.NaN, sun, occultingBodyRadius); + final Vector3D position = new Vector3D(1., 1.); + final FieldVector3D fieldPosition = new FieldVector3D<>(field, position); + // WHEN + final FieldVector3D fieldFlux = model.getUnoccultedFluxVector(fieldPosition); + // THEN + final Vector3D expectedFlux = model.getUnoccultedFluxVector(position); + Assertions.assertEquals(expectedFlux, fieldFlux.toVector3D()); + } + + @SuppressWarnings("unchecked") + private ExtendedPositionProvider mockFieldProvider(final FieldVector3D sunPosition) { + final ExtendedPositionProvider mockedProvider = Mockito.mock(ExtendedPositionProvider.class); + Mockito.when(mockedProvider.getPosition(Mockito.any(FieldAbsoluteDate.class), Mockito.any(Frame.class))) + .thenReturn(sunPosition); + return mockedProvider; + } + + private static class TestSolarFlux extends AbstractSolarLightFluxModel { + + protected TestSolarFlux(double kRef, ExtendedPositionProvider occultedBody, double occultingBodyRadius) { + super(kRef, occultedBody, occultingBodyRadius, EventDetectionSettings.getDefaultEventDetectionSettings()); + } + + @Override + protected double getLightingRatio(Vector3D position, Vector3D occultedBodyPosition) { + return 0; + } + + @Override + protected > T getLightingRatio(FieldVector3D position, FieldVector3D occultedBodyPosition) { + return null; + } + + @Override + public List getEclipseConditionsDetector() { + return Collections.emptyList(); + } + + @Override + public > List> getFieldEclipseConditionsDetector(Field field) { + return Collections.emptyList(); + } + } + +} diff --git a/src/test/java/org/orekit/forces/radiation/ConicallyShadowedLightFluxModelTest.java b/src/test/java/org/orekit/forces/radiation/ConicallyShadowedLightFluxModelTest.java new file mode 100644 index 0000000000..5fcb3e4b55 --- /dev/null +++ b/src/test/java/org/orekit/forces/radiation/ConicallyShadowedLightFluxModelTest.java @@ -0,0 +1,135 @@ +package org.orekit.forces.radiation; + +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +import org.orekit.bodies.AnalyticalSolarPositionProvider; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.errors.OrekitException; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.EventDetectionSettings; +import org.orekit.propagation.events.EventDetector; +import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.propagation.events.handlers.FieldResetDerivativesOnEvent; +import org.orekit.propagation.events.handlers.ResetDerivativesOnEvent; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.ExtendedPVCoordinatesProvider; +import org.orekit.utils.ExtendedPositionProvider; +import org.orekit.utils.PVCoordinates; + +import java.util.List; + +class ConicallyShadowedLightFluxModelTest { + + @BeforeEach + public void setUp() throws OrekitException { + Utils.setDataRoot("potential/shm-format:regular-data"); + } + + @Test + void testConstructor() { + // GIVEN + final ExtendedPositionProvider positionProvider = new AnalyticalSolarPositionProvider(); + final ConicallyShadowedLightFluxModel fluxModel = new ConicallyShadowedLightFluxModel(1., 1., + positionProvider, 1.); + // WHEN + final List detectors = fluxModel.getEclipseConditionsDetector(); + // THEN + final EventDetectionSettings detectionSettings = ConicallyShadowedLightFluxModel.getDefaultEclipseDetectionSettings(); + Assertions.assertEquals(detectionSettings.getMaxIterationCount(), detectors.get(0).getMaxIterationCount()); + Assertions.assertEquals(detectionSettings.getThreshold(), detectors.get(0).getThreshold()); + } + + @Test + void testGetEclipseConditionsDetector() { + // GIVEN + final ExtendedPositionProvider positionProvider = new AnalyticalSolarPositionProvider(); + final ConicallyShadowedLightFluxModel fluxModel = new ConicallyShadowedLightFluxModel(1., positionProvider, + 1.); + // WHEN + final List detectors = fluxModel.getEclipseConditionsDetector(); + // THEN + Assertions.assertEquals(2, detectors.size()); + for (EventDetector detector : detectors) { + Assertions.assertInstanceOf(ResetDerivativesOnEvent.class, detector.getHandler()); + } + } + + @Test + void testGetFieldEclipseConditionsDetector() { + // GIVEN + final ExtendedPositionProvider positionProvider = new AnalyticalSolarPositionProvider(); + final ConicallyShadowedLightFluxModel fluxModel = new ConicallyShadowedLightFluxModel(1., positionProvider, + 1.); + final ComplexField field = ComplexField.getInstance(); + // WHEN + final List> fieldDetectors = fluxModel.getFieldEclipseConditionsDetector(field); + // THEN + final List detectors = fluxModel.getEclipseConditionsDetector(); + Assertions.assertEquals(detectors.size(), fieldDetectors.size()); + for (int i = 0; i < detectors.size(); i++) { + Assertions.assertInstanceOf(FieldResetDerivativesOnEvent.class, fieldDetectors.get(i).getHandler()); + Assertions.assertEquals(detectors.get(i).getThreshold(), fieldDetectors.get(i).getThreshold().getReal()); + Assertions.assertEquals(detectors.get(i).getMaxIterationCount(), fieldDetectors.get(i).getMaxIterationCount()); + } + final SpacecraftState state = new SpacecraftState(new CartesianOrbit(new PVCoordinates(Vector3D.PLUS_I, Vector3D.MINUS_J), + FramesFactory.getEME2000(), AbsoluteDate.ARBITRARY_EPOCH, 1.)); + final FieldSpacecraftState fieldState = new FieldSpacecraftState<>(field, state); + fluxModel.init(fieldState, null); + for (int i = 0; i < detectors.size(); i++) { + Assertions.assertEquals(detectors.get(i).g(state), fieldDetectors.get(i).g(fieldState).getReal(), 1e-8); + Assertions.assertEquals(detectors.get(i).getMaxCheckInterval().currentInterval(state), + fieldDetectors.get(i).getMaxCheckInterval().currentInterval(fieldState), 1e-14); + } + } + + @Test + void testFieldGetLightingRatio() { + // GIVEN + final ExtendedPositionProvider positionProvider = new AnalyticalSolarPositionProvider(); + final ConicallyShadowedLightFluxModel fluxModel = new ConicallyShadowedLightFluxModel(Constants.SUN_RADIUS, positionProvider, + Constants.EGM96_EARTH_EQUATORIAL_RADIUS); + final ComplexField field = ComplexField.getInstance(); + // WHEN & THEN + for (int i = 0; i < 100000; i++) { + final SpacecraftState state = new SpacecraftState(new CartesianOrbit( + new PVCoordinates(new Vector3D(fluxModel.getOccultingBodyRadius() + 500e3, 0., 1e3), new Vector3D(1e1, 7e3, 1e2)), + FramesFactory.getEME2000(), AbsoluteDate.ARBITRARY_EPOCH.shiftedBy(i * 3600.), Constants.EGM96_EARTH_MU)); + final FieldSpacecraftState fieldState = new FieldSpacecraftState<>(field, state); + final double expectedRatio = fluxModel.getLightingRatio(state); + Assertions.assertEquals(expectedRatio, fluxModel.getLightingRatio(fieldState).getReal(), 1e-8); + } + } + + @Test + void testGetLightingRatio() { + // GIVEN + final ExtendedPositionProvider positionProvider = new AnalyticalSolarPositionProvider(); + final ConicallyShadowedLightFluxModel fluxModel = new ConicallyShadowedLightFluxModel(Constants.SUN_RADIUS, positionProvider, + Constants.EGM96_EARTH_EQUATORIAL_RADIUS); + final Frame frame = FramesFactory.getGCRF(); + final SolarRadiationPressure radiationPressure = new SolarRadiationPressure(getExtendedPVCoordinatesProvider(positionProvider), + new OneAxisEllipsoid(fluxModel.getOccultingBodyRadius(), 0., FramesFactory.getGCRF()), null); + // WHEN & THEN + for (int i = 0; i < 10000; i++) { + final SpacecraftState state = new SpacecraftState(new CartesianOrbit( + new PVCoordinates(new Vector3D(fluxModel.getOccultingBodyRadius() + 500e3, 0., 1e3), new Vector3D(1e1, 7e3, 1e2)), + frame, AbsoluteDate.ARBITRARY_EPOCH.shiftedBy(i * 3600.), Constants.EGM96_EARTH_MU)); + final double expectedRatio = radiationPressure.getLightingRatio(state); + Assertions.assertEquals(expectedRatio, fluxModel.getLightingRatio(state), 1e-6); + } + } + + private static ExtendedPVCoordinatesProvider getExtendedPVCoordinatesProvider(final ExtendedPositionProvider positionProvider) { + return positionProvider::getPVCoordinates; + } +} diff --git a/src/test/java/org/orekit/forces/radiation/CylindricallyShadowedLightFluxModelTest.java b/src/test/java/org/orekit/forces/radiation/CylindricallyShadowedLightFluxModelTest.java index 7d115851ad..691afa7d72 100644 --- a/src/test/java/org/orekit/forces/radiation/CylindricallyShadowedLightFluxModelTest.java +++ b/src/test/java/org/orekit/forces/radiation/CylindricallyShadowedLightFluxModelTest.java @@ -33,7 +33,7 @@ import org.orekit.propagation.events.handlers.FieldStopOnEvent; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; -import org.orekit.utils.ExtendedPVCoordinatesProvider; +import org.orekit.utils.ExtendedPositionProvider; import java.util.List; @@ -44,7 +44,7 @@ void testGetLightingRatio() { // GIVEN final double occultingBodyRadius = 0.5; final Vector3D sunPosition = Vector3D.PLUS_I.scalarMultiply(10.); - final ExtendedPVCoordinatesProvider sun = mockProvider(sunPosition); + final ExtendedPositionProvider sun = mockProvider(sunPosition); final CylindricallyShadowedLightFluxModel model = new CylindricallyShadowedLightFluxModel(Double.NaN, sun, occultingBodyRadius); final CylindricalShadowEclipseDetector detector = new CylindricalShadowEclipseDetector(sun, model.getOccultingBodyRadius(), Mockito.mock(EventHandler.class)); @@ -64,8 +64,8 @@ void testGetLightingRatio() { } } - private ExtendedPVCoordinatesProvider mockProvider(final Vector3D sunPosition) { - final ExtendedPVCoordinatesProvider mockedProvider = Mockito.mock(ExtendedPVCoordinatesProvider.class); + private ExtendedPositionProvider mockProvider(final Vector3D sunPosition) { + final ExtendedPositionProvider mockedProvider = Mockito.mock(ExtendedPositionProvider.class); Mockito.when(mockedProvider.getPosition(Mockito.any(AbsoluteDate.class), Mockito.any(Frame.class))) .thenReturn(sunPosition); return mockedProvider; @@ -85,7 +85,7 @@ void testFieldGetLightingRatio() { final ComplexField field = ComplexField.getInstance(); final double occultingBodyRadius = 0.5; final FieldVector3D sunPosition = FieldVector3D.getPlusI(field).scalarMultiply(10.); - final ExtendedPVCoordinatesProvider sun = mockFieldProvider(sunPosition); + final ExtendedPositionProvider sun = mockFieldProvider(sunPosition); final CylindricallyShadowedLightFluxModel model = new CylindricallyShadowedLightFluxModel(Double.NaN, sun, occultingBodyRadius); final FieldCylindricalShadowEclipseDetector detector = new FieldCylindricalShadowEclipseDetector<>(sun, new Complex(model.getOccultingBodyRadius()), new FieldStopOnEvent<>()); @@ -105,26 +105,9 @@ void testFieldGetLightingRatio() { } } - @Test - void testGetUnoccultedFluxVector() { - // GIVEN - final ComplexField field = ComplexField.getInstance(); - final double occultingBodyRadius = 0.5; - final FieldVector3D sunPosition = FieldVector3D.getPlusI(field).scalarMultiply(10.); - final ExtendedPVCoordinatesProvider sun = mockFieldProvider(sunPosition); - final CylindricallyShadowedLightFluxModel model = new CylindricallyShadowedLightFluxModel(Double.NaN, sun, occultingBodyRadius); - final Vector3D position = new Vector3D(1., 1.); - final FieldVector3D fieldPosition = new FieldVector3D<>(field, position); - // WHEN - final FieldVector3D fieldFlux = model.getUnoccultedFluxVector(fieldPosition); - // THEN - final Vector3D expectedFlux = model.getUnoccultedFluxVector(position); - Assertions.assertEquals(expectedFlux, fieldFlux.toVector3D()); - } - @SuppressWarnings("unchecked") - private ExtendedPVCoordinatesProvider mockFieldProvider(final FieldVector3D sunPosition) { - final ExtendedPVCoordinatesProvider mockedProvider = Mockito.mock(ExtendedPVCoordinatesProvider.class); + private ExtendedPositionProvider mockFieldProvider(final FieldVector3D sunPosition) { + final ExtendedPositionProvider mockedProvider = Mockito.mock(ExtendedPositionProvider.class); Mockito.when(mockedProvider.getPosition(Mockito.any(FieldAbsoluteDate.class), Mockito.any(Frame.class))) .thenReturn(sunPosition); return mockedProvider; @@ -145,6 +128,7 @@ void testGetEclipseConditionsDetector() { final CylindricallyShadowedLightFluxModel model = Mockito.mock(CylindricallyShadowedLightFluxModel.class); Mockito.when(model.getOccultingBodyRadius()).thenReturn(1.); Mockito.when(model.getEclipseConditionsDetector()).thenCallRealMethod(); + Mockito.when(model.getEventDetectionSettings()).thenReturn(EventDetectionSettings.getDefaultEventDetectionSettings()); // WHEN final List detectors = model.getEclipseConditionsDetector(); // THEN @@ -161,6 +145,7 @@ void testGetFieldEclipseConditionsDetector() { final ComplexField field = ComplexField.getInstance(); final CylindricallyShadowedLightFluxModel model = Mockito.mock(CylindricallyShadowedLightFluxModel.class); Mockito.when(model.getOccultingBodyRadius()).thenReturn(1.); + Mockito.when(model.getEventDetectionSettings()).thenReturn(EventDetectionSettings.getDefaultEventDetectionSettings()); Mockito.when(model.getFieldEclipseConditionsDetector(field)).thenCallRealMethod(); Mockito.when(model.getEclipseConditionsDetector()).thenCallRealMethod(); // WHEN diff --git a/src/test/java/org/orekit/forces/radiation/RadiationPressureModelTest.java b/src/test/java/org/orekit/forces/radiation/RadiationPressureModelTest.java index 6bda5dcfdf..a20c860826 100644 --- a/src/test/java/org/orekit/forces/radiation/RadiationPressureModelTest.java +++ b/src/test/java/org/orekit/forces/radiation/RadiationPressureModelTest.java @@ -5,24 +5,36 @@ import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.ode.AbstractIntegrator; +import org.hipparchus.ode.FieldODEIntegrator; +import org.hipparchus.util.Binary64; +import org.hipparchus.util.Binary64Field; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mockito; import org.orekit.Utils; import org.orekit.bodies.CelestialBodyFactory; import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.forces.BoxAndSolarArraySpacecraft; +import org.orekit.forces.ForceModel; import org.orekit.frames.FramesFactory; import org.orekit.orbits.EquinoctialOrbit; import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.conversion.DormandPrince54FieldIntegratorBuilder; import org.orekit.propagation.conversion.DormandPrince54IntegratorBuilder; +import org.orekit.propagation.conversion.FieldODEIntegratorBuilder; +import org.orekit.propagation.conversion.ODEIntegratorBuilder; import org.orekit.propagation.events.EventDetector; import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.propagation.numerical.FieldNumericalPropagator; import org.orekit.propagation.numerical.NumericalPropagator; import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.Constants; import org.orekit.utils.ExtendedPVCoordinatesProvider; @@ -35,7 +47,7 @@ class RadiationPressureModelTest { @Test void testAcceleration() { // GIVEN - final SpacecraftState state = createState(); + final SpacecraftState state = createState(42000e3); final ComplexField field = ComplexField.getInstance(); final FieldSpacecraftState fieldState = new FieldSpacecraftState<>(field, state); final IsotropicRadiationSingleCoefficient radiationSingleCoefficient = new IsotropicRadiationSingleCoefficient(1., 2.); @@ -112,31 +124,96 @@ void testGetFieldEventDetectors() { Assertions.assertEquals(eclipseDetectors.size(), detectors.toArray().length); } + @ParameterizedTest + @ValueSource(doubles = {400e3, 500e3, 800e3, 1000e3, 2000e3}) + void testLeoPropagation(final double altitude) { + // GIVEN + Utils.setDataRoot("regular-data"); + final IsotropicRadiationSingleCoefficient isotropicRadiationSingleCoefficient = new IsotropicRadiationSingleCoefficient(10., 1.6); + final ConicallyShadowedLightFluxModel lightFluxModel = new ConicallyShadowedLightFluxModel(Constants.SUN_RADIUS, + CelestialBodyFactory.getSun(), Constants.EGM96_EARTH_EQUATORIAL_RADIUS); + final RadiationPressureModel forceModel = new RadiationPressureModel(lightFluxModel, + isotropicRadiationSingleCoefficient); + final double radius = lightFluxModel.getOccultingBodyRadius() + altitude; + final NumericalPropagator propagator = createPropagator(radius); + propagator.addForceModel(forceModel); + final Orbit orbit = propagator.getInitialState().getOrbit(); + final AbsoluteDate epoch = orbit.getDate(); + // WHEN + final AbsoluteDate terminalDate = epoch.shiftedBy(orbit.getKeplerianPeriod() * 5); + final SpacecraftState propagateState = propagator.propagate(terminalDate); + // THEN + final SpacecraftState comparableState = computeComparableState(forceModel, radius, terminalDate); + final Vector3D relativePosition = comparableState.getPosition().subtract(propagateState.getPosition(comparableState.getFrame())); + Assertions.assertEquals(0., relativePosition.getNorm(), 1e0); + } + + @Test + void testLeoFieldPropagation() { + // GIVEN + Utils.setDataRoot("regular-data"); + final IsotropicRadiationSingleCoefficient isotropicRadiationSingleCoefficient = new IsotropicRadiationSingleCoefficient(10., 1.6); + final ConicallyShadowedLightFluxModel lightFluxModel = new ConicallyShadowedLightFluxModel(Constants.SUN_RADIUS, + CelestialBodyFactory.getSun(), Constants.EGM96_EARTH_EQUATORIAL_RADIUS); + final RadiationPressureModel forceModel = new RadiationPressureModel(lightFluxModel, + isotropicRadiationSingleCoefficient); + final double radius = lightFluxModel.getOccultingBodyRadius() + 700e3; + final ComplexField field = ComplexField.getInstance(); + final SpacecraftState initialState = createState(radius); + final FieldNumericalPropagator fieldPropagator = buildFieldPropagator(field, forceModel, initialState); + final AbsoluteDate epoch = initialState.getDate(); + final AbsoluteDate terminalDate = epoch.shiftedBy(initialState.getKeplerianPeriod() * 10); + // WHEN + final FieldSpacecraftState propagatedState = fieldPropagator.propagate(new FieldAbsoluteDate<>(field, terminalDate)); + // THEN + final NumericalPropagator propagator = createPropagator(radius); + propagator.setOrbitType(fieldPropagator.getOrbitType()); + propagator.setPositionAngleType(fieldPropagator.getPositionAngleType()); + propagator.addForceModel(forceModel); + final SpacecraftState comparableState = propagator.propagate(terminalDate); + final Vector3D relativePosition = comparableState.getPosition().subtract(propagatedState.getPosition(comparableState.getFrame()).toVector3D()); + Assertions.assertEquals(0., relativePosition.getNorm(), 1e-3); + } + + private static FieldNumericalPropagator buildFieldPropagator(final ComplexField field, + final ForceModel forceModel, + final SpacecraftState initialState) { + final FieldODEIntegratorBuilder fieldIntegratoBuilder = new DormandPrince54FieldIntegratorBuilder<>(1e-3, 1e2, 1e-3); + final OrbitType propagationType = OrbitType.EQUINOCTIAL; + final FieldODEIntegrator fieldIntegrator = fieldIntegratoBuilder.buildIntegrator(field, initialState.getOrbit(), propagationType); + final FieldNumericalPropagator fieldPropagator = new FieldNumericalPropagator<>(field, fieldIntegrator); + fieldPropagator.addForceModel(forceModel); + fieldPropagator.setOrbitType(propagationType); + fieldPropagator.setInitialState(new FieldSpacecraftState<>(field, initialState)); + return fieldPropagator; + } + @Test - void testPropagation() { + void testGeoPropagation() { // GIVEN Utils.setDataRoot("regular-data"); - final IsotropicRadiationSingleCoefficient isotropicRadiationSingleCoefficient = new IsotropicRadiationSingleCoefficient(10., 0.5); + final IsotropicRadiationSingleCoefficient isotropicRadiationSingleCoefficient = new IsotropicRadiationSingleCoefficient(10., 1.6); final CylindricallyShadowedLightFluxModel lightFluxModel = new CylindricallyShadowedLightFluxModel(CelestialBodyFactory.getSun(), Constants.EGM96_EARTH_EQUATORIAL_RADIUS); final RadiationPressureModel forceModel = new RadiationPressureModel(lightFluxModel, isotropicRadiationSingleCoefficient); - final NumericalPropagator propagator = createPropagator(); + final double radius = 42000e3; + final NumericalPropagator propagator = createPropagator(radius); propagator.addForceModel(forceModel); final AbsoluteDate epoch = propagator.getInitialState().getDate(); // WHEN final AbsoluteDate terminalDate = epoch.shiftedBy(Constants.JULIAN_DAY * 10.); final SpacecraftState propagateState = propagator.propagate(terminalDate); // THEN - final SpacecraftState comparableState = computeComparableState(forceModel, terminalDate); + final SpacecraftState comparableState = computeComparableState(forceModel, radius, terminalDate); final Vector3D relativePosition = comparableState.getPosition().subtract(propagateState.getPosition(comparableState.getFrame())); Assertions.assertEquals(0., relativePosition.getNorm(), 1e-6); } private SpacecraftState computeComparableState(final RadiationPressureModel radiationPressureModel, - final AbsoluteDate terminalDate) { - final NumericalPropagator propagator = createPropagator(); - final CylindricallyShadowedLightFluxModel lightFluxModel = (CylindricallyShadowedLightFluxModel) radiationPressureModel.getLightFluxModel(); + final double radius, final AbsoluteDate terminalDate) { + final NumericalPropagator propagator = createPropagator(radius); + final AbstractSolarLightFluxModel lightFluxModel = (AbstractSolarLightFluxModel) radiationPressureModel.getLightFluxModel(); final SolarRadiationPressure solarRadiationPressure = new SolarRadiationPressure((ExtendedPVCoordinatesProvider) lightFluxModel.getOccultedBody(), new OneAxisEllipsoid(lightFluxModel.getOccultingBodyRadius(), 0., FramesFactory.getGTOD(false)), radiationPressureModel.getRadiationSensitive()); @@ -144,19 +221,19 @@ private SpacecraftState computeComparableState(final RadiationPressureModel radi return propagator.propagate(terminalDate); } - private SpacecraftState createState() { - return new SpacecraftState(createOrbit()); + private SpacecraftState createState(final double radius) { + return new SpacecraftState(createOrbit(radius), 100); } - private Orbit createOrbit() { - return new EquinoctialOrbit(42000e3, 0., 0., 0., 0., 0., PositionAngleType.ECCENTRIC, FramesFactory.getGCRF(), + private Orbit createOrbit(final double radius) { + return new EquinoctialOrbit(radius, 0., 0., 0., 0., 0., PositionAngleType.ECCENTRIC, FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, Constants.EGM96_EARTH_MU); } - private NumericalPropagator createPropagator() { - final SpacecraftState initialState = createState(); + private NumericalPropagator createPropagator(final double radius) { + final SpacecraftState initialState = createState(radius); final Orbit initialOrbit = initialState.getOrbit(); - final DormandPrince54IntegratorBuilder integratorBuilder = new DormandPrince54IntegratorBuilder(1e-3, 1e2, 1e-3); + final ODEIntegratorBuilder integratorBuilder = new DormandPrince54IntegratorBuilder(1e-3, 1e2, 1e-3); final AbstractIntegrator integrator = integratorBuilder.buildIntegrator(initialOrbit, initialOrbit.getType()); final NumericalPropagator propagator = new NumericalPropagator(integrator); propagator.setOrbitType(initialOrbit.getType()); diff --git a/src/test/java/org/orekit/frames/FieldStaticTransformTest.java b/src/test/java/org/orekit/frames/FieldStaticTransformTest.java index 7538c6b4e0..3894881b5e 100644 --- a/src/test/java/org/orekit/frames/FieldStaticTransformTest.java +++ b/src/test/java/org/orekit/frames/FieldStaticTransformTest.java @@ -29,6 +29,7 @@ import org.hipparchus.geometry.euclidean.threed.RotationConvention; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.Binary64; import org.hipparchus.util.Binary64Field; import org.hipparchus.util.FastMath; import org.junit.jupiter.api.Assertions; @@ -191,6 +192,25 @@ void testGetFieldDate() { Assertions.assertEquals(testFieldStaticTransform.getDate(), actualFieldDate.toAbsoluteDate()); } + @Test + void testGetIdentity() { + // GIVEN + final Field field = Binary64Field.getInstance(); + final FieldStaticTransform identity = FieldStaticTransform.getIdentity(field); + final Vector3D vector3D = new Vector3D(3., 2.,1.); + final FieldVector3D fieldVector3D = new FieldVector3D<>(field, vector3D); + // WHEN & THEN + Assertions.assertEquals(identity.getFieldDate().toAbsoluteDate(), identity.getDate()); + Assertions.assertEquals(identity.transformVector(vector3D), identity.getRotation().applyTo(vector3D)); + Assertions.assertEquals(identity.transformPosition(vector3D), + identity.getRotation().applyTo(vector3D).add(identity.getTranslation().toVector3D())); + Assertions.assertEquals(identity.transformVector(fieldVector3D), identity.getRotation().applyTo(fieldVector3D)); + Assertions.assertEquals(identity.transformPosition(fieldVector3D), + identity.getRotation().applyTo(fieldVector3D).add(identity.getTranslation())); + Assertions.assertEquals(identity, identity.getInverse()); + Assertions.assertEquals(identity, identity.getStaticInverse()); + } + private static class TestFieldStaticTransform implements FieldStaticTransform { private final AbsoluteDate date; diff --git a/src/test/java/org/orekit/frames/FieldTransformTest.java b/src/test/java/org/orekit/frames/FieldTransformTest.java index 306ace510f..642100143d 100644 --- a/src/test/java/org/orekit/frames/FieldTransformTest.java +++ b/src/test/java/org/orekit/frames/FieldTransformTest.java @@ -31,6 +31,7 @@ import org.hipparchus.linear.MatrixUtils; import org.hipparchus.random.RandomGenerator; import org.hipparchus.random.Well19937a; +import org.hipparchus.util.Binary64; import org.hipparchus.util.Binary64Field; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathArrays; @@ -39,13 +40,7 @@ import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.FieldTimeInterpolator; -import org.orekit.utils.AngularCoordinates; -import org.orekit.utils.CartesianDerivativesFilter; -import org.orekit.utils.Constants; -import org.orekit.utils.FieldPVCoordinates; -import org.orekit.utils.PVCoordinates; -import org.orekit.utils.TimeStampedFieldPVCoordinates; -import org.orekit.utils.TimeStampedFieldPVCoordinatesHermiteInterpolator; +import org.orekit.utils.*; import java.lang.reflect.Array; import java.util.ArrayList; @@ -53,6 +48,123 @@ public class FieldTransformTest { + @Test + void testIdentityTransformVector() { + // GIVEN + final Binary64Field field = Binary64Field.getInstance(); + final FieldTransform identity = FieldTransform.getIdentity(field); + final Vector3D vector3D = new Vector3D(1, 2, 3); + final FieldVector3D fieldVector3D = new FieldVector3D<>(field, vector3D); + // WHEN + final FieldVector3D transformed = identity.transformVector(vector3D); + // THEN + Assertions.assertEquals(fieldVector3D, transformed); + Assertions.assertEquals(identity.transformVector(vector3D), transformed); + Assertions.assertEquals(identity.transformVector(fieldVector3D), transformed); + Assertions.assertEquals(identity.transformPosition(vector3D), transformed); + Assertions.assertEquals(identity.transformPosition(fieldVector3D), transformed); + } + + @Test + void testIdentityTransformPVCoordinates() { + // GIVEN + final Binary64Field field = Binary64Field.getInstance(); + final FieldTransform identity = FieldTransform.getIdentity(field); + final Vector3D position = new Vector3D(1, 2, 3); + final Vector3D velocity = Vector3D.MINUS_K; + final Vector3D acceleration = new Vector3D(-1, 1); + final PVCoordinates pvCoordinates = new PVCoordinates(position, velocity, acceleration); + // WHEN + final FieldPVCoordinates transformed = identity.transformPVCoordinates(pvCoordinates); + // THEN + final FieldPVCoordinates fieldPVCoordinates = new FieldPVCoordinates(field, pvCoordinates); + Assertions.assertEquals(position, transformed.getPosition().toVector3D()); + Assertions.assertEquals(velocity, transformed.getVelocity().toVector3D()); + Assertions.assertEquals(acceleration, transformed.getAcceleration().toVector3D()); + Assertions.assertEquals(identity.transformPVCoordinates(fieldPVCoordinates).getPosition(), + transformed.getPosition()); + Assertions.assertEquals(identity.transformPVCoordinates(fieldPVCoordinates).getVelocity(), + transformed.getVelocity()); + final TimeStampedPVCoordinates timeStampedFieldPVCoordinates = new TimeStampedPVCoordinates(AbsoluteDate.ARBITRARY_EPOCH, + pvCoordinates); + Assertions.assertEquals(identity.transformPVCoordinates(timeStampedFieldPVCoordinates).getPosition(), + transformed.getPosition()); + Assertions.assertEquals(identity.transformPVCoordinates(timeStampedFieldPVCoordinates).getVelocity(), + transformed.getVelocity()); + } + + @Test + void testIdentityTransformOnlyPV() { + // GIVEN + final Binary64Field field = Binary64Field.getInstance(); + final FieldTransform identity = FieldTransform.getIdentity(field); + final Vector3D position = new Vector3D(1, 2, 3); + final Vector3D velocity = Vector3D.MINUS_K; + final PVCoordinates pvCoordinates = new PVCoordinates(position, velocity); + final FieldPVCoordinates fieldPVCoordinates = new FieldPVCoordinates<>(field, pvCoordinates); + // WHEN + final FieldPVCoordinates transformed = identity.transformOnlyPV(fieldPVCoordinates); + // THEN + Assertions.assertEquals(position, transformed.getPosition().toVector3D()); + Assertions.assertEquals(velocity, transformed.getVelocity().toVector3D()); + Assertions.assertEquals(FieldVector3D.getZero(field), transformed.getAcceleration()); + Assertions.assertEquals(identity.transformPVCoordinates(fieldPVCoordinates).getPosition(), + transformed.getPosition()); + Assertions.assertEquals(identity.transformPVCoordinates(fieldPVCoordinates).getVelocity(), + transformed.getVelocity()); + final TimeStampedFieldPVCoordinates timeStampedFieldPVCoordinates = new TimeStampedFieldPVCoordinates<>(identity.getFieldDate(), + fieldPVCoordinates); + Assertions.assertEquals(identity.transformPVCoordinates(timeStampedFieldPVCoordinates).getPosition(), + transformed.getPosition()); + Assertions.assertEquals(identity.transformPVCoordinates(timeStampedFieldPVCoordinates).getVelocity(), + transformed.getVelocity()); + } + + @Test + void testIdentityShiftedBy() { + // GIVEN + final Binary64Field field = Binary64Field.getInstance(); + final FieldTransform identity = FieldTransform.getIdentity(field); + // WHEN + final FieldTransform shiftedIdentity = identity.shiftedBy(1.); + // THEN + Assertions.assertEquals(identity, shiftedIdentity); + Assertions.assertEquals(identity.shiftedBy(Binary64.ONE), shiftedIdentity); + } + + @Test + void testIdentityStaticShiftedBy() { + // GIVEN + final Binary64Field field = Binary64Field.getInstance(); + final FieldTransform identity = FieldTransform.getIdentity(field); + // WHEN + final FieldStaticTransform shiftedIdentity = identity.staticShiftedBy(Binary64.ONE); + // THEN + Assertions.assertEquals(FieldStaticTransform.getIdentity(field).getClass(), shiftedIdentity.getClass()); + } + + @Test + void testIdentityGetStaticInverse() { + // GIVEN + final Binary64Field field = Binary64Field.getInstance(); + final FieldTransform identity = FieldTransform.getIdentity(field); + // WHEN + final FieldStaticTransform staticInverse = identity.getStaticInverse(); + // THEN + Assertions.assertEquals(identity.toStaticTransform().getClass(), staticInverse.getClass()); + } + + @Test + void testIdentityFreeze() { + // GIVEN + final Binary64Field field = Binary64Field.getInstance(); + final FieldTransform identity = FieldTransform.getIdentity(field); + // WHEN + final FieldTransform frozen = identity.freeze(); + // THEN + Assertions.assertEquals(identity, frozen); + } + @Test public void testIdentityTranslation() { doTestIdentityTranslation(Binary64Field.getInstance()); diff --git a/src/test/java/org/orekit/frames/FrameTest.java b/src/test/java/org/orekit/frames/FrameTest.java index a24ac15b74..bb21f1f067 100644 --- a/src/test/java/org/orekit/frames/FrameTest.java +++ b/src/test/java/org/orekit/frames/FrameTest.java @@ -25,6 +25,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.orekit.OrekitMatchers; import org.orekit.Utils; import org.orekit.bodies.GeodeticPoint; @@ -286,6 +287,29 @@ void testGetKinematicTransformTo() { Assertions.assertEquals(transform.getRotationRate(), kinematicTransform.getRotationRate()); } + @Test + void testGetStaticTransformIdentity() { + // GIVEN + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Frame mockedFrame = Mockito.mock(Frame.class); + Mockito.when(mockedFrame.getStaticTransformTo(mockedFrame, date)).thenCallRealMethod(); + // WHEN + final StaticTransform staticTransform = mockedFrame.getStaticTransformTo(mockedFrame, date); + // THEN + Assertions.assertEquals(staticTransform, staticTransform.getStaticInverse()); + } + + @Test + void testGetStaticTransformIdentityField() { + // GIVEN + final FieldAbsoluteDate fieldDate = FieldAbsoluteDate.getArbitraryEpoch(ComplexField.getInstance()); + final Frame frame = FramesFactory.getGCRF(); + // WHEN + final FieldStaticTransform staticTransform = frame.getStaticTransformTo(frame, fieldDate); + // THEN + Assertions.assertEquals(staticTransform.getClass(), staticTransform.getStaticInverse().getClass()); + } + @Test void testFieldGetKinematicTransformToWithConstantDate() { templateTestFieldGetKinematicTransformTo(getComplexDate()); diff --git a/src/test/java/org/orekit/frames/StaticTransformTest.java b/src/test/java/org/orekit/frames/StaticTransformTest.java index fa135bd195..f36bf5f719 100644 --- a/src/test/java/org/orekit/frames/StaticTransformTest.java +++ b/src/test/java/org/orekit/frames/StaticTransformTest.java @@ -19,10 +19,9 @@ import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; -import org.hipparchus.geometry.euclidean.threed.Line; -import org.hipparchus.geometry.euclidean.threed.Rotation; -import org.hipparchus.geometry.euclidean.threed.RotationConvention; -import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +import org.hipparchus.geometry.euclidean.threed.*; import org.hipparchus.util.FastMath; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -110,4 +109,23 @@ void testGetStaticInverse() { actualInverseStaticTransform.getRotation())); } + @Test + void testGetIdentity() { + // GIVEN + final StaticTransform identity = StaticTransform.getIdentity(); + final Vector3D vector3D = new Vector3D(1, 2, 3); + final FieldVector3D fieldVector3D = new FieldVector3D<>(ComplexField.getInstance(), vector3D); + // WHEN & THEN + Assertions.assertEquals(AbsoluteDate.ARBITRARY_EPOCH, identity.getDate()); + Assertions.assertEquals(identity.transformVector(vector3D), identity.getRotation().applyTo(vector3D)); + Assertions.assertEquals(identity.transformPosition(vector3D), + identity.getRotation().applyTo(vector3D).add(identity.getTranslation())); + Assertions.assertEquals(identity.transformVector(vector3D), identity.getRotation().applyTo(vector3D)); + Assertions.assertEquals(identity.transformVector(fieldVector3D).toVector3D(), + identity.getRotation().applyTo(fieldVector3D.toVector3D())); + Assertions.assertEquals(identity.transformPosition(fieldVector3D).toVector3D(), + identity.getTranslation().add(identity.getRotation().applyTo(fieldVector3D.toVector3D()))); + Assertions.assertEquals(identity, identity.getStaticInverse()); + Assertions.assertEquals(identity, identity.getInverse()); + } } diff --git a/src/test/java/org/orekit/frames/TransformProviderUtilTest.java b/src/test/java/org/orekit/frames/TransformProviderUtilTest.java index 7117d20dff..31f2dcf4c2 100644 --- a/src/test/java/org/orekit/frames/TransformProviderUtilTest.java +++ b/src/test/java/org/orekit/frames/TransformProviderUtilTest.java @@ -156,6 +156,28 @@ void testGetReversedProviderFieldGetStaticTransform() { Assertions.assertEquals(expectedTransform, actualTransform); } + @Test + void testStaticIdentity() { + // GIVEN + + // WHEN + final StaticTransform staticTransform = TransformProviderUtils.IDENTITY_PROVIDER.getStaticTransform(Mockito.mock(AbsoluteDate.class)); + // THEN + Assertions.assertEquals(Rotation.IDENTITY, staticTransform.getRotation()); + Assertions.assertEquals(Vector3D.ZERO, staticTransform.getTranslation()); + } + + @Test + void testFieldStaticIdentity() { + // GIVEN + final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(ComplexField.getInstance(), AbsoluteDate.BEIDOU_EPOCH); + // WHEN + final FieldStaticTransform staticTransform = TransformProviderUtils.IDENTITY_PROVIDER.getStaticTransform(fieldDate); + // THEN + Assertions.assertEquals(0., Rotation.distance(Rotation.IDENTITY, staticTransform.getRotation().toRotation())); + Assertions.assertEquals(Vector3D.ZERO, staticTransform.getTranslation().toVector3D()); + } + @Test public void testIdentity() { RandomGenerator random = new Well19937a(0x87c3a5c51fb0235el); diff --git a/src/test/java/org/orekit/frames/TransformTest.java b/src/test/java/org/orekit/frames/TransformTest.java index 662d3e5742..aec18eacc0 100644 --- a/src/test/java/org/orekit/frames/TransformTest.java +++ b/src/test/java/org/orekit/frames/TransformTest.java @@ -18,6 +18,8 @@ import org.hamcrest.MatcherAssert; import org.hipparchus.Field; +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Line; import org.hipparchus.geometry.euclidean.threed.Rotation; @@ -53,6 +55,101 @@ public class TransformTest { + @Test + void testIdentityTransformVector() { + // GIVEN + final Transform identity = Transform.IDENTITY; + final Vector3D vector3D = new Vector3D(1, 2, 3); + final FieldVector3D fieldVector3D = new FieldVector3D<>(ComplexField.getInstance(), vector3D); + // WHEN + final FieldVector3D transformed = identity.transformVector(fieldVector3D); + // THEN + Assertions.assertEquals(vector3D, transformed.toVector3D()); + Assertions.assertEquals(identity.transformVector(vector3D), transformed.toVector3D()); + Assertions.assertEquals(identity.transformPosition(vector3D), transformed.toVector3D()); + Assertions.assertEquals(identity.transformPosition(fieldVector3D), transformed); + } + + @Test + void testIdentityTransformPVCoordinates() { + // GIVEN + final Transform identity = Transform.IDENTITY; + final Vector3D position = new Vector3D(1, 2, 3); + final Vector3D velocity = Vector3D.MINUS_K; + final PVCoordinates pv = new PVCoordinates(position, velocity); + // WHEN + final PVCoordinates transformed = identity.transformPVCoordinates(pv); + // THEN + Assertions.assertEquals(position, transformed.getPosition()); + Assertions.assertEquals(velocity, transformed.getVelocity()); + Assertions.assertEquals(Vector3D.ZERO, transformed.getAcceleration()); + final TimeStampedPVCoordinates timeStampedPVCoordinates = new TimeStampedPVCoordinates(identity.getDate(), position, velocity); + Assertions.assertEquals(transformed.getPosition(), identity.transformPVCoordinates(timeStampedPVCoordinates).getPosition()); + Assertions.assertEquals(transformed.getVelocity(), identity.transformPVCoordinates(timeStampedPVCoordinates).getVelocity()); + Assertions.assertEquals(transformed.getPosition(), identity.transformOnlyPV(timeStampedPVCoordinates).getPosition()); + Assertions.assertEquals(transformed.getVelocity(), identity.transformOnlyPV(timeStampedPVCoordinates).getVelocity()); + } + + @Test + void testIdentityTransformOnlyPV() { + // GIVEN + final Transform identity = Transform.IDENTITY; + final Vector3D position = new Vector3D(1, 2, 3); + final Vector3D velocity = Vector3D.MINUS_K; + final PVCoordinates pv = new PVCoordinates(position, velocity); + // WHEN + final PVCoordinates transformed = identity.transformOnlyPV(pv); + // THEN + Assertions.assertEquals(position, transformed.getPosition()); + Assertions.assertEquals(velocity, transformed.getVelocity()); + Assertions.assertEquals(Vector3D.ZERO, transformed.getAcceleration()); + Assertions.assertEquals(transformed.getPosition(), identity.transformPVCoordinates(pv).getPosition()); + Assertions.assertEquals(transformed.getVelocity(), identity.transformPVCoordinates(pv).getVelocity()); + final TimeStampedPVCoordinates timeStampedPVCoordinates = new TimeStampedPVCoordinates(identity.getDate(), position, velocity); + Assertions.assertEquals(transformed.getPosition(), identity.transformOnlyPV(timeStampedPVCoordinates).getPosition()); + Assertions.assertEquals(transformed.getVelocity(), identity.transformOnlyPV(timeStampedPVCoordinates).getVelocity()); + } + + @Test + void testIdentityShiftedBy() { + // GIVEN + final Transform identity = Transform.IDENTITY; + // WHEN + final Transform shiftedIdentity = identity.shiftedBy(1.); + // THEN + Assertions.assertEquals(identity, shiftedIdentity); + } + + @Test + void testIdentityStaticShiftedBy() { + // GIVEN + final Transform identity = Transform.IDENTITY; + // WHEN + final StaticTransform shiftedIdentity = identity.staticShiftedBy(1.); + // THEN + Assertions.assertEquals(StaticTransform.getIdentity().getClass(), shiftedIdentity.getClass()); + } + + @Test + void testIdentityGetStaticInverse() { + // GIVEN + final Transform identity = Transform.IDENTITY; + // WHEN + final StaticTransform staticInverse = identity.getStaticInverse(); + // THEN + Assertions.assertEquals(identity.toStaticTransform().getClass(), staticInverse.getClass()); + } + + @Test + void testIdentityFreeze() { + // GIVEN + final Transform identity = Transform.IDENTITY; + // WHEN + final Transform frozen = identity.freeze(); + // THEN + Assertions.assertEquals(identity, frozen); + } + @Test public void testIdentityTranslation() { checkNoTransform(new Transform(AbsoluteDate.J2000_EPOCH, new Vector3D(0, 0, 0)), diff --git a/src/test/java/org/orekit/models/earth/ionosphere/NeQuickModelTest.java b/src/test/java/org/orekit/models/earth/ionosphere/NeQuickModelTest.java index c4e45e7b8b..d242d42f98 100644 --- a/src/test/java/org/orekit/models/earth/ionosphere/NeQuickModelTest.java +++ b/src/test/java/org/orekit/models/earth/ionosphere/NeQuickModelTest.java @@ -266,8 +266,8 @@ private > void doTestFieldAntiMeridian(final F // Date final FieldAbsoluteDate date = - new FieldAbsoluteDate(field, - new AbsoluteDate(2018, 4, 2, 16, 0, 0, TimeScalesFactory.getUTC())); + new FieldAbsoluteDate<>(field, + new AbsoluteDate(2018, 4, 2, 16, 0, 0, TimeScalesFactory.getUTC())); // Geodetic points final FieldGeodeticPoint recP = new FieldGeodeticPoint<>(FastMath.toRadians(zero.newInstance(-31.80)), @@ -281,4 +281,50 @@ private > void doTestFieldAntiMeridian(final F } + @Test + public void testZenith() { + + // Model + final NeQuickModel model = new NeQuickModel(medium); + + // Date + final AbsoluteDate date = new AbsoluteDate(2018, 4, 2, 16, 0, 0, TimeScalesFactory.getUTC()); + + // Geodetic points + final GeodeticPoint recP = new GeodeticPoint(FastMath.toRadians(51.678), FastMath.toRadians(-9.448), 0.0); + final GeodeticPoint satP = new GeodeticPoint(FastMath.toRadians(51.678), FastMath.toRadians(-9.448), 600000.0); + double stec = model.stec(date, recP, satP); + Assertions.assertEquals(26.346, stec, 0.001); + + } + + @Test + public void testFieldZenith() { + doTestFieldZenith(Binary64Field.getInstance()); + } + + private > void doTestFieldZenith(final Field field) { + + final T zero = field.getZero(); + + // Model + final NeQuickModel model = new NeQuickModel(medium); + + // Date + final FieldAbsoluteDate date = + new FieldAbsoluteDate<>(field, + new AbsoluteDate(2018, 4, 2, 16, 0, 0, TimeScalesFactory.getUTC())); + + // Geodetic points + final FieldGeodeticPoint recP = new FieldGeodeticPoint<>(FastMath.toRadians(zero.newInstance(51.678)), + FastMath.toRadians(zero.newInstance(-9.448)), + zero.newInstance(0.0)); + final FieldGeodeticPoint satP = new FieldGeodeticPoint<>(FastMath.toRadians(zero.newInstance(51.678)), + FastMath.toRadians(zero.newInstance(-9.448)), + zero.newInstance(600000.0)); + T stec = model.stec(date, recP, satP); + Assertions.assertEquals(26.346, stec.getReal(), 0.001); + + } + } diff --git a/src/test/java/org/orekit/orbits/CircularLatitudeArgumentUtilityTest.java b/src/test/java/org/orekit/orbits/CircularLatitudeArgumentUtilityTest.java index 120a496acd..57b0d0b953 100644 --- a/src/test/java/org/orekit/orbits/CircularLatitudeArgumentUtilityTest.java +++ b/src/test/java/org/orekit/orbits/CircularLatitudeArgumentUtilityTest.java @@ -18,6 +18,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; @@ -66,6 +68,21 @@ void testEccentricToMeanAndBack() { Assertions.assertEquals(expectedLatitudeArgument, actualLatitudeArgument, TOLERANCE); } + @ParameterizedTest + @EnumSource(PositionAngleType.class) + void testConvertAlpha(final PositionAngleType inputType) { + // GIVEN + final double expectedLatitudeArgument = 3.; + final PositionAngleType intermediateType = PositionAngleType.MEAN; + // WHEN + final double intermediateLatitudeArgument = CircularLatitudeArgumentUtility.convertAlpha(inputType, + expectedLatitudeArgument, EX, EY, intermediateType); + final double actualLatitudeArgument = CircularLatitudeArgumentUtility.convertAlpha(intermediateType, + intermediateLatitudeArgument, EX, EY, inputType); + // THEN + Assertions.assertEquals(expectedLatitudeArgument, actualLatitudeArgument, TOLERANCE); + } + @Test void testMeanToEccentricException() { // GIVEN @@ -75,4 +92,4 @@ void testMeanToEccentricException() { nanLatitudeArgument), OrekitMessages.UNABLE_TO_COMPUTE_ECCENTRIC_LATITUDE_ARGUMENT.toString()); } -} \ No newline at end of file +} diff --git a/src/test/java/org/orekit/orbits/EquinoctialLongitudeArgumentUtilityTest.java b/src/test/java/org/orekit/orbits/EquinoctialLongitudeArgumentUtilityTest.java index 9bfb5a9e1f..b6314dc1b2 100644 --- a/src/test/java/org/orekit/orbits/EquinoctialLongitudeArgumentUtilityTest.java +++ b/src/test/java/org/orekit/orbits/EquinoctialLongitudeArgumentUtilityTest.java @@ -18,6 +18,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; @@ -30,49 +32,63 @@ class EquinoctialLongitudeArgumentUtilityTest { @Test void testMeanToTrueAndBack() { // GIVEN - final double expectedLatitudeArgument = 3.; + final double expectedLongitudeArgument = 3.; // WHEN - final double intermediateLatitudeArgument = EquinoctialLongitudeArgumentUtility.meanToTrue(EX, EY, - expectedLatitudeArgument); - final double actualLatitudeArgument = EquinoctialLongitudeArgumentUtility.trueToMean(EX, EY, - intermediateLatitudeArgument); + final double intermediateLongitudeArgument = EquinoctialLongitudeArgumentUtility.meanToTrue(EX, EY, + expectedLongitudeArgument); + final double actualLongitudeArgument = EquinoctialLongitudeArgumentUtility.trueToMean(EX, EY, + intermediateLongitudeArgument); // THEN - Assertions.assertEquals(expectedLatitudeArgument, actualLatitudeArgument, TOLERANCE); + Assertions.assertEquals(expectedLongitudeArgument, actualLongitudeArgument, TOLERANCE); } @Test void testEccentricToTrueAndBack() { // GIVEN - final double expectedLatitudeArgument = 3.; + final double expectedLongitudeArgument = 3.; // WHEN - final double intermediateLatitudeArgument = EquinoctialLongitudeArgumentUtility.eccentricToTrue(EX, EY, - expectedLatitudeArgument); - final double actualLatitudeArgument = EquinoctialLongitudeArgumentUtility.trueToEccentric(EX, EY, - intermediateLatitudeArgument); + final double intermediateLongitudeArgument = EquinoctialLongitudeArgumentUtility.eccentricToTrue(EX, EY, + expectedLongitudeArgument); + final double actualLongitudeArgument = EquinoctialLongitudeArgumentUtility.trueToEccentric(EX, EY, + intermediateLongitudeArgument); // THEN - Assertions.assertEquals(expectedLatitudeArgument, actualLatitudeArgument, TOLERANCE); + Assertions.assertEquals(expectedLongitudeArgument, actualLongitudeArgument, TOLERANCE); } @Test void testEccentricToMeanAndBack() { // GIVEN - final double expectedLatitudeArgument = 3.; + final double expectedLongitudeArgument = 3.; // WHEN - final double intermediateLatitudeArgument = EquinoctialLongitudeArgumentUtility.eccentricToMean(EX, EY, - expectedLatitudeArgument); - final double actualLatitudeArgument = EquinoctialLongitudeArgumentUtility.meanToEccentric(EX, EY, - intermediateLatitudeArgument); + final double intermediateLongitudeArgument = EquinoctialLongitudeArgumentUtility.eccentricToMean(EX, EY, + expectedLongitudeArgument); + final double actualLongitudeArgument = EquinoctialLongitudeArgumentUtility.meanToEccentric(EX, EY, + intermediateLongitudeArgument); // THEN - Assertions.assertEquals(expectedLatitudeArgument, actualLatitudeArgument, TOLERANCE); + Assertions.assertEquals(expectedLongitudeArgument, actualLongitudeArgument, TOLERANCE); } @Test void testMeanToEccentricException() { // GIVEN - final double nanLatitudeArgument = Double.NaN; + final double nanLongitudeArgument = Double.NaN; // WHEN & THEN Assertions.assertThrows(OrekitException.class, () -> EquinoctialLongitudeArgumentUtility.meanToEccentric(EX, EY, - nanLatitudeArgument), OrekitMessages.UNABLE_TO_COMPUTE_ECCENTRIC_LONGITUDE_ARGUMENT.toString()); + nanLongitudeArgument), OrekitMessages.UNABLE_TO_COMPUTE_ECCENTRIC_LONGITUDE_ARGUMENT.toString()); } -} \ No newline at end of file + @ParameterizedTest + @EnumSource(PositionAngleType.class) + void testConvertL(final PositionAngleType inputType) { + // GIVEN + final double expectedLongitudeArgument = 3.; + final PositionAngleType intermediateType = PositionAngleType.ECCENTRIC; + // WHEN + final double intermediateLongitudeArgument = EquinoctialLongitudeArgumentUtility.convertL(inputType, + expectedLongitudeArgument, EX, EY, intermediateType); + final double actualLongitudeArgument = EquinoctialLongitudeArgumentUtility.convertL(intermediateType, + intermediateLongitudeArgument, EX, EY, inputType); + // THEN + Assertions.assertEquals(expectedLongitudeArgument, actualLongitudeArgument, TOLERANCE); + } +} diff --git a/src/test/java/org/orekit/orbits/FieldCircularLatitudeArgumentUtilityTest.java b/src/test/java/org/orekit/orbits/FieldCircularLatitudeArgumentUtilityTest.java index 0b11c04360..2a058e02ac 100644 --- a/src/test/java/org/orekit/orbits/FieldCircularLatitudeArgumentUtilityTest.java +++ b/src/test/java/org/orekit/orbits/FieldCircularLatitudeArgumentUtilityTest.java @@ -19,6 +19,8 @@ import org.hipparchus.complex.Complex; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; @@ -154,4 +156,19 @@ void testMeanToEccentricException() { EY, fieldNaNPositionAngle), OrekitMessages.UNABLE_TO_COMPUTE_ECCENTRIC_LATITUDE_ARGUMENT.toString()); } -} \ No newline at end of file + @ParameterizedTest + @EnumSource(PositionAngleType.class) + void testConvertAlphaVersusDouble(final PositionAngleType positionAngleType) { + // GIVEN + final Complex fieldOriginalPositionAngle = new Complex(3., 0.); + final PositionAngleType outputType = PositionAngleType.MEAN; + // WHEN + final double actualConvertedPositionAngle = FieldCircularLatitudeArgumentUtility.convertAlpha(positionAngleType, + fieldOriginalPositionAngle, EX, EY, outputType).getReal(); + // THEN + final double expectedPositionAngle = CircularLatitudeArgumentUtility.convertAlpha(positionAngleType, + fieldOriginalPositionAngle.getReal(), EX.getReal(), EY.getReal(), outputType); + Assertions.assertEquals(expectedPositionAngle, actualConvertedPositionAngle, TOLERANCE); + } + +} diff --git a/src/test/java/org/orekit/orbits/FieldEquinoctialLongitudeArgumentUtilityTest.java b/src/test/java/org/orekit/orbits/FieldEquinoctialLongitudeArgumentUtilityTest.java index 48feb9fe2c..02c733ab87 100644 --- a/src/test/java/org/orekit/orbits/FieldEquinoctialLongitudeArgumentUtilityTest.java +++ b/src/test/java/org/orekit/orbits/FieldEquinoctialLongitudeArgumentUtilityTest.java @@ -19,6 +19,8 @@ import org.hipparchus.complex.Complex; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; @@ -153,5 +155,21 @@ void testMeanToEccentricException() { Assertions.assertThrows(OrekitException.class, () -> FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(EX, EY, fieldNaNPositionAngle), OrekitMessages.UNABLE_TO_COMPUTE_ECCENTRIC_LONGITUDE_ARGUMENT.toString()); } + + + @ParameterizedTest + @EnumSource(PositionAngleType.class) + void testConvertLVersusDouble(final PositionAngleType positionAngleType) { + // GIVEN + final Complex fieldOriginalPositionAngle = new Complex(3., 0.); + final PositionAngleType outputType = PositionAngleType.ECCENTRIC; + // WHEN + final double actualConvertedPositionAngle = FieldEquinoctialLongitudeArgumentUtility.convertL(positionAngleType, + fieldOriginalPositionAngle, EX, EY, outputType).getReal(); + // THEN + final double expectedPositionAngle = EquinoctialLongitudeArgumentUtility.convertL(positionAngleType, + fieldOriginalPositionAngle.getReal(), EX.getReal(), EY.getReal(), outputType); + Assertions.assertEquals(expectedPositionAngle, actualConvertedPositionAngle, TOLERANCE); + } -} \ No newline at end of file +} diff --git a/src/test/java/org/orekit/orbits/FieldKeplerianAnomalyUtilityTest.java b/src/test/java/org/orekit/orbits/FieldKeplerianAnomalyUtilityTest.java index 6c94d594a6..7e985de490 100644 --- a/src/test/java/org/orekit/orbits/FieldKeplerianAnomalyUtilityTest.java +++ b/src/test/java/org/orekit/orbits/FieldKeplerianAnomalyUtilityTest.java @@ -18,9 +18,12 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; +import org.hipparchus.complex.Complex; import org.hipparchus.util.Binary64Field; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; public class FieldKeplerianAnomalyUtilityTest { @@ -235,4 +238,35 @@ private > void doTestIssue544(final Field f Assertions.assertTrue(Double.isNaN(E.getReal())); } + @ParameterizedTest + @EnumSource(PositionAngleType.class) + void testConvertAnomalyEllipticVersusDouble(final PositionAngleType positionAngleType) { + // GIVEN + final Complex fieldOriginalPositionAngle = new Complex(3., 0.); + final PositionAngleType outputType = PositionAngleType.TRUE; + final Complex eccentricity = new Complex(0.5, 0.); + // WHEN + final double actualConvertedPositionAngle = FieldKeplerianAnomalyUtility.convertAnomaly(positionAngleType, + fieldOriginalPositionAngle, eccentricity, outputType).getReal(); + // THEN + final double expectedPositionAngle = KeplerianAnomalyUtility.convertAnomaly(positionAngleType, + fieldOriginalPositionAngle.getReal(), eccentricity.getReal(), outputType); + Assertions.assertEquals(expectedPositionAngle, actualConvertedPositionAngle, 1e-12); + } + + @ParameterizedTest + @EnumSource(PositionAngleType.class) + void testConvertAnomalyHyperbolicVersusDouble(final PositionAngleType positionAngleType) { + // GIVEN + final Complex fieldOriginalPositionAngle = new Complex(3., 0.); + final PositionAngleType outputType = PositionAngleType.MEAN; + final Complex eccentricity = new Complex(2., 0.); + // WHEN + final double actualConvertedPositionAngle = FieldKeplerianAnomalyUtility.convertAnomaly(positionAngleType, + fieldOriginalPositionAngle, eccentricity, outputType).getReal(); + // THEN + final double expectedPositionAngle = KeplerianAnomalyUtility.convertAnomaly(positionAngleType, + fieldOriginalPositionAngle.getReal(), eccentricity.getReal(), outputType); + Assertions.assertEquals(expectedPositionAngle, actualConvertedPositionAngle, 1e-12); + } } diff --git a/src/test/java/org/orekit/orbits/KeplerianAnomalyUtilityTest.java b/src/test/java/org/orekit/orbits/KeplerianAnomalyUtilityTest.java index bbebb9d854..2571b1e074 100644 --- a/src/test/java/org/orekit/orbits/KeplerianAnomalyUtilityTest.java +++ b/src/test/java/org/orekit/orbits/KeplerianAnomalyUtilityTest.java @@ -18,9 +18,43 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; public class KeplerianAnomalyUtilityTest { + @ParameterizedTest + @EnumSource(PositionAngleType.class) + void testConvertAlphaElliptic(final PositionAngleType inputType) { + // GIVEN + final double expectedAnomaly = 3.; + final double eccentricity = 0.1; + final PositionAngleType intermediateType = PositionAngleType.ECCENTRIC; + // WHEN + final double intermediateAnomaly = KeplerianAnomalyUtility.convertAnomaly(inputType, + expectedAnomaly, eccentricity, intermediateType); + final double actualAnomaly = KeplerianAnomalyUtility.convertAnomaly(intermediateType, + intermediateAnomaly, eccentricity, inputType); + // THEN + Assertions.assertEquals(expectedAnomaly, actualAnomaly, 1e-15); + } + + @ParameterizedTest + @EnumSource(PositionAngleType.class) + void testConvertAlphaHyperbolic(final PositionAngleType inputType) { + // GIVEN + final double expectedAnomaly = 3.; + final double eccentricity = 2; + final PositionAngleType intermediateType = PositionAngleType.TRUE; + // WHEN + final double intermediateAnomaly = KeplerianAnomalyUtility.convertAnomaly(inputType, + expectedAnomaly, eccentricity, intermediateType); + final double actualAnomaly = KeplerianAnomalyUtility.convertAnomaly(intermediateType, + intermediateAnomaly, eccentricity, inputType); + // THEN + Assertions.assertEquals(expectedAnomaly, actualAnomaly, 1e-10); + } + @Test public void testEllipticMeanToTrue() { final double e = 0.231; diff --git a/src/test/java/org/orekit/orbits/OrbitBlenderTest.java b/src/test/java/org/orekit/orbits/OrbitBlenderTest.java index c2c637638d..83b66f503f 100644 --- a/src/test/java/org/orekit/orbits/OrbitBlenderTest.java +++ b/src/test/java/org/orekit/orbits/OrbitBlenderTest.java @@ -287,12 +287,12 @@ void testBrouwerLyddaneQuadraticBlendingOnSergeiCase() { // When & Then doTestInterpolation(stateInterpolator, DEFAULT_SERGEI_PROPAGATION_TIME, DEFAUTL_SERGEI_TABULATED_TIMESTEP, - 0.05106377388516059, - 0.03671310671380644, - 0.05451875412478483, - 0.03654640625064279, - 0.09412869297314610, - 0.06642996306635666, + 0.1162978884753, + 0.0588245986331, + 0.1184075880186, + 0.0640733000785, + 0.2095374397978, + 0.0901515566884, 1e-13, false); } @@ -320,12 +320,12 @@ void testBrouwerLyddaneQuadraticBlendingOnSergeiCaseDeprecated() { // When & Then doTestInterpolation(stateInterpolator, DEFAULT_SERGEI_PROPAGATION_TIME, DEFAUTL_SERGEI_TABULATED_TIMESTEP, - 0.05106377388516059, - 0.03671310671380644, - 0.05451875412478483, - 0.03654640625064279, - 0.09412869297314610, - 0.06642996306635666, + 0.1162978884753, + 0.0588245986330, + 0.1184075880186, + 0.0640733000785, + 0.2095374397978, + 0.0901515566884, 1e-13, false); } diff --git a/src/test/java/org/orekit/propagation/FieldStateCovarianceTest.java b/src/test/java/org/orekit/propagation/FieldStateCovarianceTest.java index 900f527c2d..bf65944b9d 100644 --- a/src/test/java/org/orekit/propagation/FieldStateCovarianceTest.java +++ b/src/test/java/org/orekit/propagation/FieldStateCovarianceTest.java @@ -18,6 +18,7 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; +import org.hipparchus.FieldElement; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.linear.BlockFieldMatrix; import org.hipparchus.linear.FieldMatrix; @@ -37,9 +38,11 @@ import org.orekit.frames.LOF; import org.orekit.frames.LOFType; import org.orekit.orbits.FieldCartesianOrbit; +import org.orekit.orbits.FieldKeplerianOrbit; import org.orekit.orbits.FieldOrbit; import org.orekit.orbits.OrbitType; import org.orekit.orbits.PositionAngleType; +import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeScalesFactory; import org.orekit.utils.Constants; @@ -47,6 +50,11 @@ import org.orekit.utils.IERSConventions; import org.orekit.utils.PVCoordinates; +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; + class FieldStateCovarianceTest { private final Field field = Binary64Field.getInstance(); @@ -1068,4 +1076,103 @@ void testConversionToNonFieldEquivalentFrame() { } -} \ No newline at end of file + @Test + public void testIssue1485CartesianOrbitTypeAndNullAngleType() { + // GIVEN + final Field field = Binary64Field.getInstance(); + + // WHEN & THEN + doTestIssue1485CartesianOrbitTypeAndNullAngleType(field); + } + + > void doTestIssue1485CartesianOrbitTypeAndNullAngleType(final Field field) { + // GIVEN + final T one = field.getOne(); + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, AbsoluteDate.ARBITRARY_EPOCH); + final FieldMatrix expectedMatrix = MatrixUtils.createFieldIdentityMatrix(field, 6); + final PositionAngleType anomalyType = PositionAngleType.MEAN; + final Frame inFrame = FramesFactory.getEME2000(); + final Frame outFrame = FramesFactory.getGCRF(); + final double mu = Constants.EGM96_EARTH_MU; + + final FieldKeplerianOrbit orbit = + new FieldKeplerianOrbit<>(one.multiply(1e7), one.multiply(0.01), one.multiply(1.), one.multiply(2.), + one.multiply(3.), one.multiply(4.), anomalyType, inFrame, date, one.multiply(mu)); + + final FieldStateCovariance originalCovariance = + new FieldStateCovariance<>(expectedMatrix, date, inFrame, OrbitType.CARTESIAN, null); + + // WHEN & THEN + assertDoesNotThrow(() -> originalCovariance.changeCovarianceFrame(orbit, outFrame)); + + // THEN + final FieldStateCovariance convertedCovariance = originalCovariance.changeCovarianceFrame(orbit, outFrame); + assertEquals(getNorm1(originalCovariance.getMatrix()), getNorm1(convertedCovariance.getMatrix())); + } + + @Test + void testIssue1485SameLOF() { + // GIVEN + final Field field = Binary64Field.getInstance(); + final LOF lof = LOFType.TNW; + + // WHEN & THEN + doTestIssue1485SameFrameDefinition(field, lof); + } + + @Test + void testIssue1485SameFrame() { + // GIVEN + final Field field = Binary64Field.getInstance(); + final Frame eme2000 = FramesFactory.getEME2000(); + + // WHEN & THEN + doTestIssue1485SameFrameDefinition(field, eme2000); + } + + > void doTestIssue1485SameFrameDefinition(Field field, T frameOrLOF) { + // GIVEN + final I one = field.getOne(); + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, AbsoluteDate.ARBITRARY_EPOCH); + final FieldMatrix expectedMatrix = MatrixUtils.createFieldIdentityMatrix(field, 6); + final PositionAngleType anomalyType = PositionAngleType.MEAN; + final Frame frame = FramesFactory.getEME2000(); + final double mu = Constants.EGM96_EARTH_MU; + final FieldKeplerianOrbit orbit = + new FieldKeplerianOrbit<>(one.multiply(1e7), one.multiply(0.01), one.multiply(1.), one.multiply(2.), + one.multiply(3.), one.multiply(4.), anomalyType, frame, date, one.multiply(mu)); + // WHEN + final FieldStateCovariance originalCovariance; + final FieldStateCovariance covariance; + if (frameOrLOF instanceof LOF) { + originalCovariance = new FieldStateCovariance<>(expectedMatrix, date, (LOF) frameOrLOF); + covariance = originalCovariance.changeCovarianceFrame(orbit, (LOF) frameOrLOF); + } else { + originalCovariance = + new FieldStateCovariance<>(expectedMatrix, date, (Frame) frameOrLOF, orbit.getType(), anomalyType); + covariance = originalCovariance.changeCovarianceFrame(orbit, (Frame) frameOrLOF); + } + + // THEN + final FieldMatrix actualMatrix = covariance.getMatrix(); + Assertions.assertEquals(0, getNorm1(actualMatrix.subtract(expectedMatrix))); + } + + /** + * Compute L1 norm of given field matrix + * + * @param fieldMatrix field matrix + * @param type of field element + * @return real L1 norm + */ + private > double getNorm1(final FieldMatrix fieldMatrix) { + + return Arrays.stream( + Arrays.stream(fieldMatrix.getData()) + .flatMap(Arrays::stream) + .mapToDouble(FieldElement::getReal) + .toArray()) + .sum(); + } + +} diff --git a/src/test/java/org/orekit/propagation/StateCovarianceBlenderTest.java b/src/test/java/org/orekit/propagation/StateCovarianceBlenderTest.java index e2f5312985..15580620c0 100644 --- a/src/test/java/org/orekit/propagation/StateCovarianceBlenderTest.java +++ b/src/test/java/org/orekit/propagation/StateCovarianceBlenderTest.java @@ -16,6 +16,9 @@ */ package org.orekit.propagation; +import java.util.Locale; +import java.util.Map; + import org.hipparchus.analysis.polynomials.SmoothStepFactory; import org.hipparchus.stat.descriptive.DescriptiveStatistics; import org.hipparchus.util.FastMath; @@ -44,9 +47,6 @@ import org.orekit.time.TimeStampedPair; import org.orekit.utils.Constants; -import java.util.Locale; -import java.util.Map; - class StateCovarianceBlenderTest { private static SpacecraftState sergeiState; @@ -198,12 +198,12 @@ void testBrouwerLyddaneQuadraticBlending() { // When & Then doTestBlending(DEFAULT_SERGEI_PROPAGATION_TIME, DEFAUTL_SERGEI_TABULATED_TIMESTEP, blendingFunction, propagator, - 0.08234429610, - 0.19764677567, - 0.08885901386, - 0.20209052349, - 0.14461602033, - 0.38946109017, + 0.13366703460, + 0.13160856461, + 0.14205498786, + 0.13656689649, + 0.21941543409, + 0.25091794341, tolerance, showResults); @@ -249,12 +249,12 @@ void testBrouwerLyddaneQuadraticBlendingDeprecated() { // When & Then doTestBlending(DEFAULT_SERGEI_PROPAGATION_TIME, DEFAUTL_SERGEI_TABULATED_TIMESTEP, blendingFunction, propagator, - 0.082344296100, - 0.197646775666, - 0.088859013862, - 0.202090523492, - 0.144616020330, - 0.389461090177, + 0.13366703460, + 0.13160856461, + 0.14205498786, + 0.13656689649, + 0.21941543409, + 0.25091794341, tolerance, showResults); diff --git a/src/test/java/org/orekit/propagation/StateCovarianceTest.java b/src/test/java/org/orekit/propagation/StateCovarianceTest.java index e3fc690f90..ad70dcca90 100644 --- a/src/test/java/org/orekit/propagation/StateCovarianceTest.java +++ b/src/test/java/org/orekit/propagation/StateCovarianceTest.java @@ -32,8 +32,10 @@ import org.orekit.forces.gravity.potential.ICGEMFormatReader; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; +import org.orekit.frames.LOF; import org.orekit.frames.LOFType; import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.KeplerianOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; import org.orekit.orbits.PositionAngleType; @@ -44,6 +46,9 @@ import org.orekit.utils.IERSConventions; import org.orekit.utils.PVCoordinates; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class StateCovarianceTest { final private double DEFAULT_VALLADO_THRESHOLD = 1e-6; @@ -82,11 +87,11 @@ public void testConstructor() { final AbsoluteDate initialDate = new AbsoluteDate(); final Frame initialInertialFrame = FramesFactory.getGCRF(); final StateCovariance stateCovariance = new StateCovariance(getValladoInitialCovarianceMatrix(), initialDate, initialInertialFrame, OrbitType.CARTESIAN, PositionAngleType.MEAN); - Assertions.assertEquals(OrbitType.CARTESIAN, stateCovariance.getOrbitType()); - Assertions.assertEquals(PositionAngleType.MEAN, stateCovariance.getPositionAngleType()); - Assertions.assertEquals(initialInertialFrame, stateCovariance.getFrame()); + assertEquals(OrbitType.CARTESIAN, stateCovariance.getOrbitType()); + assertEquals(PositionAngleType.MEAN, stateCovariance.getPositionAngleType()); + assertEquals(initialInertialFrame, stateCovariance.getFrame()); Assertions.assertNull(stateCovariance.getLOF()); - Assertions.assertEquals(initialDate, stateCovariance.getDate()); + assertEquals(initialDate, stateCovariance.getDate()); } public void setUp() { @@ -120,11 +125,11 @@ public static void compareCovariance(final RealMatrix reference, final RealMatri for (int row = 0; row < reference.getRowDimension(); row++) { for (int column = 0; column < reference.getColumnDimension(); column++) { if (reference.getEntry(row, column) == 0) { - Assertions.assertEquals(reference.getEntry(row, column), computed.getEntry(row, column), + assertEquals(reference.getEntry(row, column), computed.getEntry(row, column), threshold); } else { - Assertions.assertEquals(reference.getEntry(row, column), computed.getEntry(row, column), + assertEquals(reference.getEntry(row, column), computed.getEntry(row, column), FastMath.abs(threshold * reference.getEntry(row, column))); } } @@ -961,10 +966,77 @@ void testIssue1052() { Assertions.assertDoesNotThrow(() -> propagator.propagate(initialDate.shiftedBy(1))); // Assert that propagated covariance is in the same LOF as the initial covariance - Assertions.assertEquals(LOFType.QSW, propagatedCovariance.getLOF()); + assertEquals(LOFType.QSW, propagatedCovariance.getLOF()); } + @Test + void testIssue1485CartesianOrbitTypeAndNullAngleType() { + // GIVEN + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final RealMatrix expectedMatrix = MatrixUtils.createRealIdentityMatrix(6); + final PositionAngleType anomalyType = PositionAngleType.MEAN; + final Frame inFrame = FramesFactory.getEME2000(); + final Frame outFrame = FramesFactory.getGCRF(); + final double mu = Constants.EGM96_EARTH_MU; + + final KeplerianOrbit orbit = new KeplerianOrbit(1e7, 0.01, 1., 2., 3., 4., anomalyType, inFrame, date, mu); + + final StateCovariance originalCovariance = + new StateCovariance(expectedMatrix, date, inFrame, OrbitType.CARTESIAN, null); + + // WHEN & THEN + assertDoesNotThrow(()-> originalCovariance.changeCovarianceFrame(orbit, outFrame)); + + // THEN + final StateCovariance convertedCovariance = originalCovariance.changeCovarianceFrame(orbit, outFrame); + assertEquals(originalCovariance.getMatrix().getNorm1(), convertedCovariance.getMatrix().getNorm1(), 1e-15); + } + + @Test + void testIssue1485SameLOF() { + // GIVEN + final LOF lof = LOFType.TNW; + + // WHEN & THEN + doTestIssue1485SameFrameDefinition(lof); + } + + @Test + void testIssue1485SameFrame() { + // GIVEN + final Frame eme2000 = FramesFactory.getEME2000(); + + // WHEN & THEN + doTestIssue1485SameFrameDefinition(eme2000); + } + + void doTestIssue1485SameFrameDefinition(T frameOrLOF) { + // GIVEN + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final RealMatrix expectedMatrix = MatrixUtils.createRealIdentityMatrix(6); + final PositionAngleType anomalyType = PositionAngleType.MEAN; + final Frame frame = FramesFactory.getEME2000(); + final double mu = Constants.EGM96_EARTH_MU; + + final KeplerianOrbit orbit = new KeplerianOrbit(1e7, 0.01, 1., 2., 3., 4., anomalyType, frame, date, mu); + + // WHEN + final StateCovariance originalCovariance; + final StateCovariance covariance; + if (frameOrLOF instanceof LOF) { + originalCovariance = new StateCovariance(expectedMatrix, date, (LOF) frameOrLOF); + covariance = originalCovariance.changeCovarianceFrame(orbit, (LOF) frameOrLOF); + } else { + originalCovariance = new StateCovariance(expectedMatrix, date, (Frame) frameOrLOF, orbit.getType(), anomalyType); + covariance = originalCovariance.changeCovarianceFrame(orbit, (Frame) frameOrLOF); + } + + // THEN + final RealMatrix actualMatrix = covariance.getMatrix(); + assertEquals(0., actualMatrix.subtract(expectedMatrix).getNorm1()); + } + private NumericalPropagator buildDefaultPropagator(final SpacecraftState state, final OrbitType orbitType, final PositionAngleType positionAngleType) { @@ -991,3 +1063,4 @@ private ODEIntegrator buildDefaultODEIntegrator(final Orbit orbit, final OrbitTy } } + diff --git a/src/test/java/org/orekit/propagation/analytical/AbstractAnalyticalPropagatorTest.java b/src/test/java/org/orekit/propagation/analytical/AbstractAnalyticalPropagatorTest.java new file mode 100644 index 0000000000..a74c9c2934 --- /dev/null +++ b/src/test/java/org/orekit/propagation/analytical/AbstractAnalyticalPropagatorTest.java @@ -0,0 +1,87 @@ +/* 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.propagation.analytical; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.attitudes.FrameAlignedProvider; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.DateDetector; +import org.orekit.propagation.events.EventDetector; +import org.orekit.propagation.events.handlers.ContinueOnEvent; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; + + +class AbstractAnalyticalPropagatorTest { + + @Test + void testFinish() { + // GIVEN + final Frame eme2000 = FramesFactory.getEME2000(); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Orbit orbit = new KeplerianOrbit(8000000.0, 0.01, 0.87, 2.44, 0.21, -1.05, PositionAngleType.MEAN, + eme2000, date, Constants.EIGEN5C_EARTH_MU); + final TestAnalyticalPropagator propagator = new TestAnalyticalPropagator(orbit); + final TestHandler handler = new TestHandler(); + propagator.addEventDetector(new DateDetector(AbsoluteDate.ARBITRARY_EPOCH).withHandler(handler)); + // WHEN + propagator.propagate(propagator.getInitialState().getDate().shiftedBy(1.)); + // THEN + Assertions.assertTrue(handler.isFinished); + } + + private static class TestHandler extends ContinueOnEvent { + boolean isFinished = false; + + @Override + public void finish(SpacecraftState finalState, EventDetector detector) { + isFinished = true; + } + } + + private static class TestAnalyticalPropagator extends AbstractAnalyticalPropagator { + + private final Orbit orbit; + + protected TestAnalyticalPropagator(Orbit orbit) { + super(new FrameAlignedProvider(FramesFactory.getGCRF())); + this.orbit = orbit; + resetInitialState(new SpacecraftState(orbit)); + } + + @Override + protected double getMass(AbsoluteDate date) { + return 1; + } + + @Override + protected void resetIntermediateState(SpacecraftState state, boolean forward) { + + } + + @Override + protected Orbit propagateOrbit(AbsoluteDate date) { + return orbit.shiftedBy(date.durationFrom(orbit.getDate())); + } + } +} diff --git a/src/test/java/org/orekit/propagation/analytical/BrouwerLyddaneParametersDerivativesTest.java b/src/test/java/org/orekit/propagation/analytical/BrouwerLyddaneParametersDerivativesTest.java index 182ca56b7e..8532e792d6 100644 --- a/src/test/java/org/orekit/propagation/analytical/BrouwerLyddaneParametersDerivativesTest.java +++ b/src/test/java/org/orekit/propagation/analytical/BrouwerLyddaneParametersDerivativesTest.java @@ -136,10 +136,12 @@ public void testParametersDerivatives() { fillJacobianColumn(dYdPRef, 0, orbitType, h, sM4h, sM3h, sM2h, sM1h, sP1h, sP2h, sP3h, sP4h); + // Verify for (int i = 0; i < 6; ++i) { - Assertions.assertEquals(0.0, (dYdPRef[i][0] - dYdP.getEntry(i, 0)) / dYdPRef[i][0], 1.06e-12); + double error = (dYdPRef[i][0] - dYdP.getEntry(i, 0)) / dYdPRef[i][0]; + Assertions.assertEquals(0.0, error, 2.0e-13); + } - } private void fillJacobianColumn(double[][] jacobian, int column, diff --git a/src/test/java/org/orekit/propagation/analytical/BrouwerLyddanePropagatorTest.java b/src/test/java/org/orekit/propagation/analytical/BrouwerLyddanePropagatorTest.java index 8055be71c7..f979a802c9 100644 --- a/src/test/java/org/orekit/propagation/analytical/BrouwerLyddanePropagatorTest.java +++ b/src/test/java/org/orekit/propagation/analytical/BrouwerLyddanePropagatorTest.java @@ -17,6 +17,10 @@ package org.orekit.propagation.analytical; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.ode.nonstiff.AdaptiveStepsizeIntegrator; import org.hipparchus.ode.nonstiff.DormandPrince853Integrator; @@ -34,6 +38,7 @@ import org.orekit.bodies.CelestialBodyFactory; import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.forces.ForceModel; import org.orekit.forces.drag.DragForce; import org.orekit.forces.drag.IsotropicDrag; @@ -47,6 +52,8 @@ import org.orekit.frames.FramesFactory; import org.orekit.models.earth.atmosphere.DTM2000; import org.orekit.models.earth.atmosphere.data.MarshallSolarActivityFutureEstimation; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.CircularOrbit; import org.orekit.orbits.EquinoctialOrbit; import org.orekit.orbits.KeplerianOrbit; import org.orekit.orbits.Orbit; @@ -55,6 +62,8 @@ import org.orekit.propagation.PropagationType; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.analytical.tle.TLE; +import org.orekit.propagation.analytical.tle.TLEPropagator; import org.orekit.propagation.numerical.NumericalPropagator; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScale; @@ -67,6 +76,92 @@ public class BrouwerLyddanePropagatorTest { private static final AttitudeProvider DEFAULT_LAW = Utils.defaultLaw(); + @Test + public void testIssue947ComputeMeanOrbit() { + // Error case from gitlab issue#947: negative eccentricity when calculating mean orbit + TLE tleOrbit = new TLE("1 43196U 18015E 21055.59816856 .00000894 00000-0 38966-4 0 9996", + "2 43196 97.4662 188.8169 0016935 299.6845 60.2706 15.24746686170319"); + Propagator propagator = TLEPropagator.selectExtrapolator(tleOrbit); + + //Get state at initial date and 3 days before + SpacecraftState tleState = propagator.getInitialState(); + SpacecraftState tleStateAtDate = propagator.propagate(propagator.getInitialState().getDate().shiftedBy(3, TimeUnit.DAYS)); + + //BL mean orbit + double epsilon = 1.0e-12; + int maxIter = 500; + UnnormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getUnnormalizedProvider(5, 0); + + Assertions.assertDoesNotThrow(() -> { + BrouwerLyddanePropagator.computeMeanOrbit(tleState.getOrbit(), provider, + provider.onDate(tleState.getDate()), + BrouwerLyddanePropagator.M2, + epsilon, maxIter); + }); + + Assertions.assertDoesNotThrow(() -> { + BrouwerLyddanePropagator.computeMeanOrbit(tleStateAtDate.getOrbit(), provider, + provider.onDate(tleStateAtDate.getDate()), + BrouwerLyddanePropagator.M2, + epsilon, maxIter); + }); + } + + @Test + public void testIssue947Propagate() { + // Error case from forum thread (jbrunell): negative eccentricity when propagating + final double x1 = -1772.619869591273 * 1000; + final double y1 = -3908.6521138424428 * 1000; + final double z1 = 5266.68093513367 * 1000; + final double dx1 = 6.35969327821623 * 1000; + final double dy1 = -4.165238186695803 * 1000; + final double dz1 = -0.9458311825913897 * 1000; + + final Vector3D pos = new Vector3D(x1, y1, z1); + final Vector3D vel = new Vector3D(dx1, dy1, dz1); + final TimeScale tai = TimeScalesFactory.getTAI(); + final AbsoluteDate date = new AbsoluteDate(2023, 3, 1, 14, 6, 29.639584, tai); + final CircularOrbit orbit = new CircularOrbit(new PVCoordinates(pos, vel), + FramesFactory.getGCRF(), date, + Constants.EGM96_EARTH_MU); + + final BrouwerLyddanePropagator blPropagator = new BrouwerLyddanePropagator(orbit, 1000., + Constants.IERS2010_EARTH_EQUATORIAL_RADIUS, Constants.IERS2010_EARTH_MU, + Constants.EIGEN5C_EARTH_C20, Constants.EIGEN5C_EARTH_C30, + Constants.EIGEN5C_EARTH_C40, Constants.EIGEN5C_EARTH_C50, + BrouwerLyddanePropagator.M2); + final List states = new ArrayList<>(); + blPropagator.setStepHandler(10, states::add); + final AbsoluteDate startDate = new AbsoluteDate(2023, 3, 1, 14, 6, 29.639584, tai); + final AbsoluteDate endDate = new AbsoluteDate(2023, 3, 1, 15, 6, 29.639584, tai); + Assertions.assertDoesNotThrow(() -> blPropagator.propagate(startDate, endDate)); + } + + @Test + public void testIssue947Constructor() { + // Error case from forum thread (nicksakva) : negative eccentricity when constructing + final Frame eme2000 = FramesFactory.getEME2000(); + + final AbsoluteDate t = new AbsoluteDate(2023,06,26,12,0,0.0,TimeScalesFactory.getUTC()); + final double x = 6313554.48504233; + final double y = 2775620.8433687; + final double z = -2111774.8221765; + final double vx = 1575.31305905025; + final double vy = 1786.43351611741; + final double vz = 7042.05214468662; + + final PVCoordinates pv = new PVCoordinates(new Vector3D(x, y, z), new Vector3D(vx, vy, vz)); + final CartesianOrbit orb = new CartesianOrbit(pv, eme2000, t, Constants.EGM96_EARTH_MU); + + Assertions.assertDoesNotThrow(() -> { + new BrouwerLyddanePropagator(orb, Constants.EGM96_EARTH_EQUATORIAL_RADIUS, + Constants.EGM96_EARTH_MU, + Constants.EGM96_EARTH_C20, Constants.EGM96_EARTH_C30, + Constants.EGM96_EARTH_C40, Constants.EGM96_EARTH_C50, + BrouwerLyddanePropagator.M2); + }); + } + @Test public void sameDateCartesian() { @@ -87,17 +182,16 @@ public void sameDateCartesian() { new BrouwerLyddanePropagator(initialOrbit, GravityFieldFactory.getUnnormalizedProvider(provider), BrouwerLyddanePropagator.M2); SpacecraftState finalOrbit = extrapolator.propagate(initDate); - // positions velocity and semi major axis match perfectly + // position, velocity and semi-major axis match almost perfectly Assertions.assertEquals(0.0, - Vector3D.distance(initialOrbit.getPosition(), - finalOrbit.getPosition()), - 4.4e-9); - + Vector3D.distance(initialOrbit.getPosition(), + finalOrbit.getPosition()), + 2.4e-8); Assertions.assertEquals(0.0, - Vector3D.distance(initialOrbit.getPVCoordinates().getVelocity(), - finalOrbit.getPVCoordinates().getVelocity()), - 3.9e-12); - Assertions.assertEquals(0.0, finalOrbit.getA() - initialOrbit.getA(), 0.0); + Vector3D.distance(initialOrbit.getPVCoordinates().getVelocity(), + finalOrbit.getPVCoordinates().getVelocity()), + 1.9e-11); + Assertions.assertEquals(0.0, finalOrbit.getA() - initialOrbit.getA(), 9.4e-10); } @@ -118,15 +212,14 @@ public void sameDateKeplerian() { // positions velocity and semi major axis match perfectly Assertions.assertEquals(0.0, - Vector3D.distance(initialOrbit.getPVCoordinates().getPosition(), - finalOrbit.getPVCoordinates().getPosition()), - 7.4e-9); - + Vector3D.distance(initialOrbit.getPosition(), + finalOrbit.getPosition()), + 1.3e-7); Assertions.assertEquals(0.0, Vector3D.distance(initialOrbit.getPVCoordinates().getVelocity(), finalOrbit.getPVCoordinates().getVelocity()), - 7.8e-12); - Assertions.assertEquals(0.0, finalOrbit.getA() - initialOrbit.getA(), 0.0); + 9.0e-11); + Assertions.assertEquals(0.0, finalOrbit.getA() - initialOrbit.getA(), 1.9e-9); } @@ -172,8 +265,7 @@ public void almostSphericalBody() { SpacecraftState finalOrbitAna = extrapolatorAna.propagate(extrapDate); SpacecraftState finalOrbitKep = extrapolatorKep.propagate(extrapDate); - Assertions.assertEquals(finalOrbitAna.getDate().durationFrom(extrapDate), 0.0, - Utils.epsilonTest); + Assertions.assertEquals(0.0, finalOrbitAna.getDate().durationFrom(extrapDate), 0.0); // comparison of each orbital parameters Assertions.assertEquals(finalOrbitAna.getA(), finalOrbitKep.getA(), 10 * Utils.epsilonTest * finalOrbitKep.getA()); @@ -196,12 +288,8 @@ public void almostSphericalBody() { Assertions.assertEquals(MathUtils.normalizeAngle(finalOrbitAna.getLM(), finalOrbitKep.getLM()), finalOrbitKep.getLM(), Utils.epsilonAngle * FastMath.abs(finalOrbitKep.getLM())); - } - - - @Test public void compareToNumericalPropagation() { @@ -260,15 +348,15 @@ public void compareToNumericalPropagation() { SpacecraftState BLFinalState = BLextrapolator.propagate(initDate.shiftedBy(timeshift)); final KeplerianOrbit BLOrbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(BLFinalState.getOrbit()); - Assertions.assertEquals(NumOrbit.getA(), BLOrbit.getA(), 0.2); - Assertions.assertEquals(NumOrbit.getE(), BLOrbit.getE(), 0.00000028); - Assertions.assertEquals(NumOrbit.getI(), BLOrbit.getI(), 0.00000007); + Assertions.assertEquals(NumOrbit.getA(), BLOrbit.getA(), 0.175); + Assertions.assertEquals(NumOrbit.getE(), BLOrbit.getE(), 3.2e-6); + Assertions.assertEquals(NumOrbit.getI(), BLOrbit.getI(), 6.9e-8); Assertions.assertEquals(MathUtils.normalizeAngle(NumOrbit.getPerigeeArgument(), FastMath.PI), - MathUtils.normalizeAngle(BLOrbit.getPerigeeArgument(), FastMath.PI), 0.0021); + MathUtils.normalizeAngle(BLOrbit.getPerigeeArgument(), FastMath.PI), 0.0053); Assertions.assertEquals(MathUtils.normalizeAngle(NumOrbit.getRightAscensionOfAscendingNode(), FastMath.PI), - MathUtils.normalizeAngle(BLOrbit.getRightAscensionOfAscendingNode(), FastMath.PI), 0.0000013); + MathUtils.normalizeAngle(BLOrbit.getRightAscensionOfAscendingNode(), FastMath.PI), 1.2e-6); Assertions.assertEquals(MathUtils.normalizeAngle(NumOrbit.getTrueAnomaly(), FastMath.PI), - MathUtils.normalizeAngle(BLOrbit.getTrueAnomaly(), FastMath.PI), 0.0021); + MathUtils.normalizeAngle(BLOrbit.getTrueAnomaly(), FastMath.PI), 0.0052); } @Test @@ -320,8 +408,7 @@ public void compareToNumericalPropagationWithDrag() { // Force model final ForceModel holmesFeatherstone = new HolmesFeatherstoneAttractionModel(FramesFactory.getITRF(IERSConventions.IERS_2010, true), provider); - final ForceModel drag = - new DragForce(atmosphere, new IsotropicDrag(1.0, 1.0)); + final ForceModel drag = new DragForce(atmosphere, new IsotropicDrag(1.0, 1.0)); NumPropagator.addForceModel(holmesFeatherstone); NumPropagator.addForceModel(drag); @@ -343,8 +430,8 @@ public void compareToNumericalPropagationWithDrag() { KeplerianOrbit BLOrbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(BLFinalState.getOrbit()); // Verify a and e differences without the drag effect on Brouwer-Lyddane - final double deltaSmaBefore = 20.44; - final double deltaEccBefore = 1.0301e-4; + final double deltaSmaBefore = 15.81; + final double deltaEccBefore = 9.2681e-5; Assertions.assertEquals(NumOrbit.getA(), BLOrbit.getA(), deltaSmaBefore); Assertions.assertEquals(NumOrbit.getE(), BLOrbit.getE(), deltaEccBefore); @@ -358,13 +445,13 @@ public void compareToNumericalPropagationWithDrag() { BLOrbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(BLFinalState.getOrbit()); // Verify a and e differences without the drag effect on Brouwer-Lyddane - final double deltaSmaAfter = 15.66; - final double deltaEccAfter = 1.0297e-4; + final double deltaSmaAfter = 11.04; + final double deltaEccAfter = 9.2639e-5; Assertions.assertEquals(NumOrbit.getA(), BLOrbit.getA(), deltaSmaAfter); Assertions.assertEquals(NumOrbit.getE(), BLOrbit.getE(), deltaEccAfter); + Assertions.assertTrue(deltaSmaAfter < deltaSmaBefore); Assertions.assertTrue(deltaEccAfter < deltaEccBefore); - } @@ -385,7 +472,6 @@ public void compareToNumericalPropagationMeanInitialOrbit() { final Orbit initialOrbit = new KeplerianOrbit(a, e, i, omega, raan, lM, PositionAngleType.TRUE, inertialFrame, initDate, provider.getMu()); - BrouwerLyddanePropagator BLextrapolator = new BrouwerLyddanePropagator(initialOrbit, GravityFieldFactory.getUnnormalizedProvider(provider), PropagationType.MEAN, BrouwerLyddanePropagator.M2); @@ -399,7 +485,6 @@ public void compareToNumericalPropagationMeanInitialOrbit() { // SET UP A REFERENCE NUMERICAL PROPAGATION //_______________________________________________________________________________________________ - // Adaptive step integrator with a minimum step of 0.001 and a maximum step of 1000 final double minStep = 0.001; final double maxstep = 1000.0; @@ -425,23 +510,18 @@ public void compareToNumericalPropagationMeanInitialOrbit() { final SpacecraftState NumFinalState = NumPropagator.propagate(initDate.shiftedBy(timeshift)); final KeplerianOrbit NumOrbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(NumFinalState.getOrbit()); - //_______________________________________________________________________________________________ - // SET UP A BROUWER LYDDANE PROPAGATION - //_______________________________________________________________________________________________ - - Assertions.assertEquals(NumOrbit.getA(), BLOrbit.getA(), 0.17); - Assertions.assertEquals(NumOrbit.getE(), BLOrbit.getE(), 0.00000028); - Assertions.assertEquals(NumOrbit.getI(), BLOrbit.getI(), 0.000004); + // Asserts + Assertions.assertEquals(NumOrbit.getA(), BLOrbit.getA(), 0.174); + Assertions.assertEquals(NumOrbit.getE(), BLOrbit.getE(), 3.2e-6); + Assertions.assertEquals(NumOrbit.getI(), BLOrbit.getI(), 6.9e-8); Assertions.assertEquals(MathUtils.normalizeAngle(NumOrbit.getPerigeeArgument(), FastMath.PI), - MathUtils.normalizeAngle(BLOrbit.getPerigeeArgument(), FastMath.PI), 0.197); + MathUtils.normalizeAngle(BLOrbit.getPerigeeArgument(), FastMath.PI), 0.0053); Assertions.assertEquals(MathUtils.normalizeAngle(NumOrbit.getRightAscensionOfAscendingNode(), FastMath.PI), - MathUtils.normalizeAngle(BLOrbit.getRightAscensionOfAscendingNode(), FastMath.PI), 0.00072); + MathUtils.normalizeAngle(BLOrbit.getRightAscensionOfAscendingNode(), FastMath.PI), 1.2e-6); Assertions.assertEquals(MathUtils.normalizeAngle(NumOrbit.getTrueAnomaly(), FastMath.PI), - MathUtils.normalizeAngle(BLOrbit.getTrueAnomaly(), FastMath.PI), 0.12); - + MathUtils.normalizeAngle(BLOrbit.getTrueAnomaly(), FastMath.PI), 0.0052); } - @Test public void compareToNumericalPropagationResetInitialIntermediate() { @@ -484,6 +564,7 @@ public void compareToNumericalPropagationResetInitialIntermediate() { BLextrapolator2.resetIntermediateState(BLFinalState1, true); final KeplerianOrbit BLOrbit2 = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(BLFinalState2.getOrbit()); + // Asserts Assertions.assertEquals(BLOrbit1.getA(), BLOrbit2.getA(), 0.0); Assertions.assertEquals(BLOrbit1.getE(), BLOrbit2.getE(), 0.0); Assertions.assertEquals(BLOrbit1.getI(), BLOrbit2.getI(), 0.0); @@ -493,7 +574,6 @@ public void compareToNumericalPropagationResetInitialIntermediate() { MathUtils.normalizeAngle(BLOrbit2.getRightAscensionOfAscendingNode(), FastMath.PI), 0.0); Assertions.assertEquals(MathUtils.normalizeAngle(BLOrbit1.getTrueAnomaly(), FastMath.PI), MathUtils.normalizeAngle(BLOrbit2.getTrueAnomaly(), FastMath.PI), 0.0); - } @Test @@ -509,11 +589,10 @@ public void compareConstructors() { final double i = FastMath.toRadians(7); // inclination final double omega = FastMath.toRadians(180); // perigee argument final double raan = FastMath.toRadians(261); // right ascention of ascending node - final double lM = 0; // mean anomaly - final Orbit initialOrbit = new KeplerianOrbit(a, e, i, omega, raan, lM, PositionAngleType.TRUE, + final double lV = 0; // true anomaly + final Orbit initialOrbit = new KeplerianOrbit(a, e, i, omega, raan, lV, PositionAngleType.TRUE, inertialFrame, initDate, provider.getMu()); - BrouwerLyddanePropagator BLPropagator1 = new BrouwerLyddanePropagator(initialOrbit, DEFAULT_LAW, provider.getAe(), provider.getMu(), -1.08263e-3, 2.54e-6, 1.62e-6, 2.3e-7, BrouwerLyddanePropagator.M2); BrouwerLyddanePropagator BLPropagator2 = new BrouwerLyddanePropagator(initialOrbit, @@ -528,7 +607,7 @@ public void compareConstructors() { SpacecraftState BLFinalState3 = BLPropagator3.propagate(initDate.shiftedBy(timeshift)); final KeplerianOrbit BLOrbit3 = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(BLFinalState3.getOrbit()); - + // Asserts Assertions.assertEquals(BLOrbit1.getA(), BLOrbit2.getA(), 0.0); Assertions.assertEquals(BLOrbit1.getE(), BLOrbit2.getE(), 0.0); Assertions.assertEquals(BLOrbit1.getI(), BLOrbit2.getI(), 0.0); @@ -538,6 +617,7 @@ public void compareConstructors() { MathUtils.normalizeAngle(BLOrbit2.getRightAscensionOfAscendingNode(), FastMath.PI), 0.0); Assertions.assertEquals(MathUtils.normalizeAngle(BLOrbit1.getTrueAnomaly(), FastMath.PI), MathUtils.normalizeAngle(BLOrbit2.getTrueAnomaly(), FastMath.PI), 0.0); + Assertions.assertEquals(BLOrbit1.getA(), BLOrbit3.getA(), 0.0); Assertions.assertEquals(BLOrbit1.getE(), BLOrbit3.getE(), 0.0); Assertions.assertEquals(BLOrbit1.getI(), BLOrbit3.getI(), 0.0); @@ -547,54 +627,40 @@ public void compareConstructors() { MathUtils.normalizeAngle(BLOrbit3.getRightAscensionOfAscendingNode(), FastMath.PI), 0.0); Assertions.assertEquals(MathUtils.normalizeAngle(BLOrbit1.getTrueAnomaly(), FastMath.PI), MathUtils.normalizeAngle(BLOrbit3.getTrueAnomaly(), FastMath.PI), 0.0); - } - @Test public void undergroundOrbit() { - Assertions.assertThrows(OrekitException.class, () -> { - // for a semi major axis < equatorial radius - Vector3D position = new Vector3D(7.0e6, 1.0e6, 4.0e6); - Vector3D velocity = new Vector3D(-500.0, 800.0, 100.0); - AbsoluteDate initDate = AbsoluteDate.J2000_EPOCH; - Orbit initialOrbit = new EquinoctialOrbit(new PVCoordinates(position, velocity), - FramesFactory.getEME2000(), initDate, provider.getMu()); - // Extrapolator definition - // ----------------------- - BrouwerLyddanePropagator extrapolator = - new BrouwerLyddanePropagator(initialOrbit, DEFAULT_LAW, provider.getAe(), provider.getMu(), - -1.08263e-3, 2.54e-6, 1.62e-6, 2.3e-7, BrouwerLyddanePropagator.M2); - - // Extrapolation at the initial date - // --------------------------------- - double delta_t = 0.0; - AbsoluteDate extrapDate = initDate.shiftedBy(delta_t); - extrapolator.propagate(extrapDate); + // for a semi major axis < equatorial radius + Vector3D position = new Vector3D(7.0e6, 1.0e6, 4.0e6); + Vector3D velocity = new Vector3D(-500.0, 800.0, 100.0); + AbsoluteDate initDate = AbsoluteDate.J2000_EPOCH; + Orbit initialOrbit = new EquinoctialOrbit(new PVCoordinates(position, velocity), + FramesFactory.getEME2000(), initDate, provider.getMu()); + // Extrapolator definition + // ----------------------- + OrekitException oe = Assertions.assertThrows(OrekitException.class, () -> { + new BrouwerLyddanePropagator(initialOrbit, DEFAULT_LAW, provider.getAe(), provider.getMu(), + -1.08263e-3, 2.54e-6, 1.62e-6, 2.3e-7, BrouwerLyddanePropagator.M2); }); + Assertions.assertTrue(oe.getMessage().contains("trajectory inside the Brillouin sphere (r =")); } @Test public void tooEllipticalOrbit() { - Assertions.assertThrows(OrekitException.class, () -> { - // for an eccentricity too big for the model - AbsoluteDate initDate = AbsoluteDate.J2000_EPOCH; - Orbit initialOrbit = new KeplerianOrbit(67679244.0, 1.0, 1.85850, 2.1, 2.9, - 6.2, PositionAngleType.TRUE, FramesFactory.getEME2000(), - initDate, provider.getMu()); - // Extrapolator definition - // ----------------------- - BrouwerLyddanePropagator extrapolator = - new BrouwerLyddanePropagator(initialOrbit, provider.getAe(), provider.getMu(), - -1.08263e-3, 2.54e-6, 1.62e-6, 2.3e-7, BrouwerLyddanePropagator.M2); - - // Extrapolation at the initial date - // --------------------------------- - double delta_t = 0.0; - AbsoluteDate extrapDate = initDate.shiftedBy(delta_t); - extrapolator.propagate(extrapDate); - }); + // for an eccentricity too big for the model + AbsoluteDate initDate = AbsoluteDate.J2000_EPOCH; + Orbit initialOrbit = new KeplerianOrbit(67679244.0, 1.0, 1.85850, 2.1, 2.9, + 6.2, PositionAngleType.TRUE, FramesFactory.getEME2000(), + initDate, provider.getMu()); + // Extrapolator definition + // ----------------------- + OrekitException oe = Assertions.assertThrows(OrekitException.class, () -> { + new BrouwerLyddanePropagator(initialOrbit, provider.getAe(), provider.getMu(), + -1.08263e-3, 2.54e-6, 1.62e-6, 2.3e-7, BrouwerLyddanePropagator.M2); + }); + Assertions.assertTrue(oe.getMessage().contains("too large eccentricity for propagation model: e =")); } @@ -608,10 +674,10 @@ public void criticalInclination() { final double i = FastMath.acos(1.0 / FastMath.sqrt(5.0)); // critical inclination final double omega = FastMath.toRadians(180); // perigee argument final double raan = FastMath.toRadians(261); // right ascention of ascending node - final double lM = 0; // mean anomaly + final double lV = 0; // true anomaly AbsoluteDate initDate = AbsoluteDate.J2000_EPOCH; - final Orbit initialOrbit = new KeplerianOrbit(a, e, i, omega, raan, lM, PositionAngleType.TRUE, + final Orbit initialOrbit = new KeplerianOrbit(a, e, i, omega, raan, lV, PositionAngleType.TRUE, inertialFrame, initDate, provider.getMu()); // Extrapolator definition @@ -623,70 +689,62 @@ public void criticalInclination() { // --------------------------------- SpacecraftState finalOrbit = extrapolator.propagate(initDate); - // Verify + // Asserts + // ------- Assertions.assertEquals(0.0, - Vector3D.distance(initialOrbit.getPosition(), - finalOrbit.getPosition()), - 1.9e-8); - + Vector3D.distance(initialOrbit.getPosition(), + finalOrbit.getPosition()), + 1.5e-8); Assertions.assertEquals(0.0, - Vector3D.distance(initialOrbit.getPVCoordinates().getVelocity(), - finalOrbit.getPVCoordinates().getVelocity()), - 3.0e-12); - Assertions.assertEquals(0.0, finalOrbit.getA() - initialOrbit.getA(), 0.0); - + Vector3D.distance(initialOrbit.getPVCoordinates().getVelocity(), + finalOrbit.getPVCoordinates().getVelocity()), + 2.7e-12); + Assertions.assertEquals(0.0, finalOrbit.getA() - initialOrbit.getA(), 7.5e-9); } - @Test public void insideEarth() { - Assertions.assertThrows(OrekitException.class, () -> { - // for an eccentricity too big for the model - AbsoluteDate initDate = AbsoluteDate.J2000_EPOCH; - Orbit initialOrbit = new KeplerianOrbit( provider.getAe()-1000.0, 0.01, FastMath.toRadians(10.0), 2.1, 2.9, - 6.2, PositionAngleType.TRUE, FramesFactory.getEME2000(), - initDate, provider.getMu()); - // Extrapolator definition - // ----------------------- - BrouwerLyddanePropagator extrapolator = - new BrouwerLyddanePropagator(initialOrbit, Propagator.DEFAULT_MASS, provider.getAe(), provider.getMu(), - -1.08263e-3, 2.54e-6, 1.62e-6, 2.3e-7); - - // Extrapolation at the initial date - // --------------------------------- - double delta_t = 0.0; - AbsoluteDate extrapDate = initDate.shiftedBy(delta_t); - extrapolator.propagate(extrapDate); + // for an eccentricity too big for the model + final AbsoluteDate initDate = AbsoluteDate.J2000_EPOCH; + final Orbit initialOrbit = new KeplerianOrbit(provider.getAe()-1000.0, 0.01, + FastMath.toRadians(10.0), 2.1, 2.9, + 6.2, PositionAngleType.TRUE, + FramesFactory.getEME2000(), + initDate, provider.getMu()); + // Extrapolator definition + // ----------------------- + OrekitIllegalArgumentException oe = Assertions.assertThrows(OrekitIllegalArgumentException.class, () -> { + new BrouwerLyddanePropagator(initialOrbit, Propagator.DEFAULT_MASS, provider.getAe(), provider.getMu(), + -1.08263e-3, 2.54e-6, 1.62e-6, 2.3e-7); }); + Assertions.assertTrue(oe.getMessage().contains("orbit should be either elliptic with a > 0 and e < 1 or hyperbolic with a < 0 and e > 1")); } @Test public void testUnableToComputeBLMeanParameters() { - Assertions.assertThrows(OrekitException.class, () -> { - - final Frame inertialFrame = FramesFactory.getEME2000(); - AbsoluteDate initDate = AbsoluteDate.J2000_EPOCH.shiftedBy(584.); - - // Initial orbit - final double a = 24396159; // semi major axis in meters - final double e = 0.957; // eccentricity - final double i = FastMath.toRadians(7); // inclination - final double omega = FastMath.toRadians(180); // perigee argument - final double raan = FastMath.toRadians(261); // right ascention of ascending node - final double lM = FastMath.toRadians(0); // mean anomaly - final Orbit initialOrbit = new KeplerianOrbit(a, e, i, omega, raan, lM, PositionAngleType.TRUE, - inertialFrame, initDate, provider.getMu()); - // Extrapolator definition - // ----------------------- - BrouwerLyddanePropagator extrapolator = - new BrouwerLyddanePropagator(initialOrbit, GravityFieldFactory.getUnnormalizedProvider(provider), BrouwerLyddanePropagator.M2); - - // Extrapolation at the initial date - // --------------------------------- - double delta_t = 0.0; - AbsoluteDate extrapDate = initDate.shiftedBy(delta_t); - extrapolator.propagate(extrapDate); + final Frame eci = FramesFactory.getEME2000(); + final AbsoluteDate date = AbsoluteDate.J2000_EPOCH.shiftedBy(584.); + + // Initial orbit + final double a = 24396159; // semi major axis in meters + final double e = 0.9; // eccentricity + final double i = FastMath.toRadians(7); // inclination + final double pa = FastMath.toRadians(180); // perigee argument + final double raan = FastMath.toRadians(261); // right ascention of ascending node + final double lM = FastMath.toRadians(0); // mean anomaly + final Orbit orbit = new KeplerianOrbit(a, e, i, pa, raan, lM, PositionAngleType.MEAN, + eci, date, provider.getMu()); + // Extrapolator definition + // ----------------------- + final UnnormalizedSphericalHarmonicsProvider up = GravityFieldFactory.getUnnormalizedProvider(provider); + final UnnormalizedSphericalHarmonics uh = up.onDate(date); + final double eps = 1.e-13; + final int maxIter = 10; + OrekitException oe = Assertions.assertThrows(OrekitException.class, () -> { + BrouwerLyddanePropagator.computeMeanOrbit(orbit, up, uh, BrouwerLyddanePropagator.M2, + eps, maxIter); }); + Assertions.assertTrue(oe.getMessage().contains("unable to compute Brouwer-Lyddane mean parameters after")); } @Test @@ -724,11 +782,65 @@ public void testMeanOrbit() { }); num.propagate(initialOsculating.getDate().shiftedBy(Constants.JULIAN_DAY)); + // Asserts Assertions.assertEquals(3188.347, oscMax.getResult() - oscMin.getResult(), 1.0e-3); - Assertions.assertEquals( 18.464, meanMax.getResult() - meanMin.getResult(), 1.0e-3); + Assertions.assertEquals( 25.794, meanMax.getResult() - meanMin.getResult(), 1.0e-3); } + @Test + public void testGeostationaryOrbit() { + + final AbsoluteDate date = AbsoluteDate.J2000_EPOCH; + final Frame eci = FramesFactory.getEME2000(); + final UnnormalizedSphericalHarmonicsProvider ushp = GravityFieldFactory.getUnnormalizedProvider(provider); + + // geostationary orbit + final double sma = FastMath.cbrt(Constants.IERS2010_EARTH_MU / + Constants.IERS2010_EARTH_ANGULAR_VELOCITY / + Constants.IERS2010_EARTH_ANGULAR_VELOCITY); + final double ecc = 0.0; + final double inc = 0.0; + final double pa = 0.0; + final double raan = 0.0; + final double lV = 0.0; + final Orbit orbit = new KeplerianOrbit(sma, ecc, inc, pa, raan, lV, + PositionAngleType.TRUE, + eci, date, provider.getMu()); + + // set up a BL propagator from mean orbit + final BrouwerLyddanePropagator bl = new BrouwerLyddanePropagator(orbit, ushp, PropagationType.MEAN, + BrouwerLyddanePropagator.M2); + + // propagate + final SpacecraftState state = bl.propagate(date.shiftedBy(Constants.JULIAN_DAY)); + final KeplerianOrbit orbOsc = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(state.getOrbit()); + + // Check all elements are defined, i.e. no singularity + Assertions.assertTrue(Double.isFinite(orbOsc.getA())); + Assertions.assertTrue(Double.isFinite(orbOsc.getE())); + Assertions.assertTrue(Double.isFinite(orbOsc.getI())); + Assertions.assertTrue(Double.isFinite(orbOsc.getPerigeeArgument())); + Assertions.assertTrue(Double.isFinite(orbOsc.getRightAscensionOfAscendingNode())); + Assertions.assertTrue(Double.isFinite(orbOsc.getTrueAnomaly())); + + // set up a BL propagator from osculating orbit + final BrouwerLyddanePropagator bl2 = new BrouwerLyddanePropagator(orbit, ushp, PropagationType.OSCULATING, + BrouwerLyddanePropagator.M2); + + // propagate + final SpacecraftState state2 = bl2.propagate(date.shiftedBy(Constants.JULIAN_DAY)); + final KeplerianOrbit orbOsc2 = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(state2.getOrbit()); + + // Check all elements are defined, i.e. no singularity + Assertions.assertTrue(Double.isFinite(orbOsc2.getA())); + Assertions.assertTrue(Double.isFinite(orbOsc2.getE())); + Assertions.assertTrue(Double.isFinite(orbOsc2.getI())); + Assertions.assertTrue(Double.isFinite(orbOsc2.getPerigeeArgument())); + Assertions.assertTrue(Double.isFinite(orbOsc2.getRightAscensionOfAscendingNode())); + Assertions.assertTrue(Double.isFinite(orbOsc2.getTrueAnomaly())); + } + @BeforeEach public void setUp() { Utils.setDataRoot("regular-data:atmosphere:potential/icgem-format"); @@ -743,5 +855,3 @@ public void tearDown() { private NormalizedSphericalHarmonicsProvider provider; } - - diff --git a/src/test/java/org/orekit/propagation/analytical/BrouwerLyddaneStateTransitionMatrixTest.java b/src/test/java/org/orekit/propagation/analytical/BrouwerLyddaneStateTransitionMatrixTest.java index 3a518c8fc7..5cba22e834 100644 --- a/src/test/java/org/orekit/propagation/analytical/BrouwerLyddaneStateTransitionMatrixTest.java +++ b/src/test/java/org/orekit/propagation/analytical/BrouwerLyddaneStateTransitionMatrixTest.java @@ -130,7 +130,7 @@ public void testStateJacobian() { for (int j = 0; j < 6; ++j) { if (stateVector[i] != 0) { double error = FastMath.abs((dYdY0.getEntry(i, j) - dYdY0Ref[i][j]) / stateVector[i]) * steps[j]; - Assertions.assertEquals(0, error, 1.42e-13); + Assertions.assertEquals(0, error, 3.0e-13); } } } diff --git a/src/test/java/org/orekit/propagation/analytical/FieldAbstractAnalyticalPropagatorTest.java b/src/test/java/org/orekit/propagation/analytical/FieldAbstractAnalyticalPropagatorTest.java new file mode 100644 index 0000000000..92940b6afc --- /dev/null +++ b/src/test/java/org/orekit/propagation/analytical/FieldAbstractAnalyticalPropagatorTest.java @@ -0,0 +1,95 @@ +/* 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.propagation.analytical; + + +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.attitudes.FrameAlignedProvider; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.*; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.FieldDateDetector; +import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.propagation.events.handlers.FieldContinueOnEvent; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.ParameterDriver; + +import java.util.Collections; +import java.util.List; + +class FieldAbstractAnalyticalPropagatorTest { + + @Test + void testFinish() { + // GIVEN + final Frame eme2000 = FramesFactory.getEME2000(); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Orbit orbit = new KeplerianOrbit(8000000.0, 0.01, 0.87, 2.44, 0.21, -1.05, PositionAngleType.MEAN, + eme2000, date, Constants.EIGEN5C_EARTH_MU); + final TestAnalyticalPropagator propagator = new TestAnalyticalPropagator(orbit); + final TestHandler handler = new TestHandler(); + final ComplexField field = ComplexField.getInstance(); + propagator.addEventDetector(new FieldDateDetector<>(field, FieldAbsoluteDate.getArbitraryEpoch(field)).withHandler(handler)); + // WHEN + propagator.propagate(propagator.getInitialState().getDate().shiftedBy(1.)); + // THEN + Assertions.assertTrue(handler.isFinished); + } + + private static class TestAnalyticalPropagator extends FieldAbstractAnalyticalPropagator { + + protected TestAnalyticalPropagator(Orbit orbit) { + super(ComplexField.getInstance(), new FrameAlignedProvider(FramesFactory.getGCRF())); + resetInitialState(new FieldSpacecraftState<>(new FieldCartesianOrbit<>(getField(), orbit))); + } + + @Override + protected Complex getMass(FieldAbsoluteDate date) { + return Complex.ONE; + } + + @Override + protected void resetIntermediateState(FieldSpacecraftState state, boolean forward) { + + } + + @Override + protected FieldOrbit propagateOrbit(FieldAbsoluteDate date, Complex[] parameters) { + return getInitialState().getOrbit().shiftedBy(date.durationFrom(getStartDate())); + } + + @Override + public List getParametersDrivers() { + return Collections.emptyList(); + } + } + + private static class TestHandler extends FieldContinueOnEvent { + boolean isFinished = false; + + @Override + public void finish(FieldSpacecraftState finalState, FieldEventDetector detector) { + isFinished = true; + } + } +} diff --git a/src/test/java/org/orekit/propagation/analytical/FieldBrouwerLyddanePropagatorTest.java b/src/test/java/org/orekit/propagation/analytical/FieldBrouwerLyddanePropagatorTest.java index 68bc4e870a..a656cb7bb6 100644 --- a/src/test/java/org/orekit/propagation/analytical/FieldBrouwerLyddanePropagatorTest.java +++ b/src/test/java/org/orekit/propagation/analytical/FieldBrouwerLyddanePropagatorTest.java @@ -18,6 +18,7 @@ package org.orekit.propagation.analytical; import java.io.IOException; +import java.util.concurrent.TimeUnit; import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; @@ -65,6 +66,8 @@ import org.orekit.propagation.PropagationType; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.analytical.tle.TLE; +import org.orekit.propagation.analytical.tle.TLEPropagator; import org.orekit.propagation.numerical.FieldNumericalPropagator; import org.orekit.propagation.numerical.NumericalPropagator; import org.orekit.time.AbsoluteDate; @@ -76,12 +79,46 @@ import org.orekit.utils.IERSConventions; public class FieldBrouwerLyddanePropagatorTest { + private static final AttitudeProvider DEFAULT_LAW = Utils.defaultLaw(); + @Test + public void testIssue947() { + doTestIssue947(Binary64Field.getInstance()); + } + + private > void doTestIssue947(Field field) { + // Error case from gitlab issue#947: negative eccentricity when calculating mean orbit + TLE tleOrbit = new TLE("1 43196U 18015E 21055.59816856 .00000894 00000-0 38966-4 0 9996", + "2 43196 97.4662 188.8169 0016935 299.6845 60.2706 15.24746686170319"); + Propagator propagator = TLEPropagator.selectExtrapolator(tleOrbit); + + //Get state at initial date and 3 days before + SpacecraftState tleState = propagator.getInitialState(); + FieldOrbit orbIni = OrbitType.KEPLERIAN.convertToFieldOrbit(field, tleState.getOrbit()); + SpacecraftState tleStateAtDate = propagator.propagate(propagator.getInitialState().getDate().shiftedBy(3, TimeUnit.DAYS)); + FieldOrbit orbAtDate = OrbitType.KEPLERIAN.convertToFieldOrbit(field, tleStateAtDate.getOrbit()); + + UnnormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getUnnormalizedProvider(5, 0); + + Assertions.assertDoesNotThrow(() -> { + FieldBrouwerLyddanePropagator.computeMeanOrbit(orbIni, provider, + provider.onDate(tleState.getDate()), + BrouwerLyddanePropagator.M2); + }); + + Assertions.assertDoesNotThrow(() -> { + FieldBrouwerLyddanePropagator.computeMeanOrbit(orbAtDate, provider, + provider.onDate(tleStateAtDate.getDate()), + BrouwerLyddanePropagator.M2); + }); + } + @Test public void sameDateCartesian() { doSameDateCartesian(Binary64Field.getInstance()); } + private > void doSameDateCartesian(Field field) { T zero = field.getZero(); @@ -105,23 +142,24 @@ private > void doSameDateCartesian(Field fi // positions velocity and semi major axis match perfectly Assertions.assertEquals(0.0, - FieldVector3D.distance(initialOrbit.getPosition(), - finalOrbit.getPosition()).getReal(), - 5.8e-9); - + FieldVector3D.distance(initialOrbit.getPosition(), + finalOrbit.getPosition()).getReal(), + 1.9e-8); Assertions.assertEquals(0.0, - FieldVector3D.distance(initialOrbit.getPVCoordinates().getVelocity(), - finalOrbit.getPVCoordinates().getVelocity()).getReal(), - 4.6e-12); - Assertions.assertEquals(0.0, finalOrbit.getA().getReal() - initialOrbit.getA().getReal(), 0.0); + FieldVector3D.distance(initialOrbit.getPVCoordinates().getVelocity(), + finalOrbit.getPVCoordinates().getVelocity()).getReal(), + 1.2e-11); + Assertions.assertEquals(0.0, + finalOrbit.getA().getReal() - initialOrbit.getA().getReal(), + 9.4e-10); } - @Test public void sameDateKeplerian() { doSameDateKeplerian(Binary64Field.getInstance()); } + private > void doSameDateKeplerian(Field field) { T zero = field.getZero(); @@ -140,22 +178,23 @@ private > void doSameDateKeplerian(Field fi // positions velocity and semi major axis match perfectly Assertions.assertEquals(0.0, - FieldVector3D.distance(initialOrbit.getPVCoordinates().getPosition(), - finalOrbit.getPVCoordinates().getPosition()).getReal(), - 7.4e-9); - + FieldVector3D.distance(initialOrbit.getPVCoordinates().getPosition(), + finalOrbit.getPVCoordinates().getPosition()).getReal(), + 6.7e-7); Assertions.assertEquals(0.0, - FieldVector3D.distance(initialOrbit.getPVCoordinates().getVelocity(), - finalOrbit.getPVCoordinates().getVelocity()).getReal(), - 7.8e-12); - Assertions.assertEquals(0.0, finalOrbit.getA().getReal() - initialOrbit.getA().getReal(), 0.0); + FieldVector3D.distance(initialOrbit.getPVCoordinates().getVelocity(), + finalOrbit.getPVCoordinates().getVelocity()).getReal(), + 4.2e-10); + Assertions.assertEquals(0.0, + finalOrbit.getA().getReal() - initialOrbit.getA().getReal(), + 9.4e-9); } - @Test public void almostSphericalBody() { doAlmostSphericalBody(Binary64Field.getInstance()); } + private > void doAlmostSphericalBody(Field field) { T zero = field.getZero(); @@ -228,11 +267,11 @@ private > void doAlmostSphericalBody(Field } - @Test public void compareToNumericalPropagation() { doCompareToNumericalPropagation(Binary64Field.getInstance()); } + private > void doCompareToNumericalPropagation(Field field) { T zero = field.getZero(); @@ -294,16 +333,15 @@ private > void doCompareToNumericalPropagation FieldSpacecraftState BLFinalState = BLextrapolator.propagate(initDate.shiftedBy(timeshift)); final KeplerianOrbit BLOrbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(BLFinalState.getOrbit().toOrbit()); - - Assertions.assertEquals(NumOrbit.getA(), BLOrbit.getA(), 0.2); - Assertions.assertEquals(NumOrbit.getE(), BLOrbit.getE(), 0.00000028); - Assertions.assertEquals(NumOrbit.getI(), BLOrbit.getI(), 0.00000007); + Assertions.assertEquals(NumOrbit.getA(), BLOrbit.getA(), 0.175); + Assertions.assertEquals(NumOrbit.getE(), BLOrbit.getE(), 3.2e-6); + Assertions.assertEquals(NumOrbit.getI(), BLOrbit.getI(), 6.9e-8); Assertions.assertEquals(MathUtils.normalizeAngle(NumOrbit.getPerigeeArgument(), FastMath.PI), - MathUtils.normalizeAngle(BLOrbit.getPerigeeArgument(), FastMath.PI), 0.0021); + MathUtils.normalizeAngle(BLOrbit.getPerigeeArgument(), FastMath.PI), 0.0053); Assertions.assertEquals(MathUtils.normalizeAngle(NumOrbit.getRightAscensionOfAscendingNode(), FastMath.PI), - MathUtils.normalizeAngle(BLOrbit.getRightAscensionOfAscendingNode(), FastMath.PI), 0.0000013); + MathUtils.normalizeAngle(BLOrbit.getRightAscensionOfAscendingNode(), FastMath.PI), 1.2e-6); Assertions.assertEquals(MathUtils.normalizeAngle(NumOrbit.getTrueAnomaly(), FastMath.PI), - MathUtils.normalizeAngle(BLOrbit.getTrueAnomaly(), FastMath.PI), 0.0021); + MathUtils.normalizeAngle(BLOrbit.getTrueAnomaly(), FastMath.PI), 0.0052); } @Test @@ -362,8 +400,7 @@ private > void doCompareToNumericalPropagation // Force model final ForceModel holmesFeatherstone = new HolmesFeatherstoneAttractionModel(FramesFactory.getITRF(IERSConventions.IERS_2010, true), provider); - final ForceModel drag = - new DragForce(atmosphere, new IsotropicDrag(1.0, 1.0)); + final ForceModel drag = new DragForce(atmosphere, new IsotropicDrag(1.0, 1.0)); NumPropagator.addForceModel(holmesFeatherstone); NumPropagator.addForceModel(drag); @@ -385,8 +422,8 @@ private > void doCompareToNumericalPropagation KeplerianOrbit BLOrbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(BLFinalState.getOrbit().toOrbit()); // Verify a and e differences without the drag effect on Brouwer-Lyddane - final double deltaSmaBefore = 20.44; - final double deltaEccBefore = 1.0301e-4; + final double deltaSmaBefore = 15.81; + final double deltaEccBefore = 9.2681e-5; Assertions.assertEquals(NumOrbit.getA(), BLOrbit.getA(), deltaSmaBefore); Assertions.assertEquals(NumOrbit.getE(), BLOrbit.getE(), deltaEccBefore); @@ -400,8 +437,8 @@ private > void doCompareToNumericalPropagation BLOrbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(BLFinalState.getOrbit().toOrbit()); // Verify a and e differences without the drag effect on Brouwer-Lyddane - final double deltaSmaAfter = 15.66; - final double deltaEccAfter = 1.0297e-4; + final double deltaSmaAfter = 11.04; + final double deltaEccAfter = 9.2639e-5; Assertions.assertEquals(NumOrbit.getA(), BLOrbit.getA(), deltaSmaAfter); Assertions.assertEquals(NumOrbit.getE(), BLOrbit.getE(), deltaEccAfter); Assertions.assertTrue(deltaSmaAfter < deltaSmaBefore); @@ -414,6 +451,7 @@ private > void doCompareToNumericalPropagation public void compareToNumericalPropagationMeanInitialOrbit() { doCompareToNumericalPropagationMeanInitialOrbit(Binary64Field.getInstance()); } + private > void doCompareToNumericalPropagationMeanInitialOrbit(Field field) { T zero = field.getZero(); @@ -422,8 +460,7 @@ private > void doCompareToNumericalPropagation FieldAbsoluteDate initDate = date.shiftedBy(584.); double timeshift = 60000. ; - - // Initial orbit + // Initial orbit final double a = 24396159; // semi major axis in meters final double e = 0.01; // eccentricity final double i = FastMath.toRadians(7); // inclination @@ -447,7 +484,6 @@ private > void doCompareToNumericalPropagation // SET UP A REFERENCE NUMERICAL PROPAGATION //_______________________________________________________________________________________________ - // Adaptive step integrator with a minimum step of 0.001 and a maximum step of 1000 final double minStep = 0.001; final double maxstep = 1000.0; @@ -473,26 +509,23 @@ private > void doCompareToNumericalPropagation final SpacecraftState NumFinalState = NumPropagator.propagate(initDate.toAbsoluteDate().shiftedBy(timeshift)); final KeplerianOrbit NumOrbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(NumFinalState.getOrbit()); - //_______________________________________________________________________________________________ - // SET UP A BROUWER LYDDANE PROPAGATION - //_______________________________________________________________________________________________ - - Assertions.assertEquals(NumOrbit.getA(), BLOrbit.getA(), 0.17); - Assertions.assertEquals(NumOrbit.getE(), BLOrbit.getE(), 0.00000028); - Assertions.assertEquals(NumOrbit.getI(), BLOrbit.getI(), 0.000004); + // Asserts + Assertions.assertEquals(NumOrbit.getA(), BLOrbit.getA(), 0.174); + Assertions.assertEquals(NumOrbit.getE(), BLOrbit.getE(), 3.2e-6); + Assertions.assertEquals(NumOrbit.getI(), BLOrbit.getI(), 6.9e-8); Assertions.assertEquals(MathUtils.normalizeAngle(NumOrbit.getPerigeeArgument(), FastMath.PI), - MathUtils.normalizeAngle(BLOrbit.getPerigeeArgument(), FastMath.PI), 0.197); + MathUtils.normalizeAngle(BLOrbit.getPerigeeArgument(), FastMath.PI), 0.0053); Assertions.assertEquals(MathUtils.normalizeAngle(NumOrbit.getRightAscensionOfAscendingNode(), FastMath.PI), - MathUtils.normalizeAngle(BLOrbit.getRightAscensionOfAscendingNode(), FastMath.PI), 0.00072); + MathUtils.normalizeAngle(BLOrbit.getRightAscensionOfAscendingNode(), FastMath.PI), 1.2e-6); Assertions.assertEquals(MathUtils.normalizeAngle(NumOrbit.getTrueAnomaly(), FastMath.PI), - MathUtils.normalizeAngle(BLOrbit.getTrueAnomaly(), FastMath.PI), 0.12); + MathUtils.normalizeAngle(BLOrbit.getTrueAnomaly(), FastMath.PI), 0.0052); } - @Test public void compareToNumericalPropagationResetInitialIntermediate() { doCompareToNumericalPropagationResetInitialIntermediate(Binary64Field.getInstance()); } + private > void doCompareToNumericalPropagationResetInitialIntermediate(Field field) { T zero = field.getZero(); @@ -553,11 +586,11 @@ private > void doCompareToNumericalPropagation } - @Test public void compareConstructors() { doCompareConstructors(Binary64Field.getInstance()); } + private > void doCompareConstructors(Field field) { T zero = field.getZero(); @@ -593,7 +626,6 @@ private > void doCompareConstructors(Field FieldSpacecraftState BLFinalState3 = BLPropagator3.propagate(initDate.shiftedBy(timeshift)); final KeplerianOrbit BLOrbit3 = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(BLFinalState3.getOrbit().toOrbit()); - Assertions.assertEquals(BLOrbit1.getA(), BLOrbit2.getA(), 0.0); Assertions.assertEquals(BLOrbit1.getE(), BLOrbit2.getE(), 0.0); Assertions.assertEquals(BLOrbit1.getI(), BLOrbit2.getI(), 0.0); @@ -603,6 +635,7 @@ private > void doCompareConstructors(Field MathUtils.normalizeAngle(BLOrbit2.getRightAscensionOfAscendingNode(), FastMath.PI), 0.0); Assertions.assertEquals(MathUtils.normalizeAngle(BLOrbit1.getTrueAnomaly(), FastMath.PI), MathUtils.normalizeAngle(BLOrbit2.getTrueAnomaly(), FastMath.PI), 0.0); + Assertions.assertEquals(BLOrbit1.getA(), BLOrbit3.getA(), 0.0); Assertions.assertEquals(BLOrbit1.getE(), BLOrbit3.getE(), 0.0); Assertions.assertEquals(BLOrbit1.getI(), BLOrbit3.getI(), 0.0); @@ -615,11 +648,11 @@ private > void doCompareConstructors(Field } - @Test public void undergroundOrbit() { doUndergroundOrbit(Binary64Field.getInstance()); } + private > void doUndergroundOrbit(Field field) { T zero = field.getZero(); @@ -651,11 +684,11 @@ private > void doUndergroundOrbit(Field fie } } - @Test public void tooEllipticalOrbit() { doTooEllipticalOrbit(Binary64Field.getInstance()); } + private > void doTooEllipticalOrbit(Field field) { // for an eccentricity too big for the model T zero = field.getZero(); @@ -682,11 +715,11 @@ private > void doTooEllipticalOrbit(Field f } } - @Test public void criticalInclination() { doCriticalInclination(Binary64Field.getInstance()); } + private > void doCriticalInclination(Field field) { final Frame inertialFrame = FramesFactory.getEME2000(); @@ -694,15 +727,15 @@ private > void doCriticalInclination(Field // Initial orbit final double a = 24396159; // semi major axis in meters final double e = 0.01; // eccentricity - final double i = FastMath.toRadians(7); // inclination + final double i = FastMath.acos(1.0 / FastMath.sqrt(5.0)); // critical inclination final double omega = FastMath.toRadians(180); // perigee argument final double raan = FastMath.toRadians(261); // right ascention of ascending node - final double lM = 0; // mean anomaly + final double lV = 0; // true anomaly T zero = field.getZero(); FieldAbsoluteDate initDate = new FieldAbsoluteDate<>(field); final FieldOrbit initialOrbit = new FieldKeplerianOrbit<>(zero.add(a), zero.add(e), zero.add(i), zero.add(omega), - zero.add(raan), zero.add(lM), PositionAngleType.TRUE, + zero.add(raan), zero.add(lV), PositionAngleType.TRUE, inertialFrame, initDate, zero.add(provider.getMu())); // Extrapolator definition @@ -714,56 +747,52 @@ private > void doCriticalInclination(Field // --------------------------------- final FieldSpacecraftState finalOrbit = extrapolator.propagate(initDate); - // Verify + // Asserts + // ------- Assertions.assertEquals(0.0, - FieldVector3D.distance(initialOrbit.getPosition(), - finalOrbit.getPosition()).getReal(), - 7.0e-8); - + FieldVector3D.distance(initialOrbit.getPosition(), + finalOrbit.getPosition()).getReal(), + 1.5e-8); Assertions.assertEquals(0.0, - FieldVector3D.distance(initialOrbit.getPVCoordinates().getVelocity(), - finalOrbit.getPVCoordinates().getVelocity()).getReal(), - 1.2e-11); - - Assertions.assertEquals(0.0, finalOrbit.getA().getReal() - initialOrbit.getA().getReal(), 0.0); - + FieldVector3D.distance(initialOrbit.getPVCoordinates().getVelocity(), + finalOrbit.getPVCoordinates().getVelocity()).getReal(), + 2.7e-12); + Assertions.assertEquals(0.0, + finalOrbit.getA().getReal() - initialOrbit.getA().getReal(), + 7.5e-9); } @Test public void testUnableToComputeBLMeanParameters() { - Assertions.assertThrows(OrekitException.class, () -> { + OrekitException oe = Assertions.assertThrows(OrekitException.class, () -> { doTestUnableToComputeBLMeanParameters(Binary64Field.getInstance()); }); + Assertions.assertTrue(oe.getMessage().contains("unable to compute Brouwer-Lyddane mean parameters after")); } private > void doTestUnableToComputeBLMeanParameters(Field field) { - - T zero = field.getZero(); - FieldAbsoluteDate date = new FieldAbsoluteDate<>(field); - final Frame inertialFrame = FramesFactory.getEME2000(); - FieldAbsoluteDate initDate = date.shiftedBy(584.); + final Frame eci = FramesFactory.getEME2000(); + final AbsoluteDate date = AbsoluteDate.J2000_EPOCH.shiftedBy(584.); // Initial orbit final double a = 24396159; // semi major axis in meters - final double e = 0.957; // eccentricity + final double e = 0.9; // eccentricity final double i = FastMath.toRadians(7); // inclination - final double omega = FastMath.toRadians(180); // perigee argument + final double pa = FastMath.toRadians(180); // perigee argument final double raan = FastMath.toRadians(261); // right ascention of ascending node final double lM = FastMath.toRadians(0); // mean anomaly - final FieldOrbit initialOrbit = new FieldKeplerianOrbit<>(zero.add(a), zero.add(e), zero.add(i), zero.add(omega), - zero.add(raan), zero.add(lM), PositionAngleType.TRUE, - inertialFrame, initDate, zero.add(provider.getMu())); - - // Extrapolator definition - // ----------------------- - final FieldBrouwerLyddanePropagator blField = new FieldBrouwerLyddanePropagator<>(initialOrbit, GravityFieldFactory.getUnnormalizedProvider(provider), BrouwerLyddanePropagator.M2); - - // Extrapolation at the initial date - // --------------------------------- - T delta_t = zero; - FieldAbsoluteDate extrapDate = initDate.shiftedBy(delta_t); - blField.propagate(extrapDate); - + final FieldOrbit orbit = new FieldKeplerianOrbit<>(field, + new KeplerianOrbit(a, e, i, pa, raan, lM, + PositionAngleType.MEAN, + eci, date, provider.getMu())); + // Mean orbit computation + // ---------------------- + final UnnormalizedSphericalHarmonicsProvider up = GravityFieldFactory.getUnnormalizedProvider(provider); + final UnnormalizedSphericalHarmonics uh = up.onDate(date); + final double eps = 1.e-13; + final int maxIter = 10; + FieldBrouwerLyddanePropagator.computeMeanOrbit(orbit, up, uh, BrouwerLyddanePropagator.M2, + eps, maxIter); } @Test @@ -812,7 +841,7 @@ private > void doTestMeanComparisonWithNonFiel Assertions.assertEquals(finalOrbitFieldReal.getA(), finalOrbit.getA(), Double.MIN_VALUE); Assertions.assertEquals(finalOrbitFieldReal.getE(), finalOrbit.getE(), Double.MIN_VALUE); Assertions.assertEquals(finalOrbitFieldReal.getI(), finalOrbit.getI(), Double.MIN_VALUE); - Assertions.assertEquals(finalOrbitFieldReal.getRightAscensionOfAscendingNode(), + finalOrbit.getRightAscensionOfAscendingNode(), Double.MIN_VALUE); + Assertions.assertEquals(finalOrbitFieldReal.getRightAscensionOfAscendingNode(), finalOrbit.getRightAscensionOfAscendingNode(), Double.MIN_VALUE); Assertions.assertEquals(finalOrbitFieldReal.getPerigeeArgument(), finalOrbit.getPerigeeArgument(), Double.MIN_VALUE); Assertions.assertEquals(finalOrbitFieldReal.getMeanAnomaly(), finalOrbit.getMeanAnomaly(), Double.MIN_VALUE); Assertions.assertEquals(0.0, finalOrbitFieldReal.getPosition().distance(finalOrbit.getPosition()), Double.MIN_VALUE); @@ -864,7 +893,7 @@ private > void doTestOsculatingComparisonWithN Assertions.assertEquals(finalOrbitFieldReal.getA(), finalOrbit.getA(), Double.MIN_VALUE); Assertions.assertEquals(finalOrbitFieldReal.getE(), finalOrbit.getE(), Double.MIN_VALUE); Assertions.assertEquals(finalOrbitFieldReal.getI(), finalOrbit.getI(), Double.MIN_VALUE); - Assertions.assertEquals(finalOrbitFieldReal.getRightAscensionOfAscendingNode(), + finalOrbit.getRightAscensionOfAscendingNode(), Double.MIN_VALUE); + Assertions.assertEquals(finalOrbitFieldReal.getRightAscensionOfAscendingNode(), finalOrbit.getRightAscensionOfAscendingNode(), Double.MIN_VALUE); Assertions.assertEquals(finalOrbitFieldReal.getPerigeeArgument(), finalOrbit.getPerigeeArgument(), Double.MIN_VALUE); Assertions.assertEquals(finalOrbitFieldReal.getMeanAnomaly(), finalOrbit.getMeanAnomaly(), Double.MIN_VALUE); Assertions.assertEquals(0.0, finalOrbitFieldReal.getPosition().distance(finalOrbit.getPosition()), Double.MIN_VALUE); @@ -907,17 +936,80 @@ private > void doTestMeanOrbit(Field field) oscMin.increment(osc.getA().getReal()); oscMax.increment(osc.getA().getReal()); // compute mean orbit at current date (this is what we test) - final FieldOrbit mean = FieldBrouwerLyddanePropagator.computeMeanOrbit(state.getOrbit(), ushp, ush, BrouwerLyddanePropagator.M2); + final FieldOrbit mean = FieldBrouwerLyddanePropagator.computeMeanOrbit(state.getOrbit(), + ushp, ush, + BrouwerLyddanePropagator.M2); meanMin.increment(mean.getA().getReal()); meanMax.increment(mean.getA().getReal()); }); num.propagate(initialOsculating.getDate().shiftedBy(Constants.JULIAN_DAY)); Assertions.assertEquals(3188.347, oscMax.getResult() - oscMin.getResult(), 1.0e-3); - Assertions.assertEquals( 18.464, meanMax.getResult() - meanMin.getResult(), 1.0e-3); + Assertions.assertEquals( 25.794, meanMax.getResult() - meanMin.getResult(), 1.0e-3); } + @Test + public void testGeostationaryOrbit() { + doTestGeostationaryOrbit(Binary64Field.getInstance()); + } + + private > void doTestGeostationaryOrbit(Field field) { + + final Frame eci = FramesFactory.getEME2000(); + final UnnormalizedSphericalHarmonicsProvider ushp = GravityFieldFactory.getUnnormalizedProvider(provider); + + T zero = field.getZero(); + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field); + + // geostationary orbit + final double sma = FastMath.cbrt(Constants.IERS2010_EARTH_MU / + Constants.IERS2010_EARTH_ANGULAR_VELOCITY / + Constants.IERS2010_EARTH_ANGULAR_VELOCITY); + final double ecc = 0.0; + final double inc = 0.0; + final double pa = 0.0; + final double raan = 0.0; + final double lV = 0.0; + final FieldOrbit orbit = new FieldKeplerianOrbit<>(zero.add(sma), zero.add(ecc), zero.add(inc), + zero.add(pa), zero.add(raan), zero.add(lV), + PositionAngleType.TRUE, + eci, date, zero.add(provider.getMu())); + + // set up a BL propagator from mean orbit + FieldBrouwerLyddanePropagator fbl = + new FieldBrouwerLyddanePropagator<>(orbit, ushp, PropagationType.MEAN, + BrouwerLyddanePropagator.M2); + + // propagate + final FieldSpacecraftState state = fbl.propagate(date.shiftedBy(Constants.JULIAN_DAY)); + final KeplerianOrbit orbOsc = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(state.getOrbit().toOrbit()); + + // Check all elements are defined, i.e. no singularity + Assertions.assertTrue(Double.isFinite(orbOsc.getA())); + Assertions.assertTrue(Double.isFinite(orbOsc.getE())); + Assertions.assertTrue(Double.isFinite(orbOsc.getI())); + Assertions.assertTrue(Double.isFinite(orbOsc.getPerigeeArgument())); + Assertions.assertTrue(Double.isFinite(orbOsc.getRightAscensionOfAscendingNode())); + Assertions.assertTrue(Double.isFinite(orbOsc.getTrueAnomaly())); + + // set up a BL propagator from mean orbit + FieldBrouwerLyddanePropagator fbl2 = + new FieldBrouwerLyddanePropagator<>(orbit, ushp, PropagationType.OSCULATING, + BrouwerLyddanePropagator.M2); + + // propagate + final FieldSpacecraftState state2 = fbl2.propagate(date.shiftedBy(Constants.JULIAN_DAY)); + final KeplerianOrbit orbOsc2 = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(state2.getOrbit().toOrbit()); + + // Check all elements are defined, i.e. no singularity + Assertions.assertTrue(Double.isFinite(orbOsc2.getA())); + Assertions.assertTrue(Double.isFinite(orbOsc2.getE())); + Assertions.assertTrue(Double.isFinite(orbOsc2.getI())); + Assertions.assertTrue(Double.isFinite(orbOsc2.getPerigeeArgument())); + Assertions.assertTrue(Double.isFinite(orbOsc2.getRightAscensionOfAscendingNode())); + Assertions.assertTrue(Double.isFinite(orbOsc2.getTrueAnomaly())); + } @BeforeEach public void setUp() { diff --git a/src/test/java/org/orekit/propagation/analytical/tle/generation/FixedPointTleGenerationAlgorithmTest.java b/src/test/java/org/orekit/propagation/analytical/tle/generation/FixedPointTleGenerationAlgorithmTest.java index eba949fd33..7b371d666c 100644 --- a/src/test/java/org/orekit/propagation/analytical/tle/generation/FixedPointTleGenerationAlgorithmTest.java +++ b/src/test/java/org/orekit/propagation/analytical/tle/generation/FixedPointTleGenerationAlgorithmTest.java @@ -20,7 +20,6 @@ import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.DSFactory; import org.hipparchus.analysis.differentiation.DerivativeStructure; -import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.Binary64Field; import org.hipparchus.util.FastMath; import org.junit.jupiter.api.Assertions; @@ -39,11 +38,9 @@ import org.orekit.propagation.analytical.tle.FieldTLE; import org.orekit.propagation.analytical.tle.FieldTLEPropagator; import org.orekit.propagation.analytical.tle.TLE; -import org.orekit.propagation.analytical.tle.TLEConstants; import org.orekit.propagation.analytical.tle.TLEPropagator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.Constants; -import org.orekit.utils.PVCoordinates; import org.orekit.utils.TimeStampedFieldPVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; diff --git a/src/test/java/org/orekit/propagation/conversion/AbstractFieldIntegratorBuilderTest.java b/src/test/java/org/orekit/propagation/conversion/AbstractFieldIntegratorBuilderTest.java new file mode 100644 index 0000000000..e82e8c5a38 --- /dev/null +++ b/src/test/java/org/orekit/propagation/conversion/AbstractFieldIntegratorBuilderTest.java @@ -0,0 +1,57 @@ +/* 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.propagation.conversion; + +import org.hipparchus.Field; +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.ode.FieldODEIntegrator; +import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaFieldIntegrator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.FieldCartesianOrbit; +import org.orekit.orbits.OrbitType; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.AbsolutePVCoordinates; +import org.orekit.utils.FieldAbsolutePVCoordinates; + +class AbstractFieldIntegratorBuilderTest { + + @Test + void testBuildIntegrator() { + // GIVEN + final AbsolutePVCoordinates absolutePVCoordinates = new AbsolutePVCoordinates(FramesFactory.getGCRF(), + AbsoluteDate.ARBITRARY_EPOCH, Vector3D.MINUS_I, Vector3D.MINUS_K); + final double step = 10; + final Field field = ComplexField.getInstance(); + final FieldAbsolutePVCoordinates fieldAbsolutePVCoordinates = new FieldAbsolutePVCoordinates<>(field, absolutePVCoordinates); + final ClassicalRungeKuttaFieldIntegratorBuilder builder = new ClassicalRungeKuttaFieldIntegratorBuilder<>(field.getOne().newInstance(step)); + // WHEN + final FieldODEIntegrator integrator = builder.buildIntegrator(fieldAbsolutePVCoordinates); + // THEN + final ClassicalRungeKuttaFieldIntegrator actualIntegrator = (ClassicalRungeKuttaFieldIntegrator) integrator; + final CartesianOrbit orbit = new CartesianOrbit(absolutePVCoordinates.getPVCoordinates(), absolutePVCoordinates.getFrame(), + absolutePVCoordinates.getDate(), 1.); + final FieldCartesianOrbit fieldOrbit = new FieldCartesianOrbit<>(field, orbit); + final ClassicalRungeKuttaFieldIntegrator expectedIntegrator = (ClassicalRungeKuttaFieldIntegrator) builder.buildIntegrator(fieldOrbit, OrbitType.CARTESIAN); + Assertions.assertEquals(expectedIntegrator.getCurrentSignedStepsize(), actualIntegrator.getCurrentSignedStepsize()); + } + +} diff --git a/src/test/java/org/orekit/propagation/conversion/AbstractPropagatorBuilderTest.java b/src/test/java/org/orekit/propagation/conversion/AbstractPropagatorBuilderTest.java index ab438d26be..99d7afda3d 100644 --- a/src/test/java/org/orekit/propagation/conversion/AbstractPropagatorBuilderTest.java +++ b/src/test/java/org/orekit/propagation/conversion/AbstractPropagatorBuilderTest.java @@ -16,8 +16,7 @@ */ package org.orekit.propagation.conversion; -import java.util.List; - +import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.orekit.estimation.Context; @@ -30,8 +29,12 @@ import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.Propagator; import org.orekit.propagation.analytical.KeplerianPropagator; +import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriversList; import org.orekit.utils.ParameterDriversList.DelegatingDriver; +import org.orekit.utils.TimeStampedPVCoordinates; + +import java.util.List; import static org.orekit.Utils.assertParametersDriversValues; @@ -49,6 +52,7 @@ public void testResetOrbit() { final AbstractPropagatorBuilder propagatorBuilder = new AbstractPropagatorBuilder(initialOrbit, PositionAngleType.TRUE, 10., true) { @Override + @Deprecated public PropagatorBuilder copy() { return null; } @@ -113,6 +117,14 @@ public static void assertPropagatorBuilder Assertions.assertEquals(expected.getPositionScale(), actual.getPositionScale()); Assertions.assertEquals(expected.getInitialOrbitDate(), actual.getInitialOrbitDate()); Assertions.assertEquals(expected.getAdditionalDerivativesProviders(), actual.getAdditionalDerivativesProviders()); + + // Verify that the propagations give the same results + AbsoluteDate targetEpoch = expected.getInitialOrbitDate().shiftedBy(7200.0); + TimeStampedPVCoordinates expectedCoordinates = expected.buildPropagator().propagate(targetEpoch).getPVCoordinates(); + TimeStampedPVCoordinates actualCoordinates = actual.buildPropagator().propagate(targetEpoch).getPVCoordinates(); + Assertions.assertEquals(0.0, Vector3D.distance(expectedCoordinates.getPosition(), actualCoordinates.getPosition())); + Assertions.assertEquals(0.0, Vector3D.distance(expectedCoordinates.getVelocity(), actualCoordinates.getVelocity())); + } } diff --git a/src/test/java/org/orekit/propagation/conversion/AbstractVariableStepFieldIntegratorBuilderTest.java b/src/test/java/org/orekit/propagation/conversion/AbstractVariableStepFieldIntegratorBuilderTest.java new file mode 100644 index 0000000000..84e95cd58c --- /dev/null +++ b/src/test/java/org/orekit/propagation/conversion/AbstractVariableStepFieldIntegratorBuilderTest.java @@ -0,0 +1,45 @@ +package org.orekit.propagation.conversion; + +import org.hipparchus.Field; +import org.hipparchus.ode.AbstractFieldIntegrator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.numerical.NumericalPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; + +class AbstractVariableStepFieldIntegratorBuilderTest { + + @Test + void testGetTolerances() { + // GIVEN + final Orbit orbit = new KeplerianOrbit(7e6, 0.1, 1, 2, 3, 4, PositionAngleType.ECCENTRIC, FramesFactory.getGCRF(), + AbsoluteDate.ARBITRARY_EPOCH, Constants.EGM96_EARTH_MU); + final double dP = 1.; + final double dV = 0.001; + final TestIntegratorBuilder integratorBuilder = new TestIntegratorBuilder(0., 1., dP, dV); + // WHEN + final double[][] actualTolerances = integratorBuilder.getTolerances(orbit, orbit.getType()); + // THEN + final double [][] expectedTolerances = NumericalPropagator.tolerances(dP, dV, orbit, orbit.getType()); + Assertions.assertArrayEquals(expectedTolerances[0], actualTolerances[0]); + Assertions.assertArrayEquals(expectedTolerances[1], actualTolerances[1]); + } + + private static class TestIntegratorBuilder extends AbstractVariableStepFieldIntegratorBuilder { + + protected TestIntegratorBuilder(double minStep, double maxStep, double dP, double dV) { + super(minStep, maxStep, dP, dV); + } + + @Override + public AbstractFieldIntegrator buildIntegrator(Field field, Orbit orbit, OrbitType orbitType) { + return null; + } + } +} diff --git a/src/test/java/org/orekit/propagation/conversion/AbstractVariableStepIntegratorBuilderTest.java b/src/test/java/org/orekit/propagation/conversion/AbstractVariableStepIntegratorBuilderTest.java new file mode 100644 index 0000000000..ce247f8337 --- /dev/null +++ b/src/test/java/org/orekit/propagation/conversion/AbstractVariableStepIntegratorBuilderTest.java @@ -0,0 +1,44 @@ +package org.orekit.propagation.conversion; + +import org.hipparchus.ode.AbstractIntegrator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.numerical.NumericalPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; + +class AbstractVariableStepIntegratorBuilderTest { + + @Test + void testGetTolerances() { + // GIVEN + final Orbit orbit = new KeplerianOrbit(7e6, 0.1, 1, 2, 3, 4, PositionAngleType.ECCENTRIC, FramesFactory.getGCRF(), + AbsoluteDate.ARBITRARY_EPOCH, Constants.EGM96_EARTH_MU); + final double dP = 1.; + final double dV = 0.001; + final TestIntegratorBuilder integratorBuilder = new TestIntegratorBuilder(0., 1., dP, dV); + // WHEN + final double[][] actualTolerances = integratorBuilder.getTolerances(orbit, orbit.getType()); + // THEN + final double [][] expectedTolerances = NumericalPropagator.tolerances(dP, dV, orbit, orbit.getType()); + Assertions.assertArrayEquals(expectedTolerances[0], actualTolerances[0]); + Assertions.assertArrayEquals(expectedTolerances[1], actualTolerances[1]); + } + + private static class TestIntegratorBuilder extends AbstractVariableStepIntegratorBuilder { + + protected TestIntegratorBuilder(double minStep, double maxStep, double dP, double dV) { + super(minStep, maxStep, dP, dV); + } + + @Override + public AbstractIntegrator buildIntegrator(Orbit orbit, OrbitType orbitType) { + return null; + } + } +} diff --git a/src/test/java/org/orekit/propagation/conversion/AdamsBashforthFieldIntegratorBuilderTest.java b/src/test/java/org/orekit/propagation/conversion/AdamsBashforthFieldIntegratorBuilderTest.java new file mode 100644 index 0000000000..51a438db86 --- /dev/null +++ b/src/test/java/org/orekit/propagation/conversion/AdamsBashforthFieldIntegratorBuilderTest.java @@ -0,0 +1,37 @@ +package org.orekit.propagation.conversion; + +import org.hipparchus.complex.Complex; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.PositionAngleType; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; + +class AdamsBashforthFieldIntegratorBuilderTest { + + @Test + void testGetTolerances() { + // GIVEN + final double minStep = 1; + final double maxStep = 10; + final double dP = 1; + final double dV = Double.NaN; + final Orbit orbit = new KeplerianOrbit(7e6, 0.1, 1, 2, 3, 4, PositionAngleType.ECCENTRIC, FramesFactory.getGCRF(), + AbsoluteDate.ARBITRARY_EPOCH, Constants.EGM96_EARTH_MU); + final int nSteps = 2; + // WHEN + final AdamsBashforthFieldIntegratorBuilder builder = new AdamsBashforthFieldIntegratorBuilder<>(nSteps, minStep, + maxStep, dP, dV); + final double[][] actualTolerances = builder.getTolerances(orbit, orbit.getType()); + // THEN + final AdamsBashforthFieldIntegratorBuilder builder2 = new AdamsBashforthFieldIntegratorBuilder<>(nSteps, minStep, + maxStep, dP); + final double[][] expectedTolerances = builder2.getTolerances(orbit, orbit.getType()); + Assertions.assertArrayEquals(expectedTolerances[0], actualTolerances[0]); + Assertions.assertArrayEquals(expectedTolerances[1], actualTolerances[1]); + } + +} diff --git a/src/test/java/org/orekit/propagation/conversion/AdamsMoultonFieldIntegratorBuilderTest.java b/src/test/java/org/orekit/propagation/conversion/AdamsMoultonFieldIntegratorBuilderTest.java new file mode 100644 index 0000000000..f90f5abde6 --- /dev/null +++ b/src/test/java/org/orekit/propagation/conversion/AdamsMoultonFieldIntegratorBuilderTest.java @@ -0,0 +1,37 @@ +package org.orekit.propagation.conversion; + +import org.hipparchus.complex.Complex; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.PositionAngleType; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; + +class AdamsMoultonFieldIntegratorBuilderTest { + + @Test + void testGetTolerances() { + // GIVEN + final double minStep = 1; + final double maxStep = 10; + final double dP = 1; + final double dV = Double.NaN; + final Orbit orbit = new KeplerianOrbit(7e6, 0.1, 1, 2, 3, 4, PositionAngleType.ECCENTRIC, FramesFactory.getGCRF(), + AbsoluteDate.ARBITRARY_EPOCH, Constants.EGM96_EARTH_MU); + final int nSteps = 2; + // WHEN + final AdamsMoultonFieldIntegratorBuilder builder = new AdamsMoultonFieldIntegratorBuilder<>(nSteps, minStep, + maxStep, dP, dV); + final double[][] actualTolerances = builder.getTolerances(orbit, orbit.getType()); + // THEN + final AdamsMoultonFieldIntegratorBuilder builder2 = new AdamsMoultonFieldIntegratorBuilder<>(nSteps, minStep, + maxStep, dP); + final double[][] expectedTolerances = builder2.getTolerances(orbit, orbit.getType()); + Assertions.assertArrayEquals(expectedTolerances[0], actualTolerances[0]); + Assertions.assertArrayEquals(expectedTolerances[1], actualTolerances[1]); + } + +} diff --git a/src/test/java/org/orekit/propagation/conversion/BrouwerLyddanePropagatorBuilderTest.java b/src/test/java/org/orekit/propagation/conversion/BrouwerLyddanePropagatorBuilderTest.java index ccb9056807..c4e0c0ce86 100644 --- a/src/test/java/org/orekit/propagation/conversion/BrouwerLyddanePropagatorBuilderTest.java +++ b/src/test/java/org/orekit/propagation/conversion/BrouwerLyddanePropagatorBuilderTest.java @@ -22,9 +22,7 @@ import org.hipparchus.util.FastMath; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; import org.orekit.Utils; import org.orekit.forces.gravity.potential.GravityFieldFactory; import org.orekit.forces.gravity.potential.TideSystem; @@ -132,7 +130,7 @@ public void doTestBuildPropagatorWithDrag() { @BeforeEach public void setUp() { - Utils.setDataRoot("regular-data"); + Utils.setDataRoot("potential:regular-data"); AbsoluteDate initDate = AbsoluteDate.J2000_EPOCH.shiftedBy(583.); final Frame inertialFrame = FramesFactory.getEME2000(); @@ -159,7 +157,7 @@ public void setUp() { } @Test - @DisplayName("Test copy method") + @SuppressWarnings("deprecation") void testCopyMethod() { // Given @@ -167,17 +165,10 @@ void testCopyMethod() { new Vector3D(Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS + 400000, 0, 0), new Vector3D(10, 7668.6, 3)), FramesFactory.getGCRF(), new AbsoluteDate(), Constants.EIGEN5C_EARTH_MU); - final UnnormalizedSphericalHarmonicsProvider harmonicsProvider = - Mockito.mock(UnnormalizedSphericalHarmonicsProvider.class); - Mockito.when(harmonicsProvider.getMu()).thenReturn(Constants.EIGEN5C_EARTH_MU); - - final PositionAngleType positionAngleType = PositionAngleType.MEAN; - final double positionScale = 10; - final double m2 = 0; + final UnnormalizedSphericalHarmonicsProvider harmonicsProvider = GravityFieldFactory.getUnnormalizedProvider(5, 0); final BrouwerLyddanePropagatorBuilder builder = new BrouwerLyddanePropagatorBuilder(orbit, harmonicsProvider, - positionAngleType, positionScale, - m2); + PositionAngleType.MEAN, 10.0, 0.0); // When final BrouwerLyddanePropagatorBuilder copyBuilder = builder.copy(); @@ -186,4 +177,28 @@ void testCopyMethod() { assertPropagatorBuilderIsACopy(builder, copyBuilder); } + @Test + void testClone() { + + // Given + final Orbit orbit = new CartesianOrbit(new PVCoordinates( + new Vector3D(Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS + 400000, 0, 0), + new Vector3D(10, 7668.6, 3)), FramesFactory.getGCRF(), + new AbsoluteDate(), Constants.EIGEN5C_EARTH_MU); + final UnnormalizedSphericalHarmonicsProvider harmonicsProvider = GravityFieldFactory.getUnnormalizedProvider(5, 0); + + final BrouwerLyddanePropagatorBuilder builder = new BrouwerLyddanePropagatorBuilder(orbit, harmonicsProvider, + PositionAngleType.MEAN, 10.0, 1.0e-8); + builder.getPropagationParametersDrivers().getDrivers().forEach(driver -> driver.setSelected(true)); + + // When + final BrouwerLyddanePropagatorBuilder copyBuilder = (BrouwerLyddanePropagatorBuilder) builder.clone(); + + // Then + assertPropagatorBuilderIsACopy(builder, copyBuilder); + Assertions.assertEquals(builder.getM2Value(), copyBuilder.getM2Value()); + Assertions.assertTrue(builder.getPropagationParametersDrivers().getDrivers().get(0).isSelected()); + Assertions.assertTrue(copyBuilder.getPropagationParametersDrivers().getDrivers().get(0).isSelected()); + } + } diff --git a/src/test/java/org/orekit/propagation/conversion/ClassicalRungeKuttaIntegratorBuilderTest.java b/src/test/java/org/orekit/propagation/conversion/ClassicalRungeKuttaIntegratorBuilderTest.java new file mode 100644 index 0000000000..3924f9627d --- /dev/null +++ b/src/test/java/org/orekit/propagation/conversion/ClassicalRungeKuttaIntegratorBuilderTest.java @@ -0,0 +1,49 @@ +/* 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.propagation.conversion; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.ode.ODEIntegrator; +import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaIntegrator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.OrbitType; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.AbsolutePVCoordinates; + +class ClassicalRungeKuttaIntegratorBuilderTest { + + @Test + void testBuildIntegrator() { + // GIVEN + final AbsolutePVCoordinates absolutePVCoordinates = new AbsolutePVCoordinates(FramesFactory.getGCRF(), + AbsoluteDate.ARBITRARY_EPOCH, Vector3D.MINUS_I, Vector3D.MINUS_K); + final double step = 10; + final ClassicalRungeKuttaIntegratorBuilder builder = new ClassicalRungeKuttaIntegratorBuilder(step); + // WHEN + final ODEIntegrator integrator = builder.buildIntegrator(absolutePVCoordinates); + // THEN + final ClassicalRungeKuttaIntegrator actualIntegrator = (ClassicalRungeKuttaIntegrator) integrator; + final CartesianOrbit orbit = new CartesianOrbit(absolutePVCoordinates.getPVCoordinates(), absolutePVCoordinates.getFrame(), + absolutePVCoordinates.getDate(), 1.); + final ClassicalRungeKuttaIntegrator expectedIntegrator = (ClassicalRungeKuttaIntegrator) builder.buildIntegrator(orbit, OrbitType.CARTESIAN); + Assertions.assertEquals(expectedIntegrator.getCurrentSignedStepsize(), actualIntegrator.getCurrentSignedStepsize()); + } + +} diff --git a/src/test/java/org/orekit/propagation/conversion/DSSTPropagatorBuilderTest.java b/src/test/java/org/orekit/propagation/conversion/DSSTPropagatorBuilderTest.java index c06ce5893e..cbf11c702f 100644 --- a/src/test/java/org/orekit/propagation/conversion/DSSTPropagatorBuilderTest.java +++ b/src/test/java/org/orekit/propagation/conversion/DSSTPropagatorBuilderTest.java @@ -20,15 +20,12 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.ode.nonstiff.DormandPrince853Integrator; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; +import org.junit.jupiter.api.*; import org.orekit.Utils; import org.orekit.bodies.CelestialBodyFactory; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; +import org.orekit.forces.gravity.potential.GravityFieldFactory; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.orbits.CartesianOrbit; @@ -45,6 +42,7 @@ import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel; import org.orekit.propagation.semianalytical.dsst.forces.DSSTNewtonianAttraction; import org.orekit.propagation.semianalytical.dsst.forces.DSSTThirdBody; +import org.orekit.propagation.semianalytical.dsst.forces.DSSTZonal; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScalesFactory; import org.orekit.utils.Constants; @@ -62,6 +60,7 @@ public class DSSTPropagatorBuilderTest { private double minStep; private double maxStep; private double dP; + private double dV; private double[][] tolerance; private AbsoluteDate initDate; @@ -73,14 +72,14 @@ public class DSSTPropagatorBuilderTest { @Test public void testIntegrators01() { - ODEIntegratorBuilder abBuilder = new AdamsBashforthIntegratorBuilder(2, minStep, maxStep, dP); + ODEIntegratorBuilder abBuilder = new AdamsBashforthIntegratorBuilder(2, minStep, maxStep, dP, dV); doTestBuildPropagator(abBuilder); } @Test public void testIntegrators02() { - ODEIntegratorBuilder amBuilder = new AdamsMoultonIntegratorBuilder(2, minStep, maxStep, dP); + ODEIntegratorBuilder amBuilder = new AdamsMoultonIntegratorBuilder(2, minStep, maxStep, dP, dV); doTestBuildPropagator(amBuilder); } @@ -130,14 +129,14 @@ public void testIntegrators07() { @Test public void testIntegrators08() { - ODEIntegratorBuilder gbsBuilder = new GraggBulirschStoerIntegratorBuilder(minStep, maxStep, dP); + ODEIntegratorBuilder gbsBuilder = new GraggBulirschStoerIntegratorBuilder(minStep, maxStep, dP, dV); doTestBuildPropagator(gbsBuilder); } @Test public void testIntegrators09() { - ODEIntegratorBuilder hh54Builder = new HighamHall54IntegratorBuilder(minStep, maxStep, dP); + ODEIntegratorBuilder hh54Builder = new HighamHall54IntegratorBuilder(minStep, maxStep, dP, dV); doTestBuildPropagator(hh54Builder); } @@ -160,24 +159,20 @@ public void testIntegrators11() { } @Test - @DisplayName("Test copy method") + @SuppressWarnings("deprecation") void testCopyMethod() { // Given - final ODEIntegratorBuilder integratorBuilder = Mockito.mock(ODEIntegratorBuilder.class); + final ODEIntegratorBuilder integratorBuilder = new ClassicalRungeKuttaIntegratorBuilder(3600.0); final Orbit orbit = new CartesianOrbit(new PVCoordinates( new Vector3D(Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS + 400000, 0, 0), new Vector3D(0, 7668.6, 0)), FramesFactory.getGCRF(), new AbsoluteDate(), Constants.EIGEN5C_EARTH_MU); - final double positionScale = 1; - final PropagationType propagationType = PropagationType.OSCULATING; - final PropagationType stateType = PropagationType.OSCULATING; - final DSSTPropagatorBuilder builder = - new DSSTPropagatorBuilder(orbit, integratorBuilder, positionScale, propagationType, stateType); + new DSSTPropagatorBuilder(orbit, integratorBuilder, 1.0, PropagationType.OSCULATING, PropagationType.OSCULATING); - builder.addForceModel(Mockito.mock(DSSTForceModel.class)); + builder.addForceModel(new DSSTZonal(GravityFieldFactory.getUnnormalizedProvider(2, 0))); // When final DSSTPropagatorBuilder copyBuilder = builder.copy(); @@ -187,15 +182,44 @@ void testCopyMethod() { } + @Test + void testClone() { + + // Given + final ODEIntegratorBuilder integratorBuilder = new ClassicalRungeKuttaIntegratorBuilder(3600.0); + final Orbit orbit = new CartesianOrbit(new PVCoordinates( + new Vector3D(Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS + 400000, 0, 0), + new Vector3D(0, 7668.6, 0)), FramesFactory.getGCRF(), + new AbsoluteDate(), Constants.EIGEN5C_EARTH_MU); + + final DSSTPropagatorBuilder builder = + new DSSTPropagatorBuilder(orbit, integratorBuilder, 1.0, PropagationType.OSCULATING, PropagationType.OSCULATING); + + builder.addForceModel(new DSSTZonal(GravityFieldFactory.getUnnormalizedProvider(2, 0))); + + // When + final DSSTPropagatorBuilder copyBuilder = (DSSTPropagatorBuilder) builder.clone(); + + // Then + assertDSSTPropagatorBuilderIsACopy(builder, copyBuilder); + + } + private void assertDSSTPropagatorBuilderIsACopy(final DSSTPropagatorBuilder expected, final DSSTPropagatorBuilder actual) { assertPropagatorBuilderIsACopy(expected, actual); - Assertions.assertEquals(expected.getIntegratorBuilder(), actual.getIntegratorBuilder()); + Assertions.assertEquals(expected.getIntegratorBuilder().getClass(), actual.getIntegratorBuilder().getClass()); Assertions.assertEquals(expected.getPropagationType(), actual.getPropagationType()); Assertions.assertEquals(expected.getStateType(), actual.getStateType()); Assertions.assertEquals(expected.getMass(), actual.getMass()); - Assertions.assertEquals(expected.getAllForceModels(), actual.getAllForceModels()); + final List expectedForceModelList = expected.getAllForceModels(); + final List actualForceModelList = actual.getAllForceModels(); + Assertions.assertEquals(expectedForceModelList.size(), actualForceModelList.size()); + for (int i = 0; i < expectedForceModelList.size(); i++) { + Assertions.assertEquals(expectedForceModelList.get(i).getClass(), actualForceModelList.get(i).getClass()); + } + } private void doTestBuildPropagator(final ODEIntegratorBuilder foiBuilder) { @@ -308,7 +332,7 @@ public CombinedDerivatives combinedDerivatives(SpacecraftState s) { @Test public void testDeselectOrbitals() { // Integrator builder - final ODEIntegratorBuilder dp54Builder = new DormandPrince54IntegratorBuilder(minStep, maxStep, dP); + final ODEIntegratorBuilder dp54Builder = new DormandPrince54IntegratorBuilder(minStep, maxStep, dP, dV); // Propagator builder final DSSTPropagatorBuilder builder = new DSSTPropagatorBuilder(orbit, dp54Builder, @@ -327,11 +351,12 @@ public void testDeselectOrbitals() { @BeforeEach public void setUp() throws IOException, ParseException { - Utils.setDataRoot("regular-data"); + Utils.setDataRoot("regular-data:potential"); minStep = 1.0; maxStep = 600.0; dP = 10.0; + dV = 0.0001; final Frame earthFrame = FramesFactory.getEME2000(); initDate = new AbsoluteDate(2003, 07, 01, 0, 0, 00.000, TimeScalesFactory.getUTC()); diff --git a/src/test/java/org/orekit/propagation/conversion/DormandPrince54FieldIntegratorBuilderTest.java b/src/test/java/org/orekit/propagation/conversion/DormandPrince54FieldIntegratorBuilderTest.java new file mode 100644 index 0000000000..2e78831e90 --- /dev/null +++ b/src/test/java/org/orekit/propagation/conversion/DormandPrince54FieldIntegratorBuilderTest.java @@ -0,0 +1,36 @@ +package org.orekit.propagation.conversion; + +import org.hipparchus.complex.Complex; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.PositionAngleType; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; + +class DormandPrince54FieldIntegratorBuilderTest { + + @Test + void testGetTolerances() { + // GIVEN + final double minStep = 1; + final double maxStep = 10; + final double dP = 1; + final double dV = Double.NaN; + final Orbit orbit = new KeplerianOrbit(7e6, 0.1, 1, 2, 3, 4, PositionAngleType.ECCENTRIC, FramesFactory.getGCRF(), + AbsoluteDate.ARBITRARY_EPOCH, Constants.EGM96_EARTH_MU); + // WHEN + final DormandPrince54FieldIntegratorBuilder builder = new DormandPrince54FieldIntegratorBuilder<>(minStep, + maxStep, dP, dV); + final double[][] actualTolerances = builder.getTolerances(orbit, orbit.getType()); + // THEN + final DormandPrince54FieldIntegratorBuilder builder2 = new DormandPrince54FieldIntegratorBuilder<>(minStep, + maxStep, dP); + final double[][] expectedTolerances = builder2.getTolerances(orbit, orbit.getType()); + Assertions.assertArrayEquals(expectedTolerances[0], actualTolerances[0]); + Assertions.assertArrayEquals(expectedTolerances[1], actualTolerances[1]); + } + +} diff --git a/src/test/java/org/orekit/propagation/conversion/DormandPrince853FieldIntegratorBuilderTest.java b/src/test/java/org/orekit/propagation/conversion/DormandPrince853FieldIntegratorBuilderTest.java new file mode 100644 index 0000000000..91360b6476 --- /dev/null +++ b/src/test/java/org/orekit/propagation/conversion/DormandPrince853FieldIntegratorBuilderTest.java @@ -0,0 +1,36 @@ +package org.orekit.propagation.conversion; + +import org.hipparchus.complex.Complex; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.PositionAngleType; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; + +class DormandPrince853FieldIntegratorBuilderTest { + + @Test + void testGetTolerances() { + // GIVEN + final double minStep = 1; + final double maxStep = 10; + final double dP = 1; + final double dV = Double.NaN; + final Orbit orbit = new KeplerianOrbit(7e6, 0.1, 1, 2, 3, 4, PositionAngleType.ECCENTRIC, FramesFactory.getGCRF(), + AbsoluteDate.ARBITRARY_EPOCH, Constants.EGM96_EARTH_MU); + // WHEN + final DormandPrince853FieldIntegratorBuilder builder = new DormandPrince853FieldIntegratorBuilder<>(minStep, + maxStep, dP, dV); + final double[][] actualTolerances = builder.getTolerances(orbit, orbit.getType()); + // THEN + final DormandPrince853FieldIntegratorBuilder builder2 = new DormandPrince853FieldIntegratorBuilder<>(minStep, + maxStep, dP); + final double[][] expectedTolerances = builder2.getTolerances(orbit, orbit.getType()); + Assertions.assertArrayEquals(expectedTolerances[0], actualTolerances[0]); + Assertions.assertArrayEquals(expectedTolerances[1], actualTolerances[1]); + } + +} diff --git a/src/test/java/org/orekit/propagation/conversion/EcksteinHechlerPropagatorBuilderTest.java b/src/test/java/org/orekit/propagation/conversion/EcksteinHechlerPropagatorBuilderTest.java index f9b81fcb5f..a2ab5f3cf4 100644 --- a/src/test/java/org/orekit/propagation/conversion/EcksteinHechlerPropagatorBuilderTest.java +++ b/src/test/java/org/orekit/propagation/conversion/EcksteinHechlerPropagatorBuilderTest.java @@ -18,9 +18,11 @@ package org.orekit.propagation.conversion; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; +import org.orekit.Utils; +import org.orekit.forces.gravity.potential.GravityFieldFactory; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; import org.orekit.frames.FramesFactory; import org.orekit.orbits.CartesianOrbit; @@ -34,8 +36,13 @@ public class EcksteinHechlerPropagatorBuilderTest { + @BeforeEach + public void initialize() { + Utils.setDataRoot("potential"); + } + @Test - @DisplayName("Test copy method") + @SuppressWarnings("deprecation") void testCopyMethod() { // Given @@ -44,15 +51,10 @@ void testCopyMethod() { new Vector3D(10, 7668.6, 3)), FramesFactory.getGCRF(), new AbsoluteDate(), Constants.EIGEN5C_EARTH_MU); - final UnnormalizedSphericalHarmonicsProvider harmonicsProvider = - Mockito.mock(UnnormalizedSphericalHarmonicsProvider.class); - Mockito.when(harmonicsProvider.getMu()).thenReturn(Constants.EIGEN5C_EARTH_MU); - - final PositionAngleType positionAngleType = null; - final double positionScale = 10; + final UnnormalizedSphericalHarmonicsProvider harmonicsProvider = GravityFieldFactory.getUnnormalizedProvider(6, 0); final EcksteinHechlerPropagatorBuilder builder = new EcksteinHechlerPropagatorBuilder(orbit, harmonicsProvider, - positionAngleType, positionScale); + PositionAngleType.MEAN, 10.0); // When final EcksteinHechlerPropagatorBuilder copyBuilder = builder.copy(); @@ -61,4 +63,27 @@ void testCopyMethod() { assertPropagatorBuilderIsACopy(builder, copyBuilder); } + + @Test + void testClone() { + + // Given + final Orbit orbit = new CartesianOrbit(new PVCoordinates( + new Vector3D(Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS + 400000, 0, 0), + new Vector3D(10, 7668.6, 3)), FramesFactory.getGCRF(), + new AbsoluteDate(), Constants.EIGEN5C_EARTH_MU); + + final UnnormalizedSphericalHarmonicsProvider harmonicsProvider = GravityFieldFactory.getUnnormalizedProvider(6, 0); + + final EcksteinHechlerPropagatorBuilder builder = new EcksteinHechlerPropagatorBuilder(orbit, harmonicsProvider, + PositionAngleType.MEAN, 10.0); + + // When + final EcksteinHechlerPropagatorBuilder copyBuilder = (EcksteinHechlerPropagatorBuilder) builder.clone(); + + // Then + assertPropagatorBuilderIsACopy(builder, copyBuilder); + Assertions.assertEquals(builder.getImpulseManeuvers().size(), copyBuilder.getImpulseManeuvers().size()); + + } } diff --git a/src/test/java/org/orekit/propagation/conversion/EphemerisPropagatorBuilderTest.java b/src/test/java/org/orekit/propagation/conversion/EphemerisPropagatorBuilderTest.java index 135680cc59..4c25cb63d6 100644 --- a/src/test/java/org/orekit/propagation/conversion/EphemerisPropagatorBuilderTest.java +++ b/src/test/java/org/orekit/propagation/conversion/EphemerisPropagatorBuilderTest.java @@ -17,13 +17,12 @@ package org.orekit.propagation.conversion; -import static org.orekit.propagation.conversion.AbstractPropagatorBuilderTest.assertPropagatorBuilderIsACopy; - import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.orekit.TestUtils; +import org.orekit.Utils; import org.orekit.attitudes.AttitudeProvider; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; @@ -44,6 +43,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.orekit.propagation.conversion.AbstractPropagatorBuilderTest.assertPropagatorBuilderIsACopy; /** * Unit tests for {@link EphemerisPropagatorBuilder}. @@ -127,7 +127,7 @@ void should_create_expected_propagator() { } @Test - @DisplayName("Test copy method") + @SuppressWarnings("deprecation") void testCopyMethod() { // Given @@ -136,12 +136,14 @@ void testCopyMethod() { new Vector3D(0, 7668.6, 0)), FramesFactory.getGCRF(), new AbsoluteDate(), Constants.EIGEN5C_EARTH_MU); final List states = new ArrayList<>(); - states.add(new SpacecraftState(orbit)); - states.add(new SpacecraftState(orbit)); + double end = Constants.JULIAN_DAY; + for (double dt = 0; dt <= end; dt += 3600.0) { + AbsoluteDate shiftedEpoch = orbit.getDate().shiftedBy(dt); + states.add(new SpacecraftState(orbit.shiftedBy(dt), Utils.defaultLaw().getAttitude(orbit, shiftedEpoch, orbit.getFrame()))); + } - final Frame frame = FramesFactory.getGCRF(); - final TimeInterpolator stateInterpolator = new SpacecraftStateInterpolator(frame); - final AttitudeProvider attitudeProvider = mock(AttitudeProvider.class); + final TimeInterpolator stateInterpolator = new SpacecraftStateInterpolator(orbit.getFrame()); + final AttitudeProvider attitudeProvider = Utils.defaultLaw(); final EphemerisPropagatorBuilder builder = new EphemerisPropagatorBuilder(states, stateInterpolator, attitudeProvider); @@ -152,4 +154,32 @@ void testCopyMethod() { // Then assertPropagatorBuilderIsACopy(builder, copyBuilder); } + + @Test + void testClone() { + + // Given + final Orbit orbit = new CartesianOrbit(new PVCoordinates( + new Vector3D(Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS + 400000, 0, 0), + new Vector3D(0, 7668.6, 0)), FramesFactory.getGCRF(), + new AbsoluteDate(), Constants.EIGEN5C_EARTH_MU); + final List states = new ArrayList<>(); + double end = Constants.JULIAN_DAY; + for (double dt = 0; dt <= end; dt += 3600.0) { + AbsoluteDate shiftedEpoch = orbit.getDate().shiftedBy(dt); + states.add(new SpacecraftState(orbit.shiftedBy(dt), Utils.defaultLaw().getAttitude(orbit, shiftedEpoch, orbit.getFrame()))); + } + + final TimeInterpolator stateInterpolator = new SpacecraftStateInterpolator(orbit.getFrame()); + final AttitudeProvider attitudeProvider = Utils.defaultLaw(); + + final EphemerisPropagatorBuilder builder = + new EphemerisPropagatorBuilder(states, stateInterpolator, attitudeProvider); + + // When + final EphemerisPropagatorBuilder copyBuilder = (EphemerisPropagatorBuilder) builder.clone(); + + // Then + assertPropagatorBuilderIsACopy(builder, copyBuilder); + } } diff --git a/src/test/java/org/orekit/propagation/conversion/KeplerianPropagatorBuilderTest.java b/src/test/java/org/orekit/propagation/conversion/KeplerianPropagatorBuilderTest.java index 1f4a9d8144..ca5cc23432 100644 --- a/src/test/java/org/orekit/propagation/conversion/KeplerianPropagatorBuilderTest.java +++ b/src/test/java/org/orekit/propagation/conversion/KeplerianPropagatorBuilderTest.java @@ -18,12 +18,15 @@ package org.orekit.propagation.conversion; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.forces.maneuvers.ImpulseManeuver; import org.orekit.frames.FramesFactory; import org.orekit.orbits.CartesianOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.Propagator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.Constants; import org.orekit.utils.PVCoordinates; @@ -31,25 +34,76 @@ import static org.orekit.propagation.conversion.AbstractPropagatorBuilderTest.assertPropagatorBuilderIsACopy; public class KeplerianPropagatorBuilderTest { + @Test - @DisplayName("Test copy method") - void testCopyMethod() { + void testClone() { // Given - final Orbit orbit = new CartesianOrbit(new PVCoordinates( - new Vector3D(Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS + 400000, 0, 0), - new Vector3D(0, 7668.6, 0)), FramesFactory.getGCRF(), - new AbsoluteDate(), Constants.EIGEN5C_EARTH_MU); - final PositionAngleType positionAngleType = null; - final double positionScale = 1; + final Orbit orbit = getOrbit(); + final KeplerianPropagatorBuilder builder = new KeplerianPropagatorBuilder(orbit, PositionAngleType.MEAN, 1.0); + + // When + final KeplerianPropagatorBuilder copyBuilder = (KeplerianPropagatorBuilder) builder.clone(); + + // Then + assertPropagatorBuilderIsACopy(builder, copyBuilder); + Assertions.assertEquals(builder.getImpulseManeuvers().size(), copyBuilder.getImpulseManeuvers().size()); - final KeplerianPropagatorBuilder builder = new KeplerianPropagatorBuilder(orbit, positionAngleType, positionScale); + } + + @Test + void testClearImpulseManeuvers() { + // Given + final Orbit orbit = getOrbit(); + final KeplerianPropagatorBuilder builder = new KeplerianPropagatorBuilder(orbit, PositionAngleType.MEAN, 1.0); + final ImpulseManeuver mockedManeuver = Mockito.mock(ImpulseManeuver.class); + builder.addImpulseManeuver(mockedManeuver); + + // When + builder.clearImpulseManeuvers(); + + // Then + final Propagator propagator = builder.buildPropagator(); + Assertions.assertTrue(propagator.getEventsDetectors().isEmpty()); + } + + @Test + void testAddImpulseManeuver() { + // Given + final Orbit orbit = getOrbit(); + final KeplerianPropagatorBuilder builder = new KeplerianPropagatorBuilder(orbit, PositionAngleType.MEAN, 1.0); + final ImpulseManeuver mockedManeuver = Mockito.mock(ImpulseManeuver.class); + + // When + builder.addImpulseManeuver(mockedManeuver); + + // Then + final Propagator propagator = builder.buildPropagator(); + Assertions.assertEquals(1, propagator.getEventsDetectors().size()); + Assertions.assertEquals(mockedManeuver, propagator.getEventsDetectors().toArray()[0]); + } + + @Test + @SuppressWarnings("deprecation") + void testCopyMethod() { + + // Given + final Orbit orbit = getOrbit(); + final KeplerianPropagatorBuilder builder = new KeplerianPropagatorBuilder(orbit, PositionAngleType.MEAN, 1.0); // When final KeplerianPropagatorBuilder copyBuilder = builder.copy(); // Then assertPropagatorBuilderIsACopy(builder, copyBuilder); + Assertions.assertEquals(builder.getImpulseManeuvers().size(), copyBuilder.getImpulseManeuvers().size()); } + + private static Orbit getOrbit() { + return new CartesianOrbit(new PVCoordinates( + new Vector3D(Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS + 400000, 0, 0), + new Vector3D(0, 7668.6, 0)), FramesFactory.getGCRF(), + new AbsoluteDate(), Constants.EIGEN5C_EARTH_MU); + } } diff --git a/src/test/java/org/orekit/propagation/conversion/NumericalPropagatorBuilderTest.java b/src/test/java/org/orekit/propagation/conversion/NumericalPropagatorBuilderTest.java index 4ad46f608f..0a63ef64eb 100644 --- a/src/test/java/org/orekit/propagation/conversion/NumericalPropagatorBuilderTest.java +++ b/src/test/java/org/orekit/propagation/conversion/NumericalPropagatorBuilderTest.java @@ -19,43 +19,85 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.mockito.Mock; import org.mockito.Mockito; -import org.orekit.attitudes.AttitudeProvider; +import org.orekit.Utils; import org.orekit.forces.ForceModel; +import org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel; +import org.orekit.forces.gravity.potential.GravityFieldFactory; +import org.orekit.forces.maneuvers.ImpulseManeuver; import org.orekit.frames.FramesFactory; import org.orekit.orbits.CartesianOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.Propagator; +import org.orekit.propagation.events.EventDetector; import org.orekit.time.AbsoluteDate; import org.orekit.utils.Constants; +import org.orekit.utils.IERSConventions; import org.orekit.utils.PVCoordinates; +import java.util.Collection; import java.util.List; import static org.orekit.propagation.conversion.AbstractPropagatorBuilderTest.assertPropagatorBuilderIsACopy; public class NumericalPropagatorBuilderTest { + + @BeforeAll + public static void setUpBeforeClass() { + Utils.setDataRoot("regular-data:potential"); + } + @Test - @DisplayName("Test copy method") - void testCopyMethod() { + void testClearImpulseManeuvers() { + // Given + final ODEIntegratorBuilder integratorBuilder = new ClassicalRungeKuttaIntegratorBuilder(60); + final Orbit orbit = getOrbit(); + final NumericalPropagatorBuilder builder = + new NumericalPropagatorBuilder(orbit, integratorBuilder, PositionAngleType.MEAN, 1.0, Utils.defaultLaw()); + final ImpulseManeuver mockedManeuver = Mockito.mock(ImpulseManeuver.class); + builder.addImpulseManeuver(mockedManeuver); + // WHEN + builder.clearImpulseManeuvers(); + // THEN + final Propagator propagator = builder.buildPropagator(); + final Collection detectors = propagator.getEventsDetectors(); + Assertions.assertTrue(detectors.isEmpty()); + } + @Test + void testAddImpulseManeuver() { // Given - final ODEIntegratorBuilder integratorBuilder = Mockito.mock(ODEIntegratorBuilder.class); - final Orbit orbit = new CartesianOrbit(new PVCoordinates( - new Vector3D(Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS + 400000, 0, 0), - new Vector3D(0, 7668.6, 0)), FramesFactory.getGCRF(), - new AbsoluteDate(), Constants.EIGEN5C_EARTH_MU); + final ODEIntegratorBuilder integratorBuilder = new ClassicalRungeKuttaIntegratorBuilder(60); + final Orbit orbit = getOrbit(); + final NumericalPropagatorBuilder builder = + new NumericalPropagatorBuilder(orbit, integratorBuilder, PositionAngleType.MEAN, 1.0, Utils.defaultLaw()); + final ImpulseManeuver mockedManeuver = Mockito.mock(ImpulseManeuver.class); + // WHEN + builder.addImpulseManeuver(mockedManeuver); + // THEN + final Propagator propagator = builder.buildPropagator(); + final Collection detectors = propagator.getEventsDetectors(); + Assertions.assertEquals(1, detectors.size()); + Assertions.assertEquals(mockedManeuver, detectors.toArray()[0]); + } - final PositionAngleType positionAngleType = null; - final double positionScale = 1; - final AttitudeProvider attitudeProvider = Mockito.mock(AttitudeProvider.class); + @Test + @SuppressWarnings("deprecation") + void testCopyMethod() { + + // Given + final ODEIntegratorBuilder integratorBuilder = new ClassicalRungeKuttaIntegratorBuilder(60); + final Orbit orbit = getOrbit(); final NumericalPropagatorBuilder builder = - new NumericalPropagatorBuilder(orbit, integratorBuilder, positionAngleType, positionScale, attitudeProvider); + new NumericalPropagatorBuilder(orbit, integratorBuilder, PositionAngleType.MEAN, 1.0, Utils.defaultLaw()); - builder.addForceModel(Mockito.mock(ForceModel.class)); + builder.addForceModel(new HolmesFeatherstoneAttractionModel(FramesFactory.getITRF(IERSConventions.IERS_2010, true), + GravityFieldFactory.getNormalizedProvider(2, 0))); // When final NumericalPropagatorBuilder copyBuilder = builder.copy(); @@ -65,6 +107,34 @@ void testCopyMethod() { } + @Test + void testClone() { + + // Given + final ODEIntegratorBuilder integratorBuilder = new ClassicalRungeKuttaIntegratorBuilder(60); + final Orbit orbit = getOrbit(); + + final NumericalPropagatorBuilder builder = + new NumericalPropagatorBuilder(orbit, integratorBuilder, PositionAngleType.MEAN, 1.0, Utils.defaultLaw()); + + builder.addForceModel(new HolmesFeatherstoneAttractionModel(FramesFactory.getITRF(IERSConventions.IERS_2010, true), + GravityFieldFactory.getNormalizedProvider(2, 0))); + + // When + final NumericalPropagatorBuilder copyBuilder = (NumericalPropagatorBuilder) builder.clone(); + + // Then + assertNumericalPropagatorBuilderIsACopy(builder, copyBuilder); + + } + + private static Orbit getOrbit() { + return new CartesianOrbit(new PVCoordinates( + new Vector3D(Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS + 400000, 0, 0), + new Vector3D(0, 7668.6, 0)), FramesFactory.getGCRF(), + new AbsoluteDate(), Constants.EIGEN5C_EARTH_MU); + } + private void assertNumericalPropagatorBuilderIsACopy(final NumericalPropagatorBuilder expected, final NumericalPropagatorBuilder actual) { assertPropagatorBuilderIsACopy(expected, actual); @@ -72,13 +142,13 @@ private void assertNumericalPropagatorBuilderIsACopy(final NumericalPropagatorBu // Assert force models final List expectedForceModelList = expected.getAllForceModels(); final List actualForceModelList = actual.getAllForceModels(); - + Assertions.assertEquals(expectedForceModelList.size(), actualForceModelList.size()); for (int i = 0; i < expectedForceModelList.size(); i++) { - Assertions.assertEquals(expectedForceModelList.get(i), actualForceModelList.get(i)); + Assertions.assertEquals(expectedForceModelList.get(i).getClass(), actualForceModelList.get(i).getClass()); } // Assert integrator builder - Assertions.assertEquals(expected.getIntegratorBuilder(), actual.getIntegratorBuilder()); + Assertions.assertEquals(expected.getIntegratorBuilder().getClass(), actual.getIntegratorBuilder().getClass()); // Assert mass Assertions.assertEquals(expected.getMass(), actual.getMass()); diff --git a/src/test/java/org/orekit/propagation/conversion/TLEPropagatorBuilderTest.java b/src/test/java/org/orekit/propagation/conversion/TLEPropagatorBuilderTest.java index c7f1cf0c22..91b796d8e9 100644 --- a/src/test/java/org/orekit/propagation/conversion/TLEPropagatorBuilderTest.java +++ b/src/test/java/org/orekit/propagation/conversion/TLEPropagatorBuilderTest.java @@ -17,10 +17,7 @@ package org.orekit.propagation.conversion; -import static org.orekit.Utils.assertParametersDriversValues; - import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.orekit.Utils; import org.orekit.data.DataContext; @@ -28,52 +25,46 @@ import org.orekit.propagation.analytical.tle.TLE; import org.orekit.propagation.analytical.tle.generation.FixedPointTleGenerationAlgorithm; +import static org.orekit.propagation.conversion.AbstractPropagatorBuilderTest.assertPropagatorBuilderIsACopy; + public class TLEPropagatorBuilderTest { @Test - @DisplayName("Test copy method") + @SuppressWarnings("deprecation") void testCopyMethod() { // Given final DataContext dataContext = Utils.setDataRoot("regular-data"); final TLE tle = new TLE("1 27421U 02021A 02124.48976499 -.00021470 00000-0 -89879-2 0 20", "2 27421 98.7490 199.5121 0001333 133.9522 226.1918 14.26113993 62"); - final PositionAngleType positionAngleType = null; - final double positionScale = 1; - - final TLEPropagatorBuilder builder = new TLEPropagatorBuilder(tle, positionAngleType, positionScale, dataContext, + final TLEPropagatorBuilder builder = new TLEPropagatorBuilder(tle, PositionAngleType.MEAN, 1.0, dataContext, new FixedPointTleGenerationAlgorithm()); // When final TLEPropagatorBuilder copyBuilder = builder.copy(); // Then - assertTlePropagatorBuilderIsACopy(builder, copyBuilder); + assertPropagatorBuilderIsACopy(builder, copyBuilder); + Assertions.assertEquals(builder.getTemplateTLE(), copyBuilder.getTemplateTLE()); } - private void assertTlePropagatorBuilderIsACopy(final TLEPropagatorBuilder expected, final TLEPropagatorBuilder actual) { - - // They should not be the same instance - Assertions.assertNotEquals(expected, actual); + @Test + void testClone() { - Assertions.assertArrayEquals(expected.getSelectedNormalizedParameters(), - actual.getSelectedNormalizedParameters()); + // Given + final DataContext dataContext = Utils.setDataRoot("regular-data"); + final TLE tle = new TLE("1 27421U 02021A 02124.48976499 -.00021470 00000-0 -89879-2 0 20", + "2 27421 98.7490 199.5121 0001333 133.9522 226.1918 14.26113993 62"); + final TLEPropagatorBuilder builder = new TLEPropagatorBuilder(tle, PositionAngleType.MEAN, 1.0, dataContext, + new FixedPointTleGenerationAlgorithm()); - assertParametersDriversValues(expected.getOrbitalParametersDrivers(), - actual.getOrbitalParametersDrivers()); + // When + final TLEPropagatorBuilder copyBuilder = (TLEPropagatorBuilder) builder.clone(); - Assertions.assertEquals(expected.getFrame(), actual.getFrame()); - Assertions.assertEquals(expected.getMu(), actual.getMu()); - Assertions.assertEquals(expected.getOrbitType(), actual.getOrbitType()); - Assertions.assertEquals(expected.getPositionAngleType(), actual.getPositionAngleType()); - Assertions.assertEquals(expected.getPositionScale(), actual.getPositionScale()); - Assertions.assertEquals(expected.getInitialOrbitDate(), actual.getInitialOrbitDate()); - Assertions.assertEquals(expected.getAdditionalDerivativesProviders(), actual.getAdditionalDerivativesProviders()); - Assertions.assertEquals(expected.getTemplateTLE(), actual.getTemplateTLE()); + // Then + assertPropagatorBuilderIsACopy(builder, copyBuilder); + Assertions.assertEquals(builder.getTemplateTLE(), copyBuilder.getTemplateTLE()); - // Attitude provider is necessarily different due to how a TLEPropagatorBuilder is defined - //Assertions.assertEquals(expected.getAttitudeProvider(), actual.getAttitudeProvider()); } - } diff --git a/src/test/java/org/orekit/propagation/events/AbstractDetectorTest.java b/src/test/java/org/orekit/propagation/events/AbstractDetectorTest.java new file mode 100644 index 0000000000..2a924ffeae --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/AbstractDetectorTest.java @@ -0,0 +1,59 @@ +/* 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.propagation.events; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.handlers.EventHandler; + +class AbstractDetectorTest { + + @Test + void testWithDetectionSettings() { + // GIVEN + final EventDetectionSettings settings = EventDetectionSettings.getDefaultEventDetectionSettings(); + final TestDetector testDetector = new TestDetector(settings, null); + final AdaptableInterval mockedInterval = Mockito.mock(AdaptableInterval.class); + final EventDetectionSettings expectedSettings = new EventDetectionSettings(mockedInterval, 10., 100); + // WHEN + final TestDetector newDetector = testDetector.withDetectionSettings(expectedSettings); + // THEN + Assertions.assertEquals(mockedInterval, newDetector.getDetectionSettings().getMaxCheckInterval()); + Assertions.assertEquals(expectedSettings.getThreshold(), newDetector.getDetectionSettings().getThreshold()); + Assertions.assertEquals(expectedSettings.getMaxIterationCount(), newDetector.getDetectionSettings().getMaxIterationCount()); + } + + private static class TestDetector extends AbstractDetector { + + protected TestDetector(EventDetectionSettings eventDetectionSettings, EventHandler handler) { + super(eventDetectionSettings, handler); + } + + @Override + protected TestDetector create(AdaptableInterval newMaxCheck, double newThreshold, int newMaxIter, EventHandler newHandler) { + return new TestDetector(new EventDetectionSettings(newMaxCheck, newThreshold, newMaxIter), newHandler); + } + + @Override + public double g(SpacecraftState s) { + return 0; + } + } + +} diff --git a/src/test/java/org/orekit/propagation/events/CloseEventsAbstractTest.java b/src/test/java/org/orekit/propagation/events/CloseEventsAbstractTest.java index 610d3bc547..276a9734ba 100644 --- a/src/test/java/org/orekit/propagation/events/CloseEventsAbstractTest.java +++ b/src/test/java/org/orekit/propagation/events/CloseEventsAbstractTest.java @@ -2188,7 +2188,7 @@ private TimeDetector(AdaptableInterval newMaxCheck, int newMaxIter, EventHandler newHandler, List dates) { - super(newMaxCheck, newThreshold, newMaxIter, newHandler); + super(new EventDetectionSettings(newMaxCheck, newThreshold, newMaxIter), newHandler); this.eventTs = dates; } @@ -2242,7 +2242,7 @@ private FlatDetector(AdaptableInterval newMaxCheck, int newMaxIter, EventHandler newHandler, EventDetector g) { - super(newMaxCheck, newThreshold, newMaxIter, newHandler); + super(new EventDetectionSettings(newMaxCheck, newThreshold, newMaxIter), newHandler); this.g = g; } @@ -2277,7 +2277,7 @@ private ContinuousDetector(AdaptableInterval newMaxCheck, int newMaxIter, EventHandler newHandler, List eventDates) { - super(newMaxCheck, newThreshold, newMaxIter, newHandler); + super(new EventDetectionSettings(newMaxCheck, newThreshold, newMaxIter), newHandler); this.eventTs = eventDates; } @@ -2407,7 +2407,7 @@ public ResetChangesSignGenerator(final AbsoluteDate t0, final double y1, final d private ResetChangesSignGenerator(final AdaptableInterval newMaxCheck, final double newThreshold, final int newMaxIter, final EventHandler newHandler, final AbsoluteDate t0, final double y1, final double y2, final double change ) { - super(newMaxCheck, newThreshold, newMaxIter, newHandler); + super(new EventDetectionSettings(newMaxCheck, newThreshold, newMaxIter), newHandler); this.t0 = t0; this.y1 = y1; this.y2 = y2; diff --git a/src/test/java/org/orekit/propagation/events/CylindricalShadowEclipseDetectorTest.java b/src/test/java/org/orekit/propagation/events/CylindricalShadowEclipseDetectorTest.java index 0c35c08207..5826255038 100644 --- a/src/test/java/org/orekit/propagation/events/CylindricalShadowEclipseDetectorTest.java +++ b/src/test/java/org/orekit/propagation/events/CylindricalShadowEclipseDetectorTest.java @@ -6,10 +6,19 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.orekit.Utils; +import org.orekit.bodies.AnalyticalSolarPositionProvider; import org.orekit.bodies.CelestialBodyFactory; +import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.analytical.KeplerianPropagator; import org.orekit.propagation.events.handlers.ContinueOnEvent; +import org.orekit.propagation.events.handlers.EventHandler; +import org.orekit.propagation.events.handlers.RecordAndContinue; import org.orekit.time.AbsoluteDate; import org.orekit.utils.*; @@ -20,18 +29,32 @@ public void setUp() { Utils.setDataRoot("regular-data"); } + @Test + @Deprecated + void testConstructor() { + // GIVEN + final EventDetectionSettings settings = EventDetectionSettings.getDefaultEventDetectionSettings(); + // WHEN + final CylindricalShadowEclipseDetector detector = new CylindricalShadowEclipseDetector(Mockito.mock(PVCoordinatesProvider.class), + 1., settings.getMaxCheckInterval(), settings.getThreshold(), settings.getMaxIterationCount(), + Mockito.mock(EventHandler.class)); + // THEN + Assertions.assertEquals(settings.getMaxIterationCount(), detector.getDetectionSettings().getMaxIterationCount()); + Assertions.assertEquals(settings.getThreshold(), detector.getDetectionSettings().getThreshold()); + } + @Test void testCreate() { // GIVEN - final ExtendedPVCoordinatesProvider sun = CelestialBodyFactory.getSun(); + final ExtendedPositionProvider sun = CelestialBodyFactory.getSun(); final CylindricalShadowEclipseDetector eclipseDetector = new CylindricalShadowEclipseDetector(sun, Constants.EGM96_EARTH_EQUATORIAL_RADIUS, new ContinueOnEvent()); final AdaptableInterval adaptableInterval = AdaptableInterval.of(1.); final double expectedThreshold = 0.1; final int expectedMaxIter = 10; // WHEN - final CylindricalShadowEclipseDetector detector = eclipseDetector.create(adaptableInterval, expectedThreshold, - expectedMaxIter, eclipseDetector.getHandler()); + final CylindricalShadowEclipseDetector detector = eclipseDetector.create(new EventDetectionSettings(adaptableInterval, expectedThreshold, + expectedMaxIter), eclipseDetector.getHandler()); // THEN Assertions.assertEquals(expectedMaxIter, detector.getMaxIterationCount()); Assertions.assertEquals(expectedThreshold, detector.getThreshold()); @@ -108,4 +131,42 @@ public TimeStampedPVCoordinates getPVCoordinates(AbsoluteDate date, Frame frame) } } + @Test + void testDetections() { + // GIVEN + final ExtendedPositionProvider positionProvider = new AnalyticalSolarPositionProvider(); + final RecordAndContinue recordAndContinue = new RecordAndContinue(); + final CylindricalShadowEclipseDetector cylindricalShadowEclipseDetector = new CylindricalShadowEclipseDetector(positionProvider, + Constants.EGM96_EARTH_EQUATORIAL_RADIUS, recordAndContinue); + final Orbit initialOrbit = getOrbit(); + final KeplerianPropagator propagator = new KeplerianPropagator(initialOrbit); + propagator.addEventDetector(cylindricalShadowEclipseDetector); + final AbsoluteDate targetDate = initialOrbit.getDate().shiftedBy(initialOrbit.getKeplerianPeriod() * 200); + // WHEN + propagator.propagate(targetDate); + // THEN + propagator.clearEventsDetectors(); + propagator.resetInitialState(new SpacecraftState(initialOrbit)); + final RecordAndContinue recordAndContinue2 = new RecordAndContinue(); + final EclipseDetector eclipseDetector = new EclipseDetector(getExtendedPVCoordinatesProvider(positionProvider), + Constants.SUN_RADIUS, new OneAxisEllipsoid(cylindricalShadowEclipseDetector.getOccultingBodyRadius(), 0., FramesFactory.getGTOD(false))); + propagator.addEventDetector(eclipseDetector.withPenumbra().withHandler(recordAndContinue2)); + propagator.propagate(targetDate); + Assertions.assertEquals(400, recordAndContinue2.getEvents().size()); + Assertions.assertEquals(recordAndContinue.getEvents().size(), recordAndContinue2.getEvents().size()); + for (int i = 0; i < recordAndContinue.getEvents().size(); i++) { + Assertions.assertEquals(0., recordAndContinue.getEvents().get(i).getState().durationFrom(recordAndContinue2.getEvents().get(i).getState()), + 10.); + } + } + + private static ExtendedPVCoordinatesProvider getExtendedPVCoordinatesProvider(final ExtendedPositionProvider positionProvider) { + return positionProvider::getPVCoordinates; + } + + private static KeplerianOrbit getOrbit() { + return new KeplerianOrbit(Constants.EGM96_EARTH_EQUATORIAL_RADIUS + 600e3, 0.001, 0.1, 2, 3, 4, PositionAngleType.ECCENTRIC, + FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, Constants.EGM96_EARTH_MU); + } + } diff --git a/src/test/java/org/orekit/propagation/events/EventDetectionSettingsTest.java b/src/test/java/org/orekit/propagation/events/EventDetectionSettingsTest.java new file mode 100644 index 0000000000..6391fbff83 --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/EventDetectionSettingsTest.java @@ -0,0 +1,38 @@ +/* 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.propagation.events; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.propagation.SpacecraftState; + +class EventDetectionSettingsTest { + + @Test + void testGetDefaultEventDetectionSettings() { + // GIVEN + + // WHEN + final EventDetectionSettings defaultSettings = EventDetectionSettings.getDefaultEventDetectionSettings(); + // THEN + Assertions.assertEquals(FieldEventDetectionSettings.DEFAULT_MAX_ITER, defaultSettings.getMaxIterationCount()); + Assertions.assertEquals(FieldEventDetectionSettings.DEFAULT_THRESHOLD, defaultSettings.getThreshold()); + Assertions.assertEquals(FieldEventDetectionSettings.DEFAULT_MAXCHECK, defaultSettings.getMaxCheckInterval() + .currentInterval(Mockito.mock(SpacecraftState.class))); + } +} diff --git a/src/test/java/org/orekit/propagation/events/EventDetectorTest.java b/src/test/java/org/orekit/propagation/events/EventDetectorTest.java index 643ac60b93..a6234fce9e 100644 --- a/src/test/java/org/orekit/propagation/events/EventDetectorTest.java +++ b/src/test/java/org/orekit/propagation/events/EventDetectorTest.java @@ -31,6 +31,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.orekit.Utils; import org.orekit.errors.OrekitException; import org.orekit.frames.Frame; @@ -60,6 +61,45 @@ public class EventDetectorTest { private double mu; + @Test + void testFinish() { + // GIVEN + final FinishingHandler handler = new FinishingHandler(); + final EventDetector detector = + new DummyDetector(new EventDetectionSettings(1.0, 1.0e-10, 100), handler); + // WHEN + detector.finish(Mockito.mock(SpacecraftState.class)); + // THEN + Assertions.assertTrue(handler.isFinished); + } + + private static class FinishingHandler extends ContinueOnEvent { + boolean isFinished = false; + + @Override + public void finish(SpacecraftState finalState, EventDetector detector) { + isFinished = true; + } + } + + private static class DummyDetector + extends AbstractDetector { + + public DummyDetector(final EventDetectionSettings detectionSettings, final EventHandler handler) { + super(detectionSettings, handler); + } + + public double g(final SpacecraftState s) { + return 0; + } + + protected DummyDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, final EventHandler newHandler) { + return new DummyDetector(new EventDetectionSettings(newMaxCheck, newThreshold, newMaxIter), newHandler); + } + + } + @Test public void testEventHandlerInit() { final TimeScale utc = TimeScalesFactory.getUTC(); @@ -152,11 +192,6 @@ public void handleStep(SpacecraftState currentState) { public boolean outOfOrderCallDetected() { return outOfOrderCallDetected; } - - @Override - public void init(SpacecraftState initialState, AbsoluteDate target, double step) { - } - } @Test @@ -481,6 +516,20 @@ void callDate(final AbsoluteDate date) { } + @Test + void testGetDetectionSettings() { + // GIVEN + final AdaptableInterval mockedInterval = Mockito.mock(AdaptableInterval.class); + final EventDetectionSettings settings = new EventDetectionSettings(mockedInterval, 10, 19); + final EventDetector detector = new DummyDetector(settings, null); + // WHEN + final EventDetectionSettings actualSettings = detector.getDetectionSettings(); + // THEN + Assertions.assertEquals(mockedInterval, actualSettings.getMaxCheckInterval()); + Assertions.assertEquals(settings.getMaxIterationCount(), actualSettings.getMaxIterationCount()); + Assertions.assertEquals(settings.getThreshold(), actualSettings.getThreshold()); + } + @BeforeEach public void setUp() { Utils.setDataRoot("regular-data"); diff --git a/src/test/java/org/orekit/propagation/events/EventSlopeFilterTest.java b/src/test/java/org/orekit/propagation/events/EventSlopeFilterTest.java index 9f682d40fb..7e71a566aa 100644 --- a/src/test/java/org/orekit/propagation/events/EventSlopeFilterTest.java +++ b/src/test/java/org/orekit/propagation/events/EventSlopeFilterTest.java @@ -22,6 +22,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.orekit.Utils; import org.orekit.bodies.CelestialBodyFactory; import org.orekit.bodies.OneAxisEllipsoid; @@ -48,6 +49,14 @@ public class EventSlopeFilterTest { private double sunRadius = 696000000.; private double earthRadius = 6400000.; + @Test + void testFinish() { + // + final ApsideDetector detector = new ApsideDetector(1.); + final EventSlopeFilter slopeFilter = new EventSlopeFilter<>(detector, FilterType.TRIGGER_ONLY_INCREASING_EVENTS); + slopeFilter.getHandler().finish(Mockito.mock(SpacecraftState.class), detector); + } + @Test public void testEnums() { // this test is here only for test coverage ... diff --git a/src/test/java/org/orekit/propagation/events/FieldAbstractDetectorTest.java b/src/test/java/org/orekit/propagation/events/FieldAbstractDetectorTest.java new file mode 100644 index 0000000000..b44694256b --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/FieldAbstractDetectorTest.java @@ -0,0 +1,64 @@ +/* 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.propagation.events; + +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.handlers.FieldEventHandler; + +class FieldAbstractDetectorTest { + + @Test + void testWithDetectionSettings() { + // GIVEN + final ComplexField field = ComplexField.getInstance(); + final FieldEventDetectionSettings settings = new FieldEventDetectionSettings<>(field, + EventDetectionSettings.getDefaultEventDetectionSettings()); + final TestFieldDetector testDetector = new TestFieldDetector(settings, null); + final FieldAdaptableInterval mockedInterval = Mockito.mock(FieldAdaptableInterval.class); + final FieldEventDetectionSettings expectedSettings = new FieldEventDetectionSettings<>(mockedInterval, + Complex.ONE, 100); + // WHEN + final TestFieldDetector newDetector = testDetector.withDetectionSettings(expectedSettings); + // THEN + Assertions.assertEquals(mockedInterval, newDetector.getDetectionSettings().getMaxCheckInterval()); + Assertions.assertEquals(expectedSettings.getThreshold(), newDetector.getDetectionSettings().getThreshold()); + Assertions.assertEquals(expectedSettings.getMaxIterationCount(), newDetector.getDetectionSettings().getMaxIterationCount()); + } + + private static class TestFieldDetector extends FieldAbstractDetector { + + protected TestFieldDetector(FieldEventDetectionSettings detectionSettings, FieldEventHandler handler) { + super(detectionSettings, handler); + } + + @Override + protected TestFieldDetector create(FieldAdaptableInterval newMaxCheck, Complex newThreshold, + int newMaxIter, FieldEventHandler newHandler) { + return new TestFieldDetector(new FieldEventDetectionSettings<>(newMaxCheck, newThreshold, newMaxIter), newHandler); + } + + @Override + public Complex g(FieldSpacecraftState s) { + return null; + } + } +} diff --git a/src/test/java/org/orekit/propagation/events/FieldCylindricalShadowEclipseDetectorTest.java b/src/test/java/org/orekit/propagation/events/FieldCylindricalShadowEclipseDetectorTest.java index dacafcddaf..c0eb588674 100644 --- a/src/test/java/org/orekit/propagation/events/FieldCylindricalShadowEclipseDetectorTest.java +++ b/src/test/java/org/orekit/propagation/events/FieldCylindricalShadowEclipseDetectorTest.java @@ -14,6 +14,7 @@ import org.orekit.frames.Frame; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.events.handlers.FieldContinueOnEvent; +import org.orekit.propagation.events.handlers.FieldEventHandler; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.*; @@ -25,6 +26,22 @@ public void setUp() { Utils.setDataRoot("regular-data"); } + @Test + @Deprecated + void testConstructor() { + // GIVEN + final EventDetectionSettings settings = EventDetectionSettings.getDefaultEventDetectionSettings(); + final FieldEventDetectionSettings fieldSettings = new FieldEventDetectionSettings<>(ComplexField.getInstance(), + settings); + // WHEN + final FieldCylindricalShadowEclipseDetector detector = new FieldCylindricalShadowEclipseDetector<>(Mockito.mock(ExtendedPositionProvider.class), + Complex.ONE, fieldSettings.getMaxCheckInterval(), fieldSettings.getThreshold(), fieldSettings.getMaxIterationCount(), + Mockito.mock(FieldEventHandler.class)); + // THEN + Assertions.assertEquals(fieldSettings.getMaxIterationCount(), detector.getDetectionSettings().getMaxIterationCount()); + Assertions.assertEquals(fieldSettings.getThreshold(), detector.getDetectionSettings().getThreshold()); + } + @Test void testCreate() { // GIVEN diff --git a/src/test/java/org/orekit/propagation/events/FieldEventDetectionSettingsTest.java b/src/test/java/org/orekit/propagation/events/FieldEventDetectionSettingsTest.java new file mode 100644 index 0000000000..390486f666 --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/FieldEventDetectionSettingsTest.java @@ -0,0 +1,59 @@ +package org.orekit.propagation.events; + +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.frames.FramesFactory; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.AbsolutePVCoordinates; +import org.orekit.utils.PVCoordinates; + +class FieldEventDetectionSettingsTest { + + @Test + void testConstructor() { + // GIVEN + final double maxCheck = 100.; + // WHEN + final FieldEventDetectionSettings fieldEventDetectionSettings = new FieldEventDetectionSettings<>(maxCheck, + new Complex(20.), 10); + // THEN + Assertions.assertEquals(maxCheck, fieldEventDetectionSettings.getMaxCheckInterval().currentInterval(null)); + } + + @Test + void testConstructorFromNonField() { + // GIVEN + final EventDetectionSettings settings = new EventDetectionSettings(AdaptableInterval.of(10), 100., 1000); + // WHEN + final FieldEventDetectionSettings fieldEventDetectionSettings = new FieldEventDetectionSettings<>(ComplexField.getInstance(), + settings); + // THEN + Assertions.assertEquals(settings.getMaxIterationCount(), fieldEventDetectionSettings.getMaxIterationCount()); + Assertions.assertEquals(settings.getThreshold(), fieldEventDetectionSettings.getThreshold().getReal()); + final SpacecraftState state = new SpacecraftState(new AbsolutePVCoordinates(FramesFactory.getGCRF(), + AbsoluteDate.ARBITRARY_EPOCH, new PVCoordinates())); + Assertions.assertEquals(fieldEventDetectionSettings.getMaxCheckInterval().currentInterval(new FieldSpacecraftState<>(ComplexField.getInstance(), state)), + settings.getMaxCheckInterval().currentInterval(state)); + } + + @Test + void testToEventDetectionSettings() { + // GIVEN + final FieldAdaptableInterval interval = FieldAdaptableInterval.of(1.); + final FieldEventDetectionSettings fieldEventDetectionSettings = new FieldEventDetectionSettings<>(interval, + new Complex(20.), 10); + // WHEN + final EventDetectionSettings eventDetectionSettings = fieldEventDetectionSettings.toEventDetectionSettings(); + // THEN + Assertions.assertEquals(fieldEventDetectionSettings.getMaxIterationCount(), eventDetectionSettings.getMaxIterationCount()); + Assertions.assertEquals(fieldEventDetectionSettings.getThreshold().getReal(), eventDetectionSettings.getThreshold()); + final SpacecraftState state = new SpacecraftState(new AbsolutePVCoordinates(FramesFactory.getGCRF(), + AbsoluteDate.ARBITRARY_EPOCH, new PVCoordinates())); + Assertions.assertEquals(fieldEventDetectionSettings.getMaxCheckInterval().currentInterval(new FieldSpacecraftState<>(ComplexField.getInstance(), state)), + eventDetectionSettings.getMaxCheckInterval().currentInterval(state)); + } +} diff --git a/src/test/java/org/orekit/propagation/events/FieldEventDetectorTest.java b/src/test/java/org/orekit/propagation/events/FieldEventDetectorTest.java index 9f102b02d6..e63f1d5753 100644 --- a/src/test/java/org/orekit/propagation/events/FieldEventDetectorTest.java +++ b/src/test/java/org/orekit/propagation/events/FieldEventDetectorTest.java @@ -24,6 +24,7 @@ import org.hamcrest.MatcherAssert; import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; +import org.hipparchus.complex.Complex; import org.hipparchus.exception.LocalizedCoreFormats; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.ode.FieldODEIntegrator; @@ -35,6 +36,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.orekit.Utils; import org.orekit.errors.OrekitException; import org.orekit.frames.Frame; @@ -65,6 +67,59 @@ public class FieldEventDetectorTest { private double mu; + @Test + void testFinish() { + // GIVEN + final FinishingHandler handler = new FinishingHandler(); + final FieldEventDetector detector = + new DummyDetector(new FieldEventDetectionSettings<>(1.0, Complex.ONE, 100), handler); + // WHEN + detector.finish(Mockito.mock(FieldSpacecraftState.class)); + // THEN + Assertions.assertTrue(handler.isFinished); + } + + private static class FinishingHandler extends FieldContinueOnEvent { + boolean isFinished = false; + + @Override + public void finish(FieldSpacecraftState finalState, FieldEventDetector detector) { + isFinished = true; + } + } + + private static class DummyDetector> + extends FieldAbstractDetector, T> { + + public DummyDetector(final FieldEventDetectionSettings detectionSettings, final FieldEventHandler handler) { + super(detectionSettings, handler); + } + + public T g(final FieldSpacecraftState s) { + return s.getDate().getField().getZero(); + } + + protected DummyDetector create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, + final int newMaxIter, final FieldEventHandler newHandler) { + return new DummyDetector(new FieldEventDetectionSettings<>(newMaxCheck, newThreshold, newMaxIter), newHandler); + } + + } + + @Test + void testGetDetectionSettings() { + // GIVEN + final FieldAdaptableInterval mockedInterval = Mockito.mock(FieldAdaptableInterval.class); + final FieldEventDetectionSettings settings = new FieldEventDetectionSettings<>(mockedInterval, Complex.ONE, 10); + final FieldEventDetector detector = new DummyDetector(settings, null); + // WHEN + final FieldEventDetectionSettings actualSettings = detector.getDetectionSettings(); + // THEN + Assertions.assertEquals(mockedInterval, actualSettings.getMaxCheckInterval()); + Assertions.assertEquals(settings.getMaxIterationCount(), actualSettings.getMaxIterationCount()); + Assertions.assertEquals(settings.getThreshold(), actualSettings.getThreshold()); + } + @Test public void testEventHandlerInit() { doTestEventHandlerInit(Binary64Field.getInstance()); diff --git a/src/test/java/org/orekit/propagation/events/handlers/EventHandlerTest.java b/src/test/java/org/orekit/propagation/events/handlers/EventHandlerTest.java index d0192b6071..86ad6ab769 100644 --- a/src/test/java/org/orekit/propagation/events/handlers/EventHandlerTest.java +++ b/src/test/java/org/orekit/propagation/events/handlers/EventHandlerTest.java @@ -19,6 +19,7 @@ import org.hipparchus.ode.events.Action; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.events.AdaptableInterval; import org.orekit.propagation.events.EventDetector; @@ -53,6 +54,15 @@ public void testIssue721() { } + @Test + void testFinish() { + // GIVEN + final Handler handler = new Handler(); + // WHEN & THEN + Assertions.assertDoesNotThrow(() -> handler.finish(Mockito.mock(SpacecraftState.class), + Mockito.mock(EventDetector.class))); + } + private static class Detector implements EventDetector { private boolean initialized; diff --git a/src/test/java/org/orekit/propagation/events/handlers/EventMultipleHandlerTest.java b/src/test/java/org/orekit/propagation/events/handlers/EventMultipleHandlerTest.java index 8bacf49769..8e125e65f6 100644 --- a/src/test/java/org/orekit/propagation/events/handlers/EventMultipleHandlerTest.java +++ b/src/test/java/org/orekit/propagation/events/handlers/EventMultipleHandlerTest.java @@ -20,6 +20,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.orekit.Utils; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; @@ -44,6 +45,24 @@ public void setUp() { Utils.setDataRoot("regular-data"); } + @Test + void testFinishEmpty() { + final EventHandler mockedHandler = Mockito.mock(EventHandler.class); + final SpacecraftState mockedState = Mockito.mock(SpacecraftState.class); + final EventDetector mockedDetector = Mockito.mock(EventDetector.class); + final EventMultipleHandler eventMultipleHandler = new EventMultipleHandler(); + Assertions.assertDoesNotThrow(() -> eventMultipleHandler.finish(mockedState, mockedDetector)); + } + + @Test + void testFinish() { + final EventHandler mockedHandler = Mockito.mock(EventHandler.class); + final SpacecraftState mockedState = Mockito.mock(SpacecraftState.class); + final EventDetector mockedDetector = Mockito.mock(EventDetector.class); + final EventMultipleHandler eventMultipleHandler = new EventMultipleHandler().addHandler(mockedHandler); + Assertions.assertDoesNotThrow(() -> eventMultipleHandler.finish(mockedState, mockedDetector)); + } + /** * check eventOccurred method. */ diff --git a/src/test/java/org/orekit/propagation/events/handlers/FieldEventHandlerTest.java b/src/test/java/org/orekit/propagation/events/handlers/FieldEventHandlerTest.java new file mode 100644 index 0000000000..58f6756e8f --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/handlers/FieldEventHandlerTest.java @@ -0,0 +1,47 @@ +/* Copyright 2022-2024 Romain Serra + * Licensed to CS Group 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.propagation.events.handlers; + + +import org.hipparchus.complex.Complex; +import org.hipparchus.ode.events.Action; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.FieldEventDetector; + +class FieldEventHandlerTest { + + @Test + @SuppressWarnings("unchecked") + void testFinish() { + // GIVEN + final TestFieldHandler handler = new TestFieldHandler(); + // WHEN & THEN + Assertions.assertDoesNotThrow(() -> handler.finish(Mockito.mock(FieldSpacecraftState.class), + Mockito.mock(FieldEventDetector.class))); + } + + private static class TestFieldHandler implements FieldEventHandler { + + @Override + public Action eventOccurred(FieldSpacecraftState s, FieldEventDetector detector, boolean increasing) { + return null; + } + } +} diff --git a/src/test/java/org/orekit/propagation/events/handlers/FieldRecallLastOccurrenceTest.java b/src/test/java/org/orekit/propagation/events/handlers/FieldRecallLastOccurrenceTest.java index f7ce34c621..eadc83e295 100644 --- a/src/test/java/org/orekit/propagation/events/handlers/FieldRecallLastOccurrenceTest.java +++ b/src/test/java/org/orekit/propagation/events/handlers/FieldRecallLastOccurrenceTest.java @@ -45,6 +45,19 @@ void testEventOccurred() { Assertions.assertEquals(ACTION, action); } + @Test + void testFinish() { + // GIVEN + final TestHandler testHandler = new TestHandler(); + final FieldRecallLastOccurrence recallLastOccurrence = new FieldRecallLastOccurrence<>(testHandler); + final FieldAbsoluteDate expectedDate = FieldAbsoluteDate.getArbitraryEpoch(ComplexField.getInstance()); + final FieldSpacecraftState mockedState = mockState(expectedDate); + // WHEN + recallLastOccurrence.finish(mockedState, null); + // THEN + Assertions.assertTrue(testHandler.isFinished); + } + @Test void testResetState() { // GIVEN @@ -81,6 +94,7 @@ private FieldSpacecraftState mockState(final FieldAbsoluteDate private static class TestHandler implements FieldEventHandler { boolean isInitialized = false; + boolean isFinished = false; @Override public void init(FieldSpacecraftState initialState, FieldAbsoluteDate target, FieldEventDetector detector) { @@ -92,6 +106,10 @@ public Action eventOccurred(FieldSpacecraftState s, FieldEventDetector< return ACTION; } + @Override + public void finish(FieldSpacecraftState finalState, FieldEventDetector detector) { + isFinished = true; + } } } diff --git a/src/test/java/org/orekit/propagation/events/handlers/FieldResetDerivativesOnEventTest.java b/src/test/java/org/orekit/propagation/events/handlers/FieldResetDerivativesOnEventTest.java new file mode 100644 index 0000000000..d78b8e3320 --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/handlers/FieldResetDerivativesOnEventTest.java @@ -0,0 +1,37 @@ +/* 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.propagation.events.handlers; + +import org.hipparchus.complex.Complex; +import org.hipparchus.ode.events.Action; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class FieldResetDerivativesOnEventTest { + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testEventOccurred(final boolean increasing) { + // GIVEN + final FieldResetDerivativesOnEvent handler = new FieldResetDerivativesOnEvent<>(); + // WHEN + final Action action = handler.eventOccurred(null, null, increasing); + // THEN + Assertions.assertEquals(Action.RESET_DERIVATIVES, action); + } +} diff --git a/src/test/java/org/orekit/propagation/events/handlers/RecallLastOccurrenceTest.java b/src/test/java/org/orekit/propagation/events/handlers/RecallLastOccurrenceTest.java index 5a45b08aa8..1020ad302b 100644 --- a/src/test/java/org/orekit/propagation/events/handlers/RecallLastOccurrenceTest.java +++ b/src/test/java/org/orekit/propagation/events/handlers/RecallLastOccurrenceTest.java @@ -28,6 +28,18 @@ class RecallLastOccurrenceTest { private static final Action ACTION = Action.CONTINUE; + @Test + void testFinish() { + // GIVEN + final TestHandler testHandler = new TestHandler(); + final RecallLastOccurrence recallLastOccurrence = new RecallLastOccurrence(testHandler); + final SpacecraftState mockedState = mockState(AbsoluteDate.ARBITRARY_EPOCH); + // WHEN + recallLastOccurrence.finish(mockedState, null); + // THEN + Assertions.assertEquals(true, testHandler.isFinished); + } + @Test void testEventOccurred() { // GIVEN @@ -91,6 +103,8 @@ private static class TestHandler implements EventHandler { boolean isInitialized = false; + boolean isFinished = false; + @Override public void init(SpacecraftState initialState, AbsoluteDate target, EventDetector detector) { isInitialized = true; @@ -101,6 +115,10 @@ public Action eventOccurred(SpacecraftState s, EventDetector detector, boolean i return ACTION; } + @Override + public void finish(SpacecraftState finalState, EventDetector detector) { + isFinished = true; + } } } diff --git a/src/test/java/org/orekit/propagation/events/handlers/ResetDerivativesOnEventTest.java b/src/test/java/org/orekit/propagation/events/handlers/ResetDerivativesOnEventTest.java new file mode 100644 index 0000000000..6fe6bdba08 --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/handlers/ResetDerivativesOnEventTest.java @@ -0,0 +1,36 @@ +/* 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.propagation.events.handlers; + +import org.hipparchus.ode.events.Action; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class ResetDerivativesOnEventTest { + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testEventOccurred(final boolean increasing) { + // GIVEN + final ResetDerivativesOnEvent handler = new ResetDerivativesOnEvent(); + // WHEN + final Action action = handler.eventOccurred(null, null, increasing); + // THEN + Assertions.assertEquals(Action.RESET_DERIVATIVES, action); + } +} diff --git a/src/test/java/org/orekit/propagation/integration/AbstractIntegratedPropagatorTest.java b/src/test/java/org/orekit/propagation/integration/AbstractIntegratedPropagatorTest.java index b07b7593c4..a8862fa827 100644 --- a/src/test/java/org/orekit/propagation/integration/AbstractIntegratedPropagatorTest.java +++ b/src/test/java/org/orekit/propagation/integration/AbstractIntegratedPropagatorTest.java @@ -89,6 +89,44 @@ public void testIssue1254() { Assertions.assertEquals(0., ephemeris.getMinDate().durationFrom(minDate), 0.); Assertions.assertEquals(0., ephemeris.getMaxDate().durationFrom(maxDate), 0.); } + + /** Test issue 1461. + *

      Test for the new generic method AbstractIntegratedPropagator.reset(SpacecraftState, PropagationType) + */ + @Test + public void testIssue1461() { + // GIVEN + // GEO orbit + final Orbit startOrbit = new EquinoctialOrbit(42165765.0, 0.0, 0.0, 0.0, 0.0, 0.0, PositionAngleType.TRUE, + FramesFactory.getEME2000(), AbsoluteDate.J2000_EPOCH, + Constants.IERS2010_EARTH_MU); + + // Init numerical propagator + final SpacecraftState state = new SpacecraftState(startOrbit); + final NumericalPropagator propagator = new NumericalPropagator(new ClassicalRungeKuttaIntegrator(300.)); + propagator.setInitialState(state); + + // WHEN + propagator.resetInitialState(state, PropagationType.OSCULATING); + final SpacecraftState oscState = propagator.getInitialState(); + + propagator.resetInitialState(state, PropagationType.MEAN); + final SpacecraftState meanState = propagator.getInitialState(); + + // THEN + + // Check that all three states are identical + final double dpOsc = oscState.getPosition().distance(state.getPosition()); + final double dvOsc = oscState.getPVCoordinates().getVelocity().distance(state.getPVCoordinates().getVelocity()); + + final double dpMean = meanState.getPosition().distance(state.getPosition()); + final double dvMean = meanState.getPVCoordinates().getVelocity().distance(state.getPVCoordinates().getVelocity()); + + Assertions.assertEquals(0., dpOsc, 0.); + Assertions.assertEquals(0., dvOsc, 0.); + Assertions.assertEquals(0., dpMean, 0.); + Assertions.assertEquals(0., dvMean, 0.); + } private static class TestAbstractIntegratedPropagator extends AbstractIntegratedPropagator { diff --git a/src/test/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagatorTest.java b/src/test/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagatorTest.java index 1551d929a8..499db2ffc6 100644 --- a/src/test/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagatorTest.java +++ b/src/test/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagatorTest.java @@ -16,18 +16,30 @@ */ package org.orekit.propagation.integration; +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.complex.Complex; import org.hipparchus.complex.ComplexField; import org.hipparchus.ode.FieldODEIntegrator; +import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaFieldIntegrator; +import org.hipparchus.util.Binary64Field; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.orekit.attitudes.AttitudeProvider; import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.EquinoctialOrbit; +import org.orekit.orbits.FieldEquinoctialOrbit; +import org.orekit.orbits.FieldOrbit; import org.orekit.orbits.OrbitType; import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.PropagationType; +import org.orekit.propagation.numerical.FieldNumericalPropagator; +import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.Constants; class FieldAbstractIntegratedPropagatorTest { @@ -50,6 +62,53 @@ void testGetResetAtEnd(final boolean expectedResetAtEnd) { final boolean actualResetAtEnd = testAbstractIntegratedPropagator.getResetAtEnd(); Assertions.assertEquals(expectedResetAtEnd, actualResetAtEnd); } + + /** Test issue 1461. + *

      Test for the new generic method AbstractIntegratedPropagator.reset(SpacecraftState, PropagationType) + */ + @Test + void testIssue1461() { + doTestIssue1461(Binary64Field.getInstance()); + } + + /** Method for running test for issue 1461. */ + private > void doTestIssue1461(Field field) { + // GIVEN + + final T zero = field.getZero(); + // GEO orbit + final FieldOrbit startOrbit = new FieldEquinoctialOrbit<>(field, + new EquinoctialOrbit(42165765.0, 0.0, 0.0, 0.0, 0.0, 0.0, PositionAngleType.TRUE, + FramesFactory.getEME2000(), AbsoluteDate.J2000_EPOCH, + Constants.IERS2010_EARTH_MU)); + + // Init numerical propagator + final FieldSpacecraftState state = new FieldSpacecraftState<>(startOrbit); + final FieldNumericalPropagator propagator = new FieldNumericalPropagator<>(field, + new ClassicalRungeKuttaFieldIntegrator(field, zero.newInstance(300.))); + propagator.setInitialState(state); + + // WHEN + propagator.resetInitialState(state, PropagationType.OSCULATING); + final FieldSpacecraftState oscState = propagator.getInitialState(); + + propagator.resetInitialState(state, PropagationType.MEAN); + final FieldSpacecraftState meanState = propagator.getInitialState(); + + // THEN + + // Check that all three states are identical + final double dpOsc = oscState.getPosition().distance(state.getPosition()).getReal(); + final double dvOsc = oscState.getPVCoordinates().getVelocity().distance(state.getPVCoordinates().getVelocity()).getReal(); + + final double dpMean = meanState.getPosition().distance(state.getPosition()).getReal(); + final double dvMean = meanState.getPVCoordinates().getVelocity().distance(state.getPVCoordinates().getVelocity()).getReal(); + + Assertions.assertEquals(0., dpOsc, 0.); + Assertions.assertEquals(0., dvOsc, 0.); + Assertions.assertEquals(0., dpMean, 0.); + Assertions.assertEquals(0., dvMean, 0.); + } private static class TestFieldAbstractIntegratedPropagator extends FieldAbstractIntegratedPropagator { diff --git a/src/test/java/org/orekit/propagation/numerical/FieldNumericalPropagatorTest.java b/src/test/java/org/orekit/propagation/numerical/FieldNumericalPropagatorTest.java index 9ce620d44f..b6e4a87a4d 100644 --- a/src/test/java/org/orekit/propagation/numerical/FieldNumericalPropagatorTest.java +++ b/src/test/java/org/orekit/propagation/numerical/FieldNumericalPropagatorTest.java @@ -38,7 +38,6 @@ import org.hipparchus.ode.events.Action; import org.hipparchus.ode.nonstiff.AdaptiveStepsizeFieldIntegrator; import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaFieldIntegrator; -import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaIntegrator; import org.hipparchus.ode.nonstiff.DormandPrince853FieldIntegrator; import org.hipparchus.util.Binary64Field; import org.hipparchus.util.FastMath; @@ -76,16 +75,10 @@ import org.orekit.propagation.PropagationType; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.DateDetector; -import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.events.FieldAbstractDetector; -import org.orekit.propagation.events.FieldAdaptableInterval; -import org.orekit.propagation.events.FieldApsideDetector; -import org.orekit.propagation.events.FieldDateDetector; +import org.orekit.propagation.events.*; import org.orekit.propagation.events.handlers.FieldContinueOnEvent; import org.orekit.propagation.events.handlers.FieldEventHandler; import org.orekit.propagation.events.handlers.FieldStopOnEvent; -import org.orekit.propagation.events.FieldEventDetector; import org.orekit.propagation.integration.*; import org.orekit.propagation.sampling.FieldOrekitStepHandler; import org.orekit.propagation.sampling.FieldOrekitStepInterpolator; @@ -741,7 +734,7 @@ private > void doTestStopEvent(Field field) Assertions.assertEquals(0, finalState.getDate().durationFrom(stopDate).getReal(), 1.0e-10); propagator.clearEventsDetectors(); Assertions.assertEquals(0, propagator.getEventsDetectors().size()); - + Assertions.assertTrue(checking.isFinished); } @Test @@ -990,18 +983,18 @@ private static class AdditionalStateLinearDetector, T> { public AdditionalStateLinearDetector(T maxCheck, T threshold) { - this(FieldAdaptableInterval.of(maxCheck.getReal()), threshold, DEFAULT_MAX_ITER, new FieldStopOnEvent<>()); + this(new FieldEventDetectionSettings<>(maxCheck.getReal(), threshold, DEFAULT_MAX_ITER), new FieldStopOnEvent<>()); } - private AdditionalStateLinearDetector(FieldAdaptableInterval maxCheck, T threshold, int maxIter, + private AdditionalStateLinearDetector(FieldEventDetectionSettings detectionSettings, FieldEventHandler handler) { - super(maxCheck, threshold, maxIter, handler); + super(detectionSettings, handler); } protected AdditionalStateLinearDetector create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, final int newMaxIter, final FieldEventHandler newHandler) { - return new AdditionalStateLinearDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler); + return new AdditionalStateLinearDetector<>(new FieldEventDetectionSettings<>(newMaxCheck, newThreshold, newMaxIter), newHandler); } public T g(FieldSpacecraftState s) { @@ -2049,6 +2042,7 @@ private static class CheckingHandler> implemen private final Action actionOnEvent; private boolean gotHere; + private boolean isFinished = false; public CheckingHandler(final Action actionOnEvent) { this.actionOnEvent = actionOnEvent; @@ -2064,6 +2058,10 @@ public Action eventOccurred(FieldSpacecraftState s, FieldEventDetector det return actionOnEvent; } + @Override + public void finish(FieldSpacecraftState finalState, FieldEventDetector detector) { + isFinished = true; + } } private > FieldNumericalPropagator createPropagator(Field field) { diff --git a/src/test/java/org/orekit/propagation/numerical/NumericalPropagatorTest.java b/src/test/java/org/orekit/propagation/numerical/NumericalPropagatorTest.java index 5763e0c98e..50ca623b66 100644 --- a/src/test/java/org/orekit/propagation/numerical/NumericalPropagatorTest.java +++ b/src/test/java/org/orekit/propagation/numerical/NumericalPropagatorTest.java @@ -646,7 +646,7 @@ void testStopEvent() { Assertions.assertEquals(0, finalState.getDate().durationFrom(stopDate), 1.0e-10); propagator.clearEventsDetectors(); Assertions.assertEquals(0, propagator.getEventsDetectors().size()); - + Assertions.assertTrue(checking.isFinished); } @Test @@ -1052,6 +1052,7 @@ private static class CheckingHandler implements EventHandler { private final Action actionOnEvent; private boolean gotHere; + private boolean isFinished = false; public CheckingHandler(final Action actionOnEvent) { this.actionOnEvent = actionOnEvent; @@ -1067,6 +1068,10 @@ public Action eventOccurred(SpacecraftState s, EventDetector detector, boolean i return actionOnEvent; } + @Override + public void finish(SpacecraftState finalState, EventDetector detector) { + isFinished = true; + } } @Test @@ -1562,7 +1567,7 @@ void testAdditionalDerivatives() { final Orbit initialOrbit = new KeplerianOrbit(8000000.0, 0.01, 0.87, 2.44, 0.21, -1.05, PositionAngleType.MEAN, eme2000, date, Constants.EIGEN5C_EARTH_MU); - NumericalPropagatorBuilder builder = new NumericalPropagatorBuilder(initialOrbit, new DormandPrince853IntegratorBuilder(0.02, 0.2, 1.), PositionAngleType.TRUE, 10); + NumericalPropagatorBuilder builder = new NumericalPropagatorBuilder(initialOrbit, new DormandPrince853IntegratorBuilder(0.02, 0.2, 1., 0.0001), PositionAngleType.TRUE, 10); NumericalPropagator propagator = (NumericalPropagator) builder.buildPropagator(); IntStream. diff --git a/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTJ2SquaredClosedFormTest.java b/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTJ2SquaredClosedFormTest.java index f664d11c0c..e2638bd475 100644 --- a/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTJ2SquaredClosedFormTest.java +++ b/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTJ2SquaredClosedFormTest.java @@ -16,9 +16,6 @@ */ package org.orekit.propagation.semianalytical.dsst; -import java.io.IOException; -import java.text.ParseException; - import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.Gradient; import org.hipparchus.geometry.euclidean.threed.Vector3D; @@ -27,9 +24,11 @@ import org.hipparchus.util.Binary64; import org.hipparchus.util.Binary64Field; import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathArrays; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.orekit.Utils; import org.orekit.attitudes.Attitude; import org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel; @@ -37,13 +36,7 @@ import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; -import org.orekit.orbits.EquinoctialOrbit; -import org.orekit.orbits.FieldKeplerianOrbit; -import org.orekit.orbits.FieldOrbit; -import org.orekit.orbits.KeplerianOrbit; -import org.orekit.orbits.Orbit; -import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngleType; +import org.orekit.orbits.*; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.PropagationType; import org.orekit.propagation.SpacecraftState; @@ -60,6 +53,9 @@ import org.orekit.utils.Constants; import org.orekit.utils.IERSConventions; +import java.io.IOException; +import java.text.ParseException; + public class DSSTJ2SquaredClosedFormTest { private UnnormalizedSphericalHarmonicsProvider provider; @@ -70,6 +66,21 @@ public void setUp() throws IOException, ParseException { provider = GravityFieldFactory.getUnnormalizedProvider(2, 0); } + @SuppressWarnings("unchecked") + @Test + public void testShortPeriodZeis() { + DSSTJ2SquaredClosedForm j2Squared = new DSSTJ2SquaredClosedForm(new ZeisModel(), provider); + AuxiliaryElements auxiliaryElements = Mockito.mock(AuxiliaryElements.class); + FieldAuxiliaryElements fAuxiliaryElements = Mockito.mock(FieldAuxiliaryElements.class); + Binary64[] emptyArray = MathArrays.buildArray(Binary64Field.getInstance(), 0); + + Assertions.assertTrue(j2Squared.initializeShortPeriodTerms(auxiliaryElements, PropagationType.MEAN, new double[0]).isEmpty()); + Assertions.assertTrue(j2Squared.initializeShortPeriodTerms(fAuxiliaryElements, PropagationType.MEAN, emptyArray).isEmpty()); + j2Squared.updateShortPeriodTerms(new double[0]); + j2Squared.updateShortPeriodTerms(emptyArray); + Assertions.assertTrue(j2Squared.initializeShortPeriodTerms(auxiliaryElements, PropagationType.MEAN, new double[0]).isEmpty()); + Assertions.assertTrue(j2Squared.initializeShortPeriodTerms(fAuxiliaryElements, PropagationType.MEAN, emptyArray).isEmpty()); + } /** * Non regression test for mean element rates using Zeis formulation of J2-squared */ @@ -133,7 +144,7 @@ public void testFieldGetMeanElementRateZeis() { private void doTestComparisonWithNumerical(final double perigeeAltitude, final double apogeeAltitude, final double currentDifferenceWithoutJ2Squared, - final double currenDifferenceWithJ2Squared) { + final double currentDifferenceWithJ2Squared) { // Initial spacecraft state final Orbit initialOrbit = createOrbit(perigeeAltitude, apogeeAltitude); @@ -174,7 +185,7 @@ private void doTestComparisonWithNumerical(final double perigeeAltitude, final d // Verify Assertions.assertTrue(differenceWithJ2Squared < differenceWithoutJ2Squared); Assertions.assertEquals(0.0, differenceWithoutJ2Squared, currentDifferenceWithoutJ2Squared); // Difference between DSST and numerical without J2-Squared - Assertions.assertEquals(0.0, differenceWithJ2Squared, currenDifferenceWithJ2Squared); // Difference between DSST and numerical with J2-Squared (not 0.0 due to J2-squared short periods which are not implemented) + Assertions.assertEquals(0.0, differenceWithJ2Squared, currentDifferenceWithJ2Squared); // Difference between DSST and numerical with J2-Squared (not 0.0 due to J2-squared short periods which are not implemented) } @@ -403,5 +414,4 @@ private void addToRow(final double[] derivatives, final int index, jacobian[index][i] += derivatives[i]; } } - } diff --git a/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTPropagatorTest.java b/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTPropagatorTest.java index 8e4cc05df9..dad36dac39 100644 --- a/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTPropagatorTest.java +++ b/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTPropagatorTest.java @@ -16,6 +16,10 @@ */ package org.orekit.propagation.semianalytical.dsst; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + import java.io.IOException; import java.text.ParseException; import java.util.ArrayList; @@ -50,9 +54,9 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.orekit.OrekitMatchers; -import org.orekit.TestUtils; import org.orekit.Utils; import org.orekit.attitudes.AttitudeProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.attitudes.LofOffset; import org.orekit.bodies.CelestialBody; import org.orekit.bodies.CelestialBodyFactory; @@ -117,10 +121,6 @@ import org.orekit.utils.TimeSpanMap; import org.orekit.utils.TimeStampedPVCoordinates; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; - public class DSSTPropagatorTest { private DSSTPropagator dsstProp; @@ -242,7 +242,7 @@ public void testHighDegreesSetting() { FastMath.toRadians(12.0), PositionAngleType.TRUE, eci, initialDate, Constants.EIGEN5C_EARTH_MU); SpacecraftState oscuState = DSSTPropagator.computeOsculatingState(new SpacecraftState(orbit), null, forces); - Assertions.assertEquals(7119927.097122, oscuState.getA(), 0.001); + Assertions.assertEquals(7119927.147704, oscuState.getA(), 1.e-6); } @Test @@ -1197,6 +1197,61 @@ public void testIssue672() { Assertions.assertNotNull(state); } + /** Test issue 1461. + *

      Test for the dedicated method DSSTPropagator.reset(SpacecraftState, PropagationType) + *

      This should change the status of attribute "initialIsOsculating" depending on input PropagationType + */ + @Test + public void testIssue1461() { + + // GIVEN + // ----- + + final double step = 60.; + final int nStep = 10; + final Frame gcrf = FramesFactory.getGCRF(); + final AttitudeProvider attitude = new FrameAlignedProvider(gcrf); + + // LEO orbit as input + final Orbit initialOrbit = new KeplerianOrbit(7000e3, 1.e-3, FastMath.toRadians(98.), 0., 0., 0., + PositionAngleType.ECCENTRIC, gcrf, + AbsoluteDate.ARBITRARY_EPOCH, Constants.EGM96_EARTH_MU); + final AbsoluteDate t0 = initialOrbit.getDate(); + final SpacecraftState initialState = new SpacecraftState(initialOrbit); + final ClassicalRungeKuttaIntegrator integrator = new ClassicalRungeKuttaIntegrator(step); + + // DSST is configured to propagate in MEAN elements + final DSSTPropagator propagator = new DSSTPropagator(integrator, PropagationType.MEAN); + + // The initial state is OSCULATING + propagator.setInitialState(initialState, PropagationType.OSCULATING); + + // Using a J2-only perturbed force model, the SMA of the mean / averaged orbit should remained constant during propagation + propagator.addForceModel(new DSSTZonal(GravityFieldFactory.getUnnormalizedProvider(2, 0))); + propagator.setAttitudeProvider(attitude); + + // Initial mean SMA + final double initialMeanSma = DSSTPropagator.computeMeanState(initialState, + attitude, + propagator.getAllForceModels()).getA(); + // WHEN + // ---- + + // Propagating step by step and getting mean SMAs + final List propagatedMeanSma = new ArrayList<>(); + for (int i = 1; i <= nStep; i++) { + propagatedMeanSma.add(propagator.propagate(t0.shiftedBy(i * step)).getA()); + } + + // THEN + // ---- + + // Mean SMA should remain constant and equal to initial mean sma + for (Double meanSma : propagatedMeanSma) { + Assertions.assertEquals(initialMeanSma, meanSma, 0.); + } + } + /** * Check that the DSST can include the derivatives of force model parameters in the * Jacobian. Uses a very basic force model for a quick test. Checks both mean and @@ -1488,6 +1543,7 @@ public void updateShortPeriodTerms(double[] parameters, SpacecraftState... meanStates) { } + @SuppressWarnings("unchecked") @Override public > void updateShortPeriodTerms( T[] parameters, FieldSpacecraftState... meanStates) { diff --git a/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTTesseralTest.java b/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTTesseralTest.java new file mode 100644 index 0000000000..87ecbf97f1 --- /dev/null +++ b/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTTesseralTest.java @@ -0,0 +1,417 @@ +/* Copyright 2002-2024 CS GROUP + * 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.propagation.semianalytical.dsst; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.hipparchus.exception.LocalizedCoreFormats; +import org.hipparchus.util.FastMath; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +import org.orekit.bodies.CelestialBodyFactory; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.errors.OrekitException; +import org.orekit.forces.gravity.potential.GravityFieldFactory; +import org.orekit.forces.gravity.potential.ICGEMFormatReader; +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.EquinoctialOrbit; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.PropagationType; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel; +import org.orekit.propagation.semianalytical.dsst.forces.DSSTTesseral; +import org.orekit.propagation.semianalytical.dsst.forces.ShortPeriodTerms; +import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.Constants; + +public class DSSTTesseralTest { + + @Test + public void testGetMeanElementRate() { + + // Central Body geopotential 4x4 + final UnnormalizedSphericalHarmonicsProvider provider = + GravityFieldFactory.getUnnormalizedProvider(4, 4); + + final Frame frame = FramesFactory.getEME2000(); + final Frame earthFrame = CelestialBodyFactory.getEarth().getBodyOrientedFrame(); + final AbsoluteDate initDate = new AbsoluteDate(2007, 4, 16, 0, 46, 42.400, TimeScalesFactory.getUTC()); + + // a = 26559890 m + // ex = 2.719455286199036E-4 + // ey = 0.0041543085910249414 + // hx = -0.3412974060023717 + // hy = 0.3960084733107685 + // lM = 8.566537840341699 rad + final Orbit orbit = new EquinoctialOrbit(2.655989E7, + 2.719455286199036E-4, + 0.0041543085910249414, + -0.3412974060023717, + 0.3960084733107685, + 8.566537840341699, + PositionAngleType.TRUE, + frame, + initDate, + provider.getMu()); + + final SpacecraftState state = new SpacecraftState(orbit, 1000.0); + + final AuxiliaryElements auxiliaryElements = new AuxiliaryElements(state.getOrbit(), 1); + + final DSSTForceModel tesseral = new DSSTTesseral(earthFrame, + Constants.WGS84_EARTH_ANGULAR_VELOCITY, provider, + 4, 4, 4, 8, 4, 4, 2); + // Force model parameters + final double[] parameters = tesseral.getParameters(orbit.getDate()); + + // Initialize force model + tesseral.initializeShortPeriodTerms(auxiliaryElements, PropagationType.MEAN, parameters); + + final double[] elements = new double[7]; + Arrays.fill(elements, 0.0); + + final double[] daidt = tesseral.getMeanElementRate(state, auxiliaryElements, parameters); + for (int i = 0; i < daidt.length; i++) { + elements[i] = daidt[i]; + } + + Assertions.assertEquals(7.125576870652436E-5 , elements[0], 1.e-20); + Assertions.assertEquals(-1.1135134574790914E-11, elements[1], 1.e-26); + Assertions.assertEquals(2.302319084099073E-11 , elements[2], 1.e-26); + Assertions.assertEquals(2.4994484564991748E-12 , elements[3], 1.e-27); + Assertions.assertEquals(1.381385271417345E-13 , elements[4], 1.e-28); + Assertions.assertEquals(5.815883045595586E-12 , elements[5], 1.e-27); + + } + + @Test + public void testShortPeriodTerms() throws IllegalArgumentException { + + Utils.setDataRoot("regular-data:potential/icgem-format"); + GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("^eigen-6s-truncated$", false)); + UnnormalizedSphericalHarmonicsProvider nshp = GravityFieldFactory.getUnnormalizedProvider(8, 8); + Orbit orbit = new KeplerianOrbit(13378000, 0.05, 0, 0, FastMath.PI, 0, PositionAngleType.MEAN, + FramesFactory.getTOD(false), + new AbsoluteDate(2003, 5, 6, TimeScalesFactory.getUTC()), + nshp.getMu()); + + OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getGTOD(false)); + + // Force model + final DSSTForceModel force = new DSSTTesseral(earth.getBodyFrame(), + Constants.WGS84_EARTH_ANGULAR_VELOCITY, + nshp, 8, 8, 4, 12, 8, 8, 4); + + // Initial state + final SpacecraftState meanState = new SpacecraftState(orbit, 45.0); + + //Create the auxiliary object + final AuxiliaryElements aux = new AuxiliaryElements(orbit, 1); + + final List shortPeriodTerms = new ArrayList(); + + force.registerAttitudeProvider(null); + + shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, PropagationType.OSCULATING, force.getParameters(meanState.getDate()))); + force.updateShortPeriodTerms(force.getParametersAllValues(), meanState); + + double[] y = new double[6]; + for (final ShortPeriodTerms spt : shortPeriodTerms) { + final double[] shortPeriodic = spt.value(meanState.getOrbit()); + for (int i = 0; i < shortPeriodic.length; i++) { + y[i] += shortPeriodic[i]; + } + } + + Assertions.assertEquals(5.192409957353241 , y[0], 1.e-15); + Assertions.assertEquals(9.660364749662038E-7 , y[1], 1.e-22); + Assertions.assertEquals(1.5420089871620561E-6, y[2], 1.e-21); + Assertions.assertEquals(-4.99441460131262E-8 , y[3], 1.e-22); + Assertions.assertEquals(-4.500974242661193E-8, y[4], 1.e-22); + Assertions.assertEquals(-2.785213556107623E-7, y[5], 1.e-21); + } + + @Test + public void testIssue625() { + + // Central Body geopotential 4x4 + final UnnormalizedSphericalHarmonicsProvider provider = + GravityFieldFactory.getUnnormalizedProvider(4, 4); + + final Frame frame = FramesFactory.getEME2000(); + final Frame earthFrame = CelestialBodyFactory.getEarth().getBodyOrientedFrame(); + final AbsoluteDate initDate = new AbsoluteDate(2007, 4, 16, 0, 46, 42.400, TimeScalesFactory.getUTC()); + + // a = 26559890 m + // ex = 2.719455286199036E-4 + // ey = 0.0041543085910249414 + // hx = -0.3412974060023717 + // hy = 0.3960084733107685 + // lM = 8.566537840341699 rad + final Orbit orbit = new EquinoctialOrbit(2.655989E7, + 2.719455286199036E-4, + 0.0041543085910249414, + -0.3412974060023717, + 0.3960084733107685, + 8.566537840341699, + PositionAngleType.TRUE, + frame, + initDate, + provider.getMu()); + + final SpacecraftState state = new SpacecraftState(orbit, 1000.0); + + final AuxiliaryElements auxiliaryElements = new AuxiliaryElements(state.getOrbit(), 1); + + // Tesseral force model + final DSSTForceModel tesseral = new DSSTTesseral(earthFrame, + Constants.WGS84_EARTH_ANGULAR_VELOCITY, provider, + 4, 4, 4, 8, 4, 4, 2); + tesseral.initializeShortPeriodTerms(auxiliaryElements, PropagationType.MEAN, tesseral.getParameters(orbit.getDate())); + + // Tesseral force model with default constructor + final DSSTForceModel tesseralDefault = new DSSTTesseral(earthFrame, + Constants.WGS84_EARTH_ANGULAR_VELOCITY, + provider); + tesseralDefault.initializeShortPeriodTerms(auxiliaryElements, PropagationType.MEAN, tesseralDefault.getParameters(orbit.getDate())); + + // Compute mean element rate for the tesseral force model + final double[] elements = tesseral.getMeanElementRate(state, auxiliaryElements, tesseral.getParameters(orbit.getDate())); + + // Compute mean element rate for the "default" tesseral force model + final double[] elementsDefault = tesseralDefault.getMeanElementRate(state, auxiliaryElements, tesseralDefault.getParameters(orbit.getDate())); + + // Verify + for (int i = 0; i < 6; i++) { + Assertions.assertEquals(elements[i], elementsDefault[i], Double.MIN_VALUE); + } + + } + + @Test + public void testIssue736() { + + // Central Body geopotential 4x4 + final UnnormalizedSphericalHarmonicsProvider provider = + GravityFieldFactory.getUnnormalizedProvider(4, 4); + + // Frames and epoch + final Frame frame = FramesFactory.getEME2000(); + final Frame earthFrame = CelestialBodyFactory.getEarth().getBodyOrientedFrame(); + final AbsoluteDate initDate = new AbsoluteDate(2007, 4, 16, 0, 46, 42.400, TimeScalesFactory.getUTC()); + + // Initial orbit + final Orbit orbit = new EquinoctialOrbit(2.655989E7, 2.719455286199036E-4, 0.0041543085910249414, + -0.3412974060023717, 0.3960084733107685, + 8.566537840341699, PositionAngleType.TRUE, + frame, initDate, provider.getMu()); + + // Force model + final DSSTForceModel tesseral = new DSSTTesseral(earthFrame, Constants.WGS84_EARTH_ANGULAR_VELOCITY, provider); + final double[] parameters = tesseral.getParameters(orbit.getDate()); + + // Initialize force model + tesseral.initializeShortPeriodTerms(new AuxiliaryElements(orbit, 1), PropagationType.MEAN, parameters); + + // Eccentricity shift + final Orbit shfitedOrbit = new EquinoctialOrbit(2.655989E7, 0.02, 0.0041543085910249414, + -0.3412974060023717, 0.3960084733107685, + 8.566537840341699, PositionAngleType.TRUE, + frame, initDate, provider.getMu()); + + final double[] elements = tesseral.getMeanElementRate(new SpacecraftState(shfitedOrbit), new AuxiliaryElements(shfitedOrbit, 1), parameters); + + // The purpose of this test is not to verify a specific value. + // Its purpose is to verify that a NullPointerException does not + // occur when calculating initial values of Hansen Coefficients + for (int i = 0; i < elements.length; i++) { + Assertions.assertTrue(elements[i] != 0); + } + + } + + /** Test issue 672: + * DSST Propagator was crashing with tesseral harmonics of the gravity field + * when the order is lower or equal to 3. + */ + @Test + public void testIssue672() { + + // GIVEN + // ----- + + // Test with a central Body geopotential of 2x2 + final UnnormalizedSphericalHarmonicsProvider provider = + GravityFieldFactory.getUnnormalizedProvider(2, 2); + final Frame earthFrame = CelestialBodyFactory.getEarth().getBodyOrientedFrame(); + + // GPS Orbit + final AbsoluteDate initDate = new AbsoluteDate(2007, 4, 16, 0, 46, 42.400, + TimeScalesFactory.getUTC()); + final Orbit orbit = new KeplerianOrbit(26559890., + 0.0041632, + FastMath.toRadians(55.2), + FastMath.toRadians(315.4985), + FastMath.toRadians(130.7562), + FastMath.toRadians(44.2377), + PositionAngleType.MEAN, + FramesFactory.getEME2000(), + initDate, + provider.getMu()); + + // Set propagator with state and force model + final SpacecraftState initialState = new SpacecraftState(orbit); + + // Tesseral force model + final DSSTTesseral dsstTesseral = + new DSSTTesseral(earthFrame, + Constants.WGS84_EARTH_ANGULAR_VELOCITY, provider); + + // Initialize short period terms + final List shortPeriodTerms = new ArrayList(); + final AuxiliaryElements aux = new AuxiliaryElements(orbit, 1); + shortPeriodTerms.addAll(dsstTesseral.initializeShortPeriodTerms(aux, + PropagationType.OSCULATING, + dsstTesseral.getParameters(orbit.getDate()))); + // WHEN + // ---- + + // Updating short period terms + dsstTesseral.updateShortPeriodTerms(dsstTesseral.getParametersAllValues(), initialState); + + // THEN + // ---- + + // Verify that no exception was raised + Assertions.assertNotNull(shortPeriodTerms); + + } + + /** Test issue 672 with OUT_OF_RANGE_SIMPLE exception: + * 1. DSSTTesseral should throw an exception if input "maxEccPowTesseralSP" is greater than the order of the gravity field. + * 2. DSSTTesseral should not throw an exception if order = 0, even if input "maxEccPowTesseralSP" is greater than the + * order of the gravity field (0 in this case). This last behavior was added for non-regression purposes. + */ + @Test + public void testIssue672OutOfRangeException() { + + // Throwing exception + // ------------------ + + // GIVEN + // Test with a central Body geopotential of 3x3 + int degree = 3; + int order = 2; + UnnormalizedSphericalHarmonicsProvider provider = + GravityFieldFactory.getUnnormalizedProvider(degree, order); + final Frame earthFrame = CelestialBodyFactory.getEarth().getBodyOrientedFrame(); + + try { + + // WHEN + // Tesseral force model: force "maxEccPowTesseralSP" to 3 which is greater than the order (2) + new DSSTTesseral(earthFrame, + Constants.WGS84_EARTH_ANGULAR_VELOCITY, provider, + degree, order, 3, FastMath.min(12, degree + 4), + degree, order, FastMath.min(4, degree - 2)); + Assertions.fail("An exception should have been thrown"); + } catch (OrekitException oe) { + + // THEN + Assertions.assertEquals(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE, oe.getSpecifier()); + } + + + // NOT throwing exception + // ---------------------- + + // GIVEN + // Test with a central Body geopotential of 2x0 + degree = 2; + order = 0; + provider = GravityFieldFactory.getUnnormalizedProvider(degree, order); + + // WHEN + // Tesseral force model: force "maxEccPowTesseralSP" to 4 which is greater than the order (0) + final DSSTTesseral dsstTesseral = new DSSTTesseral(earthFrame, + Constants.WGS84_EARTH_ANGULAR_VELOCITY, provider, + degree, order, 4, FastMath.min(12, degree + 4), + degree, order, FastMath.min(4, degree - 2)); + // THEN: Verify that no exception was raised + Assertions.assertNotNull(dsstTesseral); + + } + + @Test + public void testOutOfRangeException() { + // Central Body geopotential 1x0 + final UnnormalizedSphericalHarmonicsProvider provider = + GravityFieldFactory.getUnnormalizedProvider(1, 0); + // Earth + final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getGTOD(false)); + try { + @SuppressWarnings("unused") + final DSSTForceModel tesseral = new DSSTTesseral(earth.getBodyFrame(), + Constants.WGS84_EARTH_ANGULAR_VELOCITY, + provider); + Assertions.fail("An exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE, oe.getSpecifier()); + } + } + + @Test + public void testGetMaxEccPow() + throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + final UnnormalizedSphericalHarmonicsProvider provider = + GravityFieldFactory.getUnnormalizedProvider(4, 4);; + final Frame earthFrame = CelestialBodyFactory.getEarth().getBodyOrientedFrame(); + final DSSTTesseral force = new DSSTTesseral(earthFrame, Constants.WGS84_EARTH_ANGULAR_VELOCITY, provider); + Method getMaxEccPow = DSSTTesseral.class.getDeclaredMethod("getMaxEccPow", Double.TYPE); + getMaxEccPow.setAccessible(true); + Assertions.assertEquals(3, getMaxEccPow.invoke(force, 0.0)); + Assertions.assertEquals(4, getMaxEccPow.invoke(force, 0.01)); + Assertions.assertEquals(7, getMaxEccPow.invoke(force, 0.08)); + Assertions.assertEquals(10, getMaxEccPow.invoke(force, 0.15)); + Assertions.assertEquals(12, getMaxEccPow.invoke(force, 0.25)); + Assertions.assertEquals(15, getMaxEccPow.invoke(force, 0.35)); + Assertions.assertEquals(20, getMaxEccPow.invoke(force, 1.0)); + } + + @BeforeEach + public void setUp() throws IOException, ParseException { + Utils.setDataRoot("regular-data:potential/shm-format"); + } +} diff --git a/src/test/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTPropagatorTest.java b/src/test/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTPropagatorTest.java index d0334582a5..68599ccfda 100644 --- a/src/test/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTPropagatorTest.java +++ b/src/test/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTPropagatorTest.java @@ -49,6 +49,7 @@ import org.orekit.OrekitMatchers; import org.orekit.Utils; import org.orekit.attitudes.AttitudeProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.attitudes.LofOffset; import org.orekit.bodies.CelestialBody; import org.orekit.bodies.CelestialBodyFactory; @@ -73,6 +74,7 @@ import org.orekit.orbits.FieldEquinoctialOrbit; import org.orekit.orbits.FieldKeplerianOrbit; import org.orekit.orbits.FieldOrbit; +import org.orekit.orbits.KeplerianOrbit; import org.orekit.orbits.OrbitType; import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.FieldBoundedPropagator; @@ -252,7 +254,7 @@ private > void doTestHighDegreesSetting(Field< PositionAngleType.TRUE, eci, initialDate, zero.add(Constants.EIGEN5C_EARTH_MU)); final FieldSpacecraftState state = new FieldSpacecraftState<>(orbit); FieldSpacecraftState oscuState = FieldDSSTPropagator.computeOsculatingState(state, null, forces); - Assertions.assertEquals(7119927.097122, oscuState.getA().getReal(), 0.001); + Assertions.assertEquals(7119927.148, oscuState.getA().getReal(), 0.001); } @Test @@ -1279,6 +1281,69 @@ private > void doTestIssue672(final Field f Assertions.assertNotNull(state); } + /** Test issue 1461. + *

      Test for the dedicated method DSSTPropagator.reset(SpacecraftState, PropagationType) + *

      This should change the status of attribute "initialIsOsculating" depending on input PropagationType + */ + @Test + public void testIssue1461() { + doTestIssue1461(Binary64Field.getInstance()); + } + + /** Method for running test for issue 1461. */ + private > void doTestIssue1461(final Field field) { + + // GIVEN + // ----- + + final T zero = field.getZero(); + + final double step = 60.; + final int nStep = 10; + final Frame gcrf = FramesFactory.getGCRF(); + final AttitudeProvider attitude = new FrameAlignedProvider(gcrf); + + // LEO orbit as input + final FieldOrbit initialOrbit = new FieldKeplerianOrbit<>(field, + new KeplerianOrbit(7000e3, 1.e-3, FastMath.toRadians(98.), 0., 0., 0., + PositionAngleType.ECCENTRIC, gcrf, + AbsoluteDate.ARBITRARY_EPOCH, Constants.EGM96_EARTH_MU)); + final FieldAbsoluteDate t0 = initialOrbit.getDate(); + final FieldSpacecraftState initialState = new FieldSpacecraftState<>(initialOrbit); + final ClassicalRungeKuttaFieldIntegrator integrator = new ClassicalRungeKuttaFieldIntegrator<>(field, zero.newInstance(step)); + + // DSST is configured to propagate in MEAN elements + final FieldDSSTPropagator propagator = new FieldDSSTPropagator<>(field, integrator, PropagationType.MEAN); + + // The initial state is OSCULATING + propagator.setInitialState(initialState, PropagationType.OSCULATING); + + // Using a J2-only perturbed force model, the SMA of the mean / averaged orbit should remained constant during propagation + propagator.addForceModel(new DSSTZonal(GravityFieldFactory.getUnnormalizedProvider(2, 0))); + propagator.setAttitudeProvider(attitude); + + // Initial mean SMA + final double initialMeanSma = FieldDSSTPropagator.computeMeanState(initialState, + attitude, + propagator.getAllForceModels()).getA().getReal(); + // WHEN + // ---- + + // Propagating step by step and getting mean SMAs + final List propagatedMeanSma = new ArrayList<>(); + for (int i = 1; i <= nStep; i++) { + propagatedMeanSma.add(propagator.propagate(t0.shiftedBy(i * step)).getA().getReal()); + } + + // THEN + // ---- + + // Mean SMA should remain constant and equal to initial mean sma + for (Double meanSma : propagatedMeanSma) { + Assertions.assertEquals(initialMeanSma, meanSma, 0.); + } + } + private > FieldSpacecraftState getGEOState(final Field field) { final T zero = field.getZero(); // No shadow at this date diff --git a/src/test/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTTesseralTest.java b/src/test/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTTesseralTest.java index a2db184fe6..a5415baead 100644 --- a/src/test/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTTesseralTest.java +++ b/src/test/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTTesseralTest.java @@ -16,6 +16,12 @@ */ package org.orekit.propagation.semianalytical.dsst; +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.Gradient; @@ -61,12 +67,6 @@ import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterDriversList; -import java.io.IOException; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - public class FieldDSSTTesseralTest { @Test @@ -125,12 +125,12 @@ private > void doTestGetMeanElementRate(final elements[i] = daidt[i]; } - Assertions.assertEquals(7.120011500375922E-5, elements[0].getReal(), 6.0e-19); - Assertions.assertEquals(-1.109767646425212E-11, elements[1].getReal(), 2.0e-26); - Assertions.assertEquals(2.3036711391089307E-11, elements[2].getReal(), 1.5e-26); - Assertions.assertEquals(2.499304852807308E-12, elements[3].getReal(), 1.0e-27); - Assertions.assertEquals(1.3899097178558372E-13, elements[4].getReal(), 3.0e-27); - Assertions.assertEquals(5.795522421338584E-12, elements[5].getReal(), 1.0e-26); + Assertions.assertEquals(7.12557687065243E-05 , elements[0].getReal(), 6.0e-19); + Assertions.assertEquals(-1.11351345747909E-11, elements[1].getReal(), 2.0e-26); + Assertions.assertEquals(2.302319084099072E-11, elements[2].getReal(), 1.5e-26); + Assertions.assertEquals(2.499448456499174E-12, elements[3].getReal(), 1.0e-27); + Assertions.assertEquals(1.38138527141734E-13 , elements[4].getReal(), 3.0e-27); + Assertions.assertEquals(5.81588304559558E-12 , elements[5].getReal(), 1.0e-26); } @@ -187,12 +187,12 @@ private > void doTestShortPeriodTerms(final Fi } } - Assertions.assertEquals(5.192409957353236, y[0].getReal(), 1.e-15); - Assertions.assertEquals(9.660364749662076E-7, y[1].getReal(), 1.e-22); - Assertions.assertEquals(1.542008987162059E-6, y[2].getReal(), 1.e-21); - Assertions.assertEquals(-4.9944146013126755E-8, y[3].getReal(), 1.e-22); - Assertions.assertEquals(-4.500974242661177E-8, y[4].getReal(), 1.e-22); - Assertions.assertEquals(-2.785213556107612E-7, y[5].getReal(), 1.e-21); + Assertions.assertEquals(5.192409957353241 , y[0].getReal(), 1.e-15); + Assertions.assertEquals(9.660364749662038E-7 , y[1].getReal(), 1.e-22); + Assertions.assertEquals(1.5420089871620561E-6, y[2].getReal(), 1.e-21); + Assertions.assertEquals(-4.99441460131262E-8 , y[3].getReal(), 1.e-22); + Assertions.assertEquals(-4.500974242661193E-8, y[4].getReal(), 1.e-22); + Assertions.assertEquals(-2.785213556107623E-7, y[5].getReal(), 1.e-21); } @Test @@ -415,7 +415,7 @@ public void testShortPeriodTermsStateDerivatives() { for (int m = 0; m < 6; ++m) { for (int n = 0; n < 6; ++n) { double error = FastMath.abs((shortPeriodJacobian[m][n] - shortPeriodJacobianRef[m][n]) / shortPeriodJacobianRef[m][n]); - Assertions.assertEquals(0, error, 7.6e-10); + Assertions.assertEquals(0, error, 1.52e-09); } } diff --git a/src/test/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTZonalTest.java b/src/test/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTZonalTest.java index 2ce091833d..6e779e9a22 100644 --- a/src/test/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTZonalTest.java +++ b/src/test/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTZonalTest.java @@ -16,9 +16,18 @@ */ package org.orekit.propagation.semianalytical.dsst; +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaFieldIntegrator; +import org.hipparchus.stat.descriptive.StreamingStatistics; +import org.hipparchus.util.Binary64; import org.hipparchus.util.Binary64Field; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathArrays; @@ -27,20 +36,28 @@ import org.junit.jupiter.api.Test; import org.orekit.Utils; import org.orekit.attitudes.Attitude; +import org.orekit.attitudes.AttitudeProvider; +import org.orekit.attitudes.FrameAlignedProvider; +import org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel; import org.orekit.forces.gravity.potential.GRGSFormatReader; import org.orekit.forces.gravity.potential.GravityFieldFactory; +import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.orbits.EquinoctialOrbit; +import org.orekit.orbits.FieldCircularOrbit; import org.orekit.orbits.FieldEquinoctialOrbit; import org.orekit.orbits.FieldOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.FieldBoundedPropagator; +import org.orekit.propagation.FieldEphemerisGenerator; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.PropagationType; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.numerical.FieldNumericalPropagator; import org.orekit.propagation.numerical.NumericalPropagator; import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel; import org.orekit.propagation.semianalytical.dsst.forces.DSSTNewtonianAttraction; @@ -54,15 +71,10 @@ import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeComponents; import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.IERSConventions; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterDriversList; -import java.io.IOException; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - public class FieldDSSTZonalTest { @Test @@ -493,6 +505,194 @@ private > FieldSpacecraftState getGEOState( zero.add(3.986004415E14)); return new FieldSpacecraftState<>(orbit); } + + /** Test for issue 1104. + *

      Only J2 is used + *

      Comparisons to a numerical propagator are done, with different frames as "body-fixed frames": GCRF, ITRF, TOD + */ + @Test + void testIssue1104() { + + final boolean printResults = false; + + final Field field = Binary64Field.getInstance(); + + // Frames + final Frame gcrf = FramesFactory.getGCRF(); + final Frame tod = FramesFactory.getTOD(IERSConventions.IERS_2010, true); + final Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); + + // GCRF/GCRF test + // --------- + + // Using GCRF as both inertial and body frame (behaviour before the fix) + double diMax = 9.615e-5; + double dOmMax = 3.374e-3; + double dLmMax = 1.128e-2; + doTestIssue1104(gcrf, gcrf, field, printResults, diMax, dOmMax, dLmMax); + + // TOD/TOD test + // -------- + + // Before fix, using TOD was the best choice to reduce the errors DSST vs Numerical + // INC is one order of magnitude better compared to GCRF/GCRF (and not diverging anymore but it's not testable here) + // RAAN and LM are only slightly better + diMax = 1.059e-5; + dOmMax = 2.789e-3; + dLmMax = 1.040e-2; + doTestIssue1104(tod, tod, field, printResults, diMax, dOmMax, dLmMax); + + // GCRF/ITRF test + // --------- + + // Using ITRF as body-fixed frame and GCRF as inertial frame + // Results are on par with TOD/TOD + diMax = 1.067e-5; + dOmMax = 2.789e-3; + dLmMax = 1.040e-2; + doTestIssue1104(gcrf, itrf, field, printResults, diMax, dOmMax, dLmMax); + + // GCRF/TOD test + // --------- + + // Using TOD as body-fixed frame and GCRF as inertial frame + // Results are identical to TOD/TOD + diMax = 1.059e-5; + dOmMax = 2.789e-3; + dLmMax = 1.040e-2; + doTestIssue1104(tod, itrf, field, printResults, diMax, dOmMax, dLmMax); + + // Since ITRF is longer to compute, if another inertial frame than TOD is used, + // the best balance performance vs accuracy is to use TOD as body-fixed frame + } + + /** Implements the comparison between DSST osculating and numerical. */ + private > void doTestIssue1104(final Frame inertialFrame, + final Frame bodyFixedFrame, + final Field field, + final boolean printResults, + final double diMax, + final double dOmMax, + final double dLmMax) { + + // GIVEN + // ----- + + // Parameters + final T zero = field.getZero(); + final double step = 60.; + final double nOrb = 50.; + + final FieldAbsoluteDate t0 = new FieldAbsoluteDate<>(field); + + // Frames + final Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); + + // Potential coefficients providers + final int degree = 2; + final int order = 0; + final UnnormalizedSphericalHarmonicsProvider unnormalized = + GravityFieldFactory.getConstantUnnormalizedProvider(degree, order, t0.toAbsoluteDate()); + final NormalizedSphericalHarmonicsProvider normalized = + GravityFieldFactory.getConstantNormalizedProvider(degree, order, t0.toAbsoluteDate()); + + // Initial LEO osculating orbit + final double mass = 150.; + final double a = 6906780.35; + final double ex = 5.09E-4; + final double ey = 1.24e-3; + final double i = FastMath.toRadians(97.49); + final double raan = FastMath.toRadians(-94.607); + final double alphaM = FastMath.toRadians(0.); + final FieldCircularOrbit oscCircOrbit0 = new FieldCircularOrbit<>( + zero.newInstance(a), + zero.newInstance(ex), + zero.newInstance(ey), + zero.newInstance(i), + zero.newInstance(raan), + zero.newInstance(alphaM), + PositionAngleType.MEAN, + inertialFrame, + t0, + zero.newInstance(unnormalized.getMu())); + + final FieldOrbit oscOrbit0 = new FieldEquinoctialOrbit<>(oscCircOrbit0); + final FieldSpacecraftState oscState0 = new FieldSpacecraftState<>(oscOrbit0, zero.newInstance(mass)); + final AttitudeProvider attProvider = new FrameAlignedProvider(inertialFrame); + + // Propagation duration + final double duration = nOrb * oscOrbit0.getKeplerianPeriod().getReal(); + final FieldAbsoluteDate tf = t0.shiftedBy(duration); + + // Numerical prop + final ClassicalRungeKuttaFieldIntegrator integrator = + new ClassicalRungeKuttaFieldIntegrator<>(field, zero.newInstance(step)); + + final FieldNumericalPropagator numProp = new FieldNumericalPropagator<>(field, integrator); + numProp.setOrbitType(oscOrbit0.getType()); + numProp.setInitialState(oscState0); + numProp.setAttitudeProvider(attProvider); + numProp.addForceModel(new HolmesFeatherstoneAttractionModel(itrf, normalized)); // J2-only gravity field + final FieldEphemerisGenerator numEphemGen = numProp.getEphemerisGenerator(); + + // DSST prop: max step could be much higher but made explicitly equal to numerical to rule out a step difference + final ClassicalRungeKuttaFieldIntegrator integratorDsst = + new ClassicalRungeKuttaFieldIntegrator<>(field, zero.newInstance(step)); + final FieldDSSTPropagator dsstProp = new FieldDSSTPropagator(field, integratorDsst, PropagationType.OSCULATING); + dsstProp.setInitialState(oscState0, PropagationType.OSCULATING); // Initial state is OSCULATING + dsstProp.setAttitudeProvider(attProvider); + final DSSTForceModel zonal = new DSSTZonal(bodyFixedFrame, unnormalized); // J2-only with custom Earth-fixed frame + dsstProp.addForceModel(zonal); + final FieldEphemerisGenerator dsstEphemGen = dsstProp.getEphemerisGenerator(); + + // WHEN + // ---- + + // Statistics containers: compare on INC, RAAN and anomaly since that's where there is + // improvement brought by fixing 1104. The in-plane parameters (a, ex, ey) are almost equal + final StreamingStatistics dI = new StreamingStatistics(); + final StreamingStatistics dOm = new StreamingStatistics(); + final StreamingStatistics dLM = new StreamingStatistics(); + + // Propagate and get ephemeris + numProp.propagate(t0, tf); + dsstProp.propagate(t0, tf); + + final FieldBoundedPropagator numEphem = numEphemGen.getGeneratedEphemeris(); + final FieldBoundedPropagator dsstEphem = dsstEphemGen.getGeneratedEphemeris(); + + // Compare and fill statistics + for (double dt = 0; dt < duration; dt += step) { + + // Date + final FieldAbsoluteDate t = t0.shiftedBy(dt); + + // Orbits and comparison + final FieldCircularOrbit num = new FieldCircularOrbit<>(numEphem.propagate(t).getOrbit()); + final FieldCircularOrbit dsst = new FieldCircularOrbit<>(dsstEphem.propagate(t).getOrbit()); + dI.addValue(FastMath.toDegrees(dsst.getI().getReal() - num.getI().getReal())); + dOm.addValue(FastMath.toDegrees(dsst.getRightAscensionOfAscendingNode().getReal() - + num.getRightAscensionOfAscendingNode().getReal())); + dLM.addValue(FastMath.toDegrees(dsst.getLM().getReal() - num.getLM().getReal())); + } + + // THEN + // ---- + + // Optional: print the statistics + if (printResults) { + System.out.println("Inertial frame : " + inertialFrame.toString()); + System.out.println("Body-Fixed frame: " + bodyFixedFrame.toString()); + System.out.println("\ndi\n" + dI.toString()); + System.out.println("\ndΩ\n" + dOm.toString()); + System.out.println("\ndLM\n" + dLM.toString()); + } + + // Compare to reference + Assertions.assertEquals(diMax, FastMath.max(FastMath.abs(dI.getMax()), FastMath.abs(dI.getMin())), 1.e-8); + Assertions.assertEquals(dOmMax, FastMath.max(FastMath.abs(dOm.getMax()), FastMath.abs(dOm.getMin())), 1.e-6); + Assertions.assertEquals(dLmMax, FastMath.max(FastMath.abs(dLM.getMax()), FastMath.abs(dLM.getMin())), 1.e-5); + } private double[] computeShortPeriodTerms(SpacecraftState state, DSSTForceModel force) { diff --git a/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseralTest.java b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseralTest.java index d15707dbcf..740c8b1b32 100644 --- a/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseralTest.java +++ b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseralTest.java @@ -99,14 +99,12 @@ void testGetMeanElementRate() { for (int i = 0; i < daidt.length; i++) { elements[i] = daidt[i]; } - - Assertions.assertEquals(7.120011500375922E-5, elements[0], 1.e-20); - Assertions.assertEquals(-1.109767646425212E-11, elements[1], 1.e-26); - Assertions.assertEquals(2.3036711391089307E-11, elements[2], 1.e-26); - Assertions.assertEquals(2.499304852807308E-12, elements[3], 1.e-27); - Assertions.assertEquals(1.3899097178558372E-13, elements[4], 1.e-28); - Assertions.assertEquals(5.795522421338584E-12, elements[5], 1.e-27); - + Assertions.assertEquals(7.125576870652436E-5 , elements[0], 1.e-20); + Assertions.assertEquals(-1.1135134574790914E-11, elements[1], 1.e-26); + Assertions.assertEquals(2.302319084099073E-11 , elements[2], 1.e-26); + Assertions.assertEquals(2.4994484564991748E-12 , elements[3], 1.e-27); + Assertions.assertEquals(1.381385271417345E-13 , elements[4], 1.e-28); + Assertions.assertEquals(5.815883045595586E-12 , elements[5], 1.e-27); } @Test @@ -149,13 +147,12 @@ void testShortPeriodTerms() throws IllegalArgumentException { y[i] += shortPeriodic[i]; } } - - Assertions.assertEquals(5.192409957353236, y[0], 1.e-15); - Assertions.assertEquals(9.660364749662076E-7, y[1], 1.e-22); - Assertions.assertEquals(1.542008987162059E-6, y[2], 1.e-21); - Assertions.assertEquals(-4.9944146013126755E-8, y[3], 1.e-22); - Assertions.assertEquals(-4.500974242661177E-8, y[4], 1.e-22); - Assertions.assertEquals(-2.785213556107612E-7, y[5], 1.e-21); + Assertions.assertEquals(5.192409957353241 , y[0], 1.e-15); + Assertions.assertEquals(9.660364749662038E-7 , y[1], 1.e-22); + Assertions.assertEquals(1.5420089871620561E-6, y[2], 1.e-21); + Assertions.assertEquals(-4.99441460131262E-8 , y[3], 1.e-22); + Assertions.assertEquals(-4.500974242661193E-8, y[4], 1.e-22); + Assertions.assertEquals(-2.785213556107623E-7, y[5], 1.e-21); } @Test diff --git a/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonalTesseralDeprecatedFunctionsTest.java b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonalTesseralDeprecatedFunctionsTest.java new file mode 100644 index 0000000000..fab083467f --- /dev/null +++ b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonalTesseralDeprecatedFunctionsTest.java @@ -0,0 +1,137 @@ +/* Copyright 2002-2024 CS GROUP + * 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.propagation.semianalytical.dsst.forces; + +import org.hipparchus.Field; +import org.hipparchus.util.Binary64; +import org.hipparchus.util.Binary64Field; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.estimation.Context; +import org.orekit.estimation.EstimationTestUtils; +import org.orekit.forces.gravity.potential.GravityFieldFactory; +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.EquinoctialOrbit; +import org.orekit.orbits.FieldEquinoctialOrbit; +import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements; +import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; +import org.orekit.utils.Constants; +import org.orekit.utils.IERSConventions; + +/** Test for deprecated functions of (Field)AuxiliaryElements. + * + * @deprecated should be removed in 13.0 + * @since 12.2 + */ +public class DSSTZonalTesseralDeprecatedFunctionsTest { + + @Test + @Deprecated + void testAuxiliaryElementsDeprecatedFunctions() { + + // GIVEN + Context context = EstimationTestUtils.eccentricContext("regular-data:potential"); + + final EquinoctialOrbit orbit = new EquinoctialOrbit(context.initialOrbit); + + final Field field = Binary64Field.getInstance(); + final FieldEquinoctialOrbit fieldOrbit = new FieldEquinoctialOrbit<>(field, orbit); + + // WHEN + final AuxiliaryElements aux = new AuxiliaryElements(context.initialOrbit, 1); + final FieldAuxiliaryElements fieldAux = new FieldAuxiliaryElements<>(fieldOrbit, 1); + + // THEN + Assertions.assertEquals(aux.getVectorF().getZ(), aux.getAlpha(), 0.); + Assertions.assertEquals(aux.getVectorG().getZ(), aux.getBeta(), 0.); + Assertions.assertEquals(aux.getVectorW().getZ(), aux.getGamma(), 0.); + + Assertions.assertEquals(fieldAux.getVectorF().getZ().getReal(), fieldAux.getAlpha().getReal(), 0.); + Assertions.assertEquals(fieldAux.getVectorG().getZ().getReal(), fieldAux.getBeta().getReal(), 0.); + Assertions.assertEquals(fieldAux.getVectorW().getZ().getReal(), fieldAux.getGamma().getReal(), 0.); + } + + @Test + @Deprecated + void testDSSTZonalContextDeprecatedFunctions() { + + // GIVEN + Context context = EstimationTestUtils.eccentricContext("regular-data:potential"); + + final EquinoctialOrbit orbit = new EquinoctialOrbit(context.initialOrbit); + + final Field field = Binary64Field.getInstance(); + final FieldEquinoctialOrbit fieldOrbit = new FieldEquinoctialOrbit<>(field, orbit); + + final AuxiliaryElements aux = new AuxiliaryElements(context.initialOrbit, 1); + final FieldAuxiliaryElements fieldAux = new FieldAuxiliaryElements<>(fieldOrbit, 1); + + final UnnormalizedSphericalHarmonicsProvider gravity = GravityFieldFactory.getUnnormalizedProvider(2, 0); + + // WHEN + final DSSTZonalContext zonalContext = new DSSTZonalContext(aux, gravity, new double[] {gravity.getMu()}); + final FieldDSSTZonalContext fieldZonalContext = new FieldDSSTZonalContext<>(fieldAux, gravity, + new Binary64[] {field.getZero().newInstance(gravity.getMu())}); + + // THEN + Assertions.assertEquals(zonalContext.getX(), zonalContext.getChi(), 0.); + Assertions.assertEquals(zonalContext.getXX(), zonalContext.getChi2(), 0.); + Assertions.assertEquals(zonalContext.getXXX(), zonalContext.getChi3(), 0.); + Assertions.assertEquals(zonalContext.getM2aoA(), -zonalContext.getAx2oA(), 0.); + Assertions.assertEquals(zonalContext.getMCo2AB(), -zonalContext.getCo2AB(), 0.); + + Assertions.assertEquals(fieldZonalContext.getX().getReal(), fieldZonalContext.getChi().getReal(), 0.); + Assertions.assertEquals(fieldZonalContext.getXX().getReal(), fieldZonalContext.getChi2().getReal(), 0.); + Assertions.assertEquals(fieldZonalContext.getXXX().getReal(), fieldZonalContext.getChi3().getReal(), 0.); + Assertions.assertEquals(fieldZonalContext.getM2aoA().getReal(), -fieldZonalContext.getAx2oA().getReal(), 0.); + Assertions.assertEquals(fieldZonalContext.getMCo2AB().getReal(), -fieldZonalContext.getCo2AB().getReal(), 0.); + } + + @Test + @Deprecated + void testDSSTTesseralContextDeprecatedFunctions() { + + // GIVEN + Context context = EstimationTestUtils.eccentricContext("regular-data:potential"); + + final EquinoctialOrbit orbit = new EquinoctialOrbit(context.initialOrbit); + + final Field field = Binary64Field.getInstance(); + final FieldEquinoctialOrbit fieldOrbit = new FieldEquinoctialOrbit<>(field, orbit); + + final AuxiliaryElements aux = new AuxiliaryElements(context.initialOrbit, 1); + final FieldAuxiliaryElements fieldAux = new FieldAuxiliaryElements<>(fieldOrbit, 1); + + final UnnormalizedSphericalHarmonicsProvider gravity = GravityFieldFactory.getUnnormalizedProvider(2, 0); + final Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); + + // WHEN + final DSSTTesseralContext tesseralContext = new DSSTTesseralContext(aux, itrf, gravity, gravity.getMaxDegree(), + Constants.WGS84_EARTH_ANGULAR_VELOCITY, + new double[] {gravity.getMu()}); + final FieldDSSTTesseralContext fieldTesseralContext = + new FieldDSSTTesseralContext<>(fieldAux, itrf, gravity, gravity.getMaxDegree(), + Constants.WGS84_EARTH_ANGULAR_VELOCITY, + new Binary64[] {field.getZero().newInstance(gravity.getMu())}); + + // THEN + Assertions.assertEquals(tesseralContext.getMoa(), tesseralContext.getMuoa(), 0.); + Assertions.assertEquals(fieldTesseralContext.getMoa().getReal(), fieldTesseralContext.getMuoa().getReal(), 0.); + } +} diff --git a/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonalTest.java b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonalTest.java index e05516412c..d9a9f5bb84 100644 --- a/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonalTest.java +++ b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonalTest.java @@ -16,39 +16,61 @@ */ package org.orekit.propagation.semianalytical.dsst.forces; +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + import org.hipparchus.exception.LocalizedCoreFormats; +import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaIntegrator; +import org.hipparchus.stat.descriptive.StreamingStatistics; import org.hipparchus.util.FastMath; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.orekit.Utils; +import org.orekit.attitudes.AttitudeProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.errors.OrekitException; +import org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel; import org.orekit.forces.gravity.potential.GRGSFormatReader; import org.orekit.forces.gravity.potential.GravityFieldFactory; +import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; +import org.orekit.orbits.CircularOrbit; import org.orekit.orbits.EquinoctialOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.BoundedPropagator; +import org.orekit.propagation.EphemerisGenerator; import org.orekit.propagation.PropagationType; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.numerical.NumericalPropagator; +import org.orekit.propagation.semianalytical.dsst.DSSTPropagator; import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements; import org.orekit.time.AbsoluteDate; import org.orekit.time.DateComponents; import org.orekit.time.TimeComponents; import org.orekit.time.TimeScalesFactory; - -import java.io.IOException; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import org.orekit.utils.IERSConventions; class DSSTZonalTest { + /** Test mean elements rates computation. + * + * First without setting the body-fixed frame, then by setting it to the inertial propagation frame. + * Both should give the same results. + */ @Test void testGetMeanElementRate() { + doTestGetMeanElementRate(false); + doTestGetMeanElementRate(true); + } + + private void doTestGetMeanElementRate(final boolean testIssue1104) { // Central Body geopotential 4x4 final UnnormalizedSphericalHarmonicsProvider provider = @@ -76,7 +98,14 @@ void testGetMeanElementRate() { final SpacecraftState state = new SpacecraftState(orbit, 1000.0); - final DSSTForceModel zonal = new DSSTZonal(provider, 4, 3, 9); + final DSSTForceModel zonal; + if (testIssue1104) { + // Non regression for issue 1104, pass-on the inertial propagation frame as body-fixed frame + zonal = new DSSTZonal(state.getFrame(), provider, 4, 3, 9); + } else { + // Classical way of doing the same thing + zonal = new DSSTZonal(provider, 4, 3, 9); + } // Force model parameters final double[] parameters = zonal.getParameters(orbit.getDate()); @@ -136,6 +165,22 @@ void testShortPeriodTerms() { Assertions.assertEquals(-1.1607392915997098E-6, y[5], 1.e-21); } + private SpacecraftState getGEOState() { + // No shadow at this date + final AbsoluteDate initDate = new AbsoluteDate(new DateComponents(2003, 05, 21), new TimeComponents(1, 0, 0.), + TimeScalesFactory.getUTC()); + final Orbit orbit = new EquinoctialOrbit(42164000, + 10e-3, + 10e-3, + FastMath.tan(0.001745329) * FastMath.cos(2 * FastMath.PI / 3), + FastMath.tan(0.001745329) * FastMath.sin(2 * FastMath.PI / 3), 0.1, + PositionAngleType.TRUE, + FramesFactory.getEME2000(), + initDate, + 3.986004415E14); + return new SpacecraftState(orbit); + } + @Test void testIssue625() { @@ -201,21 +246,177 @@ void testOutOfRangeException() { Assertions.assertEquals(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE, oe.getSpecifier()); } } + + /** Test for issue 1104. + *

      Only J2 is used + *

      Comparisons to a numerical propagator are done, with different frames as "body-fixed frames": GCRF, ITRF, TOD + */ + @Test + void testIssue1104() { + + final boolean printResults = false; + + // Frames + final Frame gcrf = FramesFactory.getGCRF(); + final Frame tod = FramesFactory.getTOD(IERSConventions.IERS_2010, true); + final Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); + + // GCRF/GCRF test + // --------- + + // Using GCRF as both inertial and body frame (behaviour before the fix) + double diMax = 9.615e-5; + double dOmMax = 3.374e-3; + double dLmMax = 1.128e-2; + doTestIssue1104(gcrf, gcrf, printResults, diMax, dOmMax, dLmMax); + + // TOD/TOD test + // -------- + + // Before fix, using TOD was the best choice to reduce the errors DSST vs Numerical + // INC is one order of magnitude better compared to GCRF/GCRF (and not diverging anymore but it's not testable here) + // RAAN and LM are only slightly better + diMax = 1.059e-5; + dOmMax = 2.789e-3; + dLmMax = 1.040e-2; + doTestIssue1104(tod, tod, printResults, diMax, dOmMax, dLmMax); + + // GCRF/ITRF test + // --------- + + // Using ITRF as body-fixed frame and GCRF as inertial frame + // Results are on par with TOD/TOD + diMax = 1.067e-5; + dOmMax = 2.789e-3; + dLmMax = 1.040e-2; + doTestIssue1104(gcrf, itrf, printResults, diMax, dOmMax, dLmMax); + + // GCRF/TOD test + // --------- + + // Using TOD as body-fixed frame and GCRF as inertial frame + // Results are identical to TOD/TOD + diMax = 1.059e-5; + dOmMax = 2.789e-3; + dLmMax = 1.040e-2; + doTestIssue1104(tod, itrf, printResults, diMax, dOmMax, dLmMax); + + // Since ITRF is longer to compute, if another inertial frame than TOD is used, + // the best balance performance vs accuracy is to use TOD as body-fixed frame + } - private SpacecraftState getGEOState() { - // No shadow at this date - final AbsoluteDate initDate = new AbsoluteDate(new DateComponents(2003, 05, 21), new TimeComponents(1, 0, 0.), - TimeScalesFactory.getUTC()); - final Orbit orbit = new EquinoctialOrbit(42164000, - 10e-3, - 10e-3, - FastMath.tan(0.001745329) * FastMath.cos(2 * FastMath.PI / 3), - FastMath.tan(0.001745329) * FastMath.sin(2 * FastMath.PI / 3), 0.1, - PositionAngleType.TRUE, - FramesFactory.getEME2000(), - initDate, - 3.986004415E14); - return new SpacecraftState(orbit); + /** Implements the comparison between DSST osculating and numerical. */ + private void doTestIssue1104(final Frame inertialFrame, + final Frame bodyFixedFrame, + final boolean printResults, + final double diMax, + final double dOmMax, + final double dLmMax) { + + // GIVEN + // ----- + + // Parameters + final double step = 60.; + final double nOrb = 50.; + + final AbsoluteDate t0 = new AbsoluteDate(); + + // Frames + final Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); + + // Potential coefficients providers + final int degree = 2; + final int order = 0; + final UnnormalizedSphericalHarmonicsProvider unnormalized = + GravityFieldFactory.getConstantUnnormalizedProvider(degree, order, t0); + final NormalizedSphericalHarmonicsProvider normalized = + GravityFieldFactory.getConstantNormalizedProvider(degree, order, t0); + + // Initial LEO osculating orbit + final double mass = 150.; + final double a = 6906780.35; + final double ex = 5.09E-4; + final double ey = 1.24e-3; + final double i = FastMath.toRadians(97.49); + final double raan = FastMath.toRadians(-94.607); + final double alphaM = FastMath.toRadians(0.); + final CircularOrbit oscCircOrbit0 = new CircularOrbit(a, ex, ey, i, raan, alphaM, PositionAngleType.MEAN, + inertialFrame, t0, unnormalized.getMu()); + + final Orbit oscOrbit0 = new EquinoctialOrbit(oscCircOrbit0); + final SpacecraftState oscState0 = new SpacecraftState(oscOrbit0, mass); + final AttitudeProvider attProvider = new FrameAlignedProvider(inertialFrame); + + // Propagation duration + final double duration = nOrb * oscOrbit0.getKeplerianPeriod(); + final AbsoluteDate tf = t0.shiftedBy(duration); + + // Numerical prop + final ClassicalRungeKuttaIntegrator integrator = new ClassicalRungeKuttaIntegrator(step); + + final NumericalPropagator numProp = new NumericalPropagator(integrator); + numProp.setOrbitType(oscOrbit0.getType()); + numProp.setInitialState(oscState0); + numProp.setAttitudeProvider(attProvider); + numProp.addForceModel(new HolmesFeatherstoneAttractionModel(itrf, normalized)); // J2-only gravity field + final EphemerisGenerator numEphemGen = numProp.getEphemerisGenerator(); + + // DSST prop: max step could be much higher but made explicitly equal to numerical to rule out a step difference + final ClassicalRungeKuttaIntegrator integratorDsst = new ClassicalRungeKuttaIntegrator(step); + final DSSTPropagator dsstProp = new DSSTPropagator(integratorDsst, PropagationType.OSCULATING); + dsstProp.setInitialState(oscState0, PropagationType.OSCULATING); // Initial state is OSCULATING + dsstProp.setAttitudeProvider(attProvider); + final DSSTForceModel zonal = new DSSTZonal(bodyFixedFrame, unnormalized); // J2-only with custom Earth-fixed frame + dsstProp.addForceModel(zonal); + final EphemerisGenerator dsstEphemGen = dsstProp.getEphemerisGenerator(); + + // WHEN + // ---- + + // Statistics containers: compare on INC, RAAN and anomaly since that's where there is + // improvement brought by fixing 1104. The in-plane parameters (a, ex, ey) are almost equal + final StreamingStatistics dI = new StreamingStatistics(); + final StreamingStatistics dOm = new StreamingStatistics(); + final StreamingStatistics dLM = new StreamingStatistics(); + + // Propagate and get ephemeris + numProp.propagate(t0, tf); + dsstProp.propagate(t0, tf); + + final BoundedPropagator numEphem = numEphemGen.getGeneratedEphemeris(); + final BoundedPropagator dsstEphem = dsstEphemGen.getGeneratedEphemeris(); + + // Compare and fill statistics + for (double dt = 0; dt < duration; dt += step) { + + // Date + final AbsoluteDate t = t0.shiftedBy(dt); + + // Orbits and comparison + final CircularOrbit num = new CircularOrbit(numEphem.propagate(t).getOrbit()); + final CircularOrbit dsst = new CircularOrbit(dsstEphem.propagate(t).getOrbit()); + dI.addValue(FastMath.toDegrees(dsst.getI() - num.getI())); + dOm.addValue(FastMath.toDegrees(dsst.getRightAscensionOfAscendingNode() - num.getRightAscensionOfAscendingNode())); + dLM.addValue(FastMath.toDegrees(dsst.getLM() - num.getLM())); + } + + // THEN + // ---- + + // Optional: print the statistics + if (printResults) { + System.out.println("Inertial frame : " + inertialFrame.toString()); + System.out.println("Body-Fixed frame: " + bodyFixedFrame.toString()); + System.out.println("\ndi\n" + dI.toString()); + System.out.println("\ndΩ\n" + dOm.toString()); + System.out.println("\ndLM\n" + dLM.toString()); + } + + // Compare to reference + Assertions.assertEquals(diMax, FastMath.max(FastMath.abs(dI.getMax()), FastMath.abs(dI.getMin())), 1.e-8); + Assertions.assertEquals(dOmMax, FastMath.max(FastMath.abs(dOm.getMax()), FastMath.abs(dOm.getMin())), 1.e-6); + Assertions.assertEquals(dLmMax, FastMath.max(FastMath.abs(dLM.getMax()), FastMath.abs(dLM.getMin())), 1.e-5); } @BeforeEach diff --git a/src/test/java/org/orekit/time/AbsoluteDateTest.java b/src/test/java/org/orekit/time/AbsoluteDateTest.java index c26ee666a0..073879c0ab 100644 --- a/src/test/java/org/orekit/time/AbsoluteDateTest.java +++ b/src/test/java/org/orekit/time/AbsoluteDateTest.java @@ -1328,10 +1328,10 @@ public void testToString() { // test proleptic checkToString(new AbsoluteDate(123, 4, 5, 6, 7, 8.9, utc), "0123-04-05T06:07:08.900"); - // there is not way to produce valid RFC3339 for these cases + // there is no way to produce valid RFC3339 for these cases // I would rather print something useful than throw an exception // so these cases don't check for a correct answer, just an informative one - checkToString(new AbsoluteDate(-123, 4, 5, 6, 7, 8.9, utc), "-0123-04-05T06:07:08.900"); + checkToString(new AbsoluteDate(-123, 4, 5, 6, 7, 8.9, utc), "-123-04-05T06:07:08.900"); checkToString(new AbsoluteDate(-1230, 4, 5, 6, 7, 8.9, utc), "-1230-04-05T06:07:08.900"); // test far future checkToString(new AbsoluteDate(12300, 4, 5, 6, 7, 8.9, utc), "12300-04-05T06:07:08.900"); @@ -1624,6 +1624,34 @@ public void testShiftedByWithTimeUnit() { } } + @Test + public void testGetJulianDates() { + // GIVEN a reference date + final TimeScale utc = TimeScalesFactory.getUTC(); + + AbsoluteDate reference = new AbsoluteDate(2024, 7, 4, 13, 0, 0, utc); + AbsoluteDate referenceFromJDMethod = AbsoluteDate.createJDDate(2460496, .0416667 * Constants.JULIAN_DAY, utc); + AbsoluteDate referenceFromMJDMethod = AbsoluteDate.createMJDDate(60495, 0.54166670 * Constants.JULIAN_DAY, utc); + + // WHEN converting it to Julian Date or Modified Julian Date + double mjdDateDefaultData = reference.getMJD(); + double jdDateDefaultData = reference.getJD(); + double mjdDate = reference.getMJD(utc); + double jdDate = reference.getJD(utc); + + // THEN + // source : Time/Date Converter - HEASARC - NASA + Assertions.assertEquals(2460496.0416667, jdDateDefaultData, 1.0e-6); + Assertions.assertEquals(60495.54166670, mjdDateDefaultData, 1.0e-6); + Assertions.assertEquals(jdDate, jdDateDefaultData, 1.0e-6); + Assertions.assertEquals(mjdDateDefaultData, mjdDate); + + // Assert that static method are correct when creating date from JD or MJD + Assertions.assertTrue(reference.isCloseTo(referenceFromJDMethod, 1e-2)); + Assertions.assertTrue(reference.isCloseTo(referenceFromMJDMethod, 1e-2)); + } + + @BeforeEach public void setUp() { Utils.setDataRoot("regular-data"); @@ -1652,5 +1680,4 @@ public AbsoluteDate getDate() { return date; } } - } diff --git a/src/test/java/org/orekit/time/FieldAbsoluteDateTest.java b/src/test/java/org/orekit/time/FieldAbsoluteDateTest.java index ef7c36c111..d513a66012 100644 --- a/src/test/java/org/orekit/time/FieldAbsoluteDateTest.java +++ b/src/test/java/org/orekit/time/FieldAbsoluteDateTest.java @@ -28,20 +28,7 @@ import org.hamcrest.MatcherAssert; import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; -import org.hipparchus.analysis.differentiation.DSFactory; -import org.hipparchus.analysis.differentiation.DerivativeStructure; -import org.hipparchus.analysis.differentiation.FDSFactory; -import org.hipparchus.analysis.differentiation.FieldDerivativeStructure; -import org.hipparchus.analysis.differentiation.FieldGradient; -import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative1; -import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2; -import org.hipparchus.analysis.differentiation.Gradient; -import org.hipparchus.analysis.differentiation.GradientField; -import org.hipparchus.analysis.differentiation.SparseGradient; -import org.hipparchus.analysis.differentiation.UnivariateDerivative1; -import org.hipparchus.analysis.differentiation.UnivariateDerivative1Field; -import org.hipparchus.analysis.differentiation.UnivariateDerivative2; -import org.hipparchus.analysis.differentiation.UnivariateDerivative2Field; +import org.hipparchus.analysis.differentiation.*; import org.hipparchus.complex.Complex; import org.hipparchus.complex.ComplexField; import org.hipparchus.complex.FieldComplex; @@ -1767,4 +1754,85 @@ public FieldAbsoluteDate getPresentFieldAbsoluteDate() { } } + @Test + public void doTestGetJulianDatesWithBinar64() { + // GIVEN + final Field field = Binary64Field.getInstance(); + + // WHEN & THEN + doTestGetJulianDates(field); + } + + public > void doTestGetJulianDates(Field field) { + // GIVEN a reference date + final T one = field.getOne(); + final TimeScale utc = TimeScalesFactory.getUTC(); + + FieldAbsoluteDate reference = new FieldAbsoluteDate<>(field, 2024, 7, 4, 13, 0, 0, utc); + FieldAbsoluteDate referenceFromJDMethod = + FieldAbsoluteDate.createJDDate(2460496, one.multiply(0.0416667 * Constants.JULIAN_DAY), utc); + FieldAbsoluteDate referenceFromMJDMethod = + FieldAbsoluteDate.createMJDDate(60495, one.multiply(0.54166670 * Constants.JULIAN_DAY), utc); + + // WHEN converting it to Julian Date or Modified Julian Date + T mjdDateDefaultData = reference.getMJD(); + T jdDateDefaultData = reference.getJD(); + T mjdDate = reference.getMJD(utc); + T jdDate = reference.getJD(utc); + + // THEN + // source : Time/Date Converter - HEASARC - NASA + Assertions.assertEquals(2460496.0416667, jdDateDefaultData.getReal(), 1.0e-6); + Assertions.assertEquals(60495.54166670, mjdDateDefaultData.getReal(), 1.0e-6); + Assertions.assertEquals(jdDate, jdDateDefaultData); + Assertions.assertEquals(mjdDate, mjdDateDefaultData); + + // Assert that static method are correct when creating date from JD or MJD + Assertions.assertTrue(reference.isCloseTo(referenceFromJDMethod, 1e-2)); + Assertions.assertTrue(reference.isCloseTo(referenceFromMJDMethod, 1e-2)); + } + + @Test + void testGetJD() { + // GIVEN + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(UnivariateDerivative1Field.getInstance(), + date).shiftedBy(new UnivariateDerivative1(0., 1)); + // WHEN + final UnivariateDerivative1 jdField = fieldDate.getJD(); + // THEN + final double shift = 10.; + final FieldAbsoluteDate shiftedDate = fieldDate.shiftedBy(shift); + final double expectedJdDerivative = (shiftedDate.getJD().getReal() - jdField.getReal()) / shift; + Assertions.assertEquals(expectedJdDerivative, jdField.getFirstDerivative(), 1e-10); + } + + @Test + void testGetMJD() { + // GIVEN + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(UnivariateDerivative1Field.getInstance(), + date).shiftedBy(new UnivariateDerivative1(0., 1)); + // WHEN + final UnivariateDerivative1 mjdField = fieldDate.getMJD(); + // THEN + final double shift = 10.; + final FieldAbsoluteDate shiftedDate = fieldDate.shiftedBy(shift); + final double expectedMjdDerivative = (shiftedDate.getMJD().getReal() - mjdField.getReal()) / shift; + Assertions.assertEquals(expectedMjdDerivative, mjdField.getFirstDerivative(), 1e-10); + } + + @Test + void testToFUD2Field() { + // GIVEN + final Field field = Binary64Field.getInstance(); + final FieldAbsoluteDate date = FieldAbsoluteDate.getArbitraryEpoch(field); + // WHEN + final FieldAbsoluteDate> ud2Date = date.toFUD2Field(); + // THEN + Assertions.assertEquals(date.toAbsoluteDate(), ud2Date.toAbsoluteDate()); + final FieldUnivariateDerivative2 shift = ud2Date.durationFrom(date.toAbsoluteDate()); + Assertions.assertEquals(field.getOne(), shift.getFirstDerivative()); + Assertions.assertEquals(field.getZero(), shift.getSecondDerivative()); + } } diff --git a/src/test/java/org/orekit/utils/CartesianCovarianceUtilsTest.java b/src/test/java/org/orekit/utils/CartesianCovarianceUtilsTest.java new file mode 100644 index 0000000000..c779972275 --- /dev/null +++ b/src/test/java/org/orekit/utils/CartesianCovarianceUtilsTest.java @@ -0,0 +1,99 @@ +/* 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.utils; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.linear.MatrixUtils; +import org.hipparchus.linear.RealMatrix; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.orekit.frames.*; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.StateCovariance; +import org.orekit.time.AbsoluteDate; + +class CartesianCovarianceUtilsTest { + + @Test + void testConvertToLofType() { + // GIVEN + final Frame inputFrame = FramesFactory.getEME2000(); + final Frame outputFrame = FramesFactory.getGCRF(); + final RealMatrix matrix = buildSomeMatrix(); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + // WHEN + final RealMatrix actualMatrix = CartesianCovarianceUtils.changeReferenceFrame(inputFrame, matrix, date, + outputFrame); + // THEN + final Transform transform = inputFrame.getTransformTo(outputFrame, date); + final RealMatrix jacobianMatrix = MatrixUtils.createRealMatrix(transform.getPVJacobian()); + final RealMatrix expectedMatrix = jacobianMatrix.multiply(matrix).multiplyTransposed(jacobianMatrix); + Assertions.assertEquals(0., expectedMatrix.subtract(actualMatrix).getNorm1(), 1e-15); + } + + @ParameterizedTest + @EnumSource(LOFType.class) + void testConvertToLofType(final LOFType lofType) { + // GIVEN + final Vector3D position = new Vector3D(1.0, 2.0, 3.0); + final Vector3D velocity = new Vector3D(4., -0.5); + final RealMatrix matrix = buildSomeMatrix(); + // WHEN + final RealMatrix actualMatrix = CartesianCovarianceUtils.convertToLofType(position, velocity, matrix, lofType); + // THEN + final StateCovariance covariance = new StateCovariance(matrix, AbsoluteDate.ARBITRARY_EPOCH, + FramesFactory.getGCRF(), OrbitType.CARTESIAN, PositionAngleType.MEAN); + final CartesianOrbit orbit = new CartesianOrbit(new PVCoordinates(position, velocity), covariance.getFrame(), + covariance.getDate(), 1.); + Assertions.assertEquals(covariance.changeCovarianceFrame(orbit, lofType).getMatrix(), actualMatrix); + } + + @ParameterizedTest + @EnumSource(LOFType.class) + void testConvertFromLofType(final LOFType lofType) { + // GIVEN + final Vector3D position = new Vector3D(1.0, 2.0, 3.0); + final Vector3D velocity = new Vector3D(4., -0.5); + final RealMatrix expectedMatrix = buildSomeMatrix(); + // WHEN + final RealMatrix matrix = CartesianCovarianceUtils.convertToLofType(position, velocity, expectedMatrix, lofType); + // THEN + final RealMatrix actualMatrix = CartesianCovarianceUtils.convertFromLofType(lofType, matrix, position, velocity); + Assertions.assertEquals(0., expectedMatrix.subtract(actualMatrix).getNorm1(), 1e-14); + } + + private static RealMatrix buildSomeMatrix() { + final RealMatrix matrix = MatrixUtils.createRealIdentityMatrix(6); + final double entry = 0.4; + final int index2 = 2; + final int index3 = 3; + matrix.setEntry(index3, index3, entry); + matrix.setEntry(index2, index2, entry); + final int index4 = 4; + final int index5 = 5; + final double otherEntry = -0.2; + matrix.setEntry(index4, index5, otherEntry); + matrix.setEntry(index5, index4, otherEntry); + return matrix; + } + +} diff --git a/src/test/resources/potential/sha-format/README b/src/test/resources/potential/sha-format/README new file mode 100644 index 0000000000..3e625d3eea --- /dev/null +++ b/src/test/resources/potential/sha-format/README @@ -0,0 +1,13 @@ +As all other sibling gravity fields resource folders for test purposes, only ONE file in +this directory must match the default pattern for the format as defined in the class +org.orekit.forces.gravity.potential.GravityFieldFactory. All other files names must NOT +match the default pattern. + +This ensures that when we specify only the directory name in the data roots (i.e. +potential/egm-format, or potential/grgs-format, or potential/icgem-format, or +potential/shm-format), then we know for sure which file will be selected by the default +configuration. + +Loading the other files (the ones which do not match the default pattern) is done in test +files by explicitly setting a reader with the specific non-default name and adding the +reader to the factory by calling GravityFieldFactory.addPotentialCoefficientsReader. diff --git a/src/test/resources/potential/sha-format/corrupted-1_sha.grgm1200b_sigma_truncated_5x5 b/src/test/resources/potential/sha-format/corrupted-1_sha.grgm1200b_sigma_truncated_5x5 new file mode 100644 index 0000000000..e50e42192d --- /dev/null +++ b/src/test/resources/potential/sha-format/corrupted-1_sha.grgm1200b_sigma_truncated_5x5 @@ -0,0 +1,19 @@ + 4.9028001224452998E+12 1.7380000000000000E+06 5 5 + 2 0 -9.0882399518633699E-05 0.0000000000000000E+00 + 2 1 1.3062238115075299E-12 1.1689779521201799E-09 9.9675499999999999E-12 1.0340750000000000E-11 + 2 2 3.4673141521596401E-05 7.5683209135304897E-10 1.0325200000000000E-11 1.1211550000000001E-11 + 3 0 -3.1973502105869101E-06 0.0000000000000000E+00 3.2032999999999999E-12 0.0000000000000000E+00 + 3 1 2.6367971329793599E-05 5.4545234758460603E-06 3.4987499999999998E-12 3.8408500000000001E-12 + 3 2 1.4171462398237300E-05 4.8780422067827496E-06 4.7583000000000000E-12 5.0070999999999995E-12 + 3 3 1.2275110410764001E-05 -1.7743616719102799E-06 7.4795500000000000E-12 8.0859999999999993E-12 + 4 0 3.2347760491901400E-06 0.0000000000000000E+00 9.5010500000000002E-13 0.0000000000000000E+00 + 4 1 -6.0134827991045798E-06 1.6643224587048200E-06 9.4699500000000005E-13 1.0169700000000001E-12 + 4 2 -7.1161591609509197E-06 -6.7770575259451700E-06 1.2751000000000001E-12 1.3233049999999999E-12 + 4 3 -1.3498926164216201E-06 -1.3444987335842399E-05 1.7726999999999999E-12 1.9281999999999998E-12 + 4 4 -6.0069538669876603E-06 3.9263792903879803E-06 3.4831999999999999E-12 2.7990000000000001E-12 + 5 0 -2.2378588607372900E-07 0.0000000000000000E+00 3.6386999999999999E-13 0.0000000000000000E+00 + 5 1 -1.0115917201824100E-06 -4.1189129582567597E-06 3.7164499999999998E-13 4.0274500000000002E-13 + 5 2 4.3995192763167700E-06 1.0571405564135299E-06 4.5561499999999997E-13 4.6805499999999997E-13 + 5 3 4.6613359601990599E-07 8.6988718727567904E-06 6.6864999999999998E-13 7.0130500000000001E-13 + 5 4 2.7542657233402899E-06 6.7696050150517194E-08 1.3450750000000001E-12 1.0200800000000000E-12 + 5 5 3.1105527966439498E-06 -2.7544142076146700E-06 1.6638500000000000E-12 2.3480499999999999E-12 diff --git a/src/test/resources/potential/sha-format/corrupted-2_sha.grgm1200b_sigma_truncated_5x5 b/src/test/resources/potential/sha-format/corrupted-2_sha.grgm1200b_sigma_truncated_5x5 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/test/resources/potential/sha-format/corrupted-3_sha.grgm1200b_sigma_truncated_5x5 b/src/test/resources/potential/sha-format/corrupted-3_sha.grgm1200b_sigma_truncated_5x5 new file mode 100644 index 0000000000..3072ccf15f --- /dev/null +++ b/src/test/resources/potential/sha-format/corrupted-3_sha.grgm1200b_sigma_truncated_5x5 @@ -0,0 +1,19 @@ + 4.9028001224452998E+12 1.7380000000000000E+06 5 5 + 2 0 -9.0882399518633699E-05 0.0000000000000000E+00 3.5454000000000000E-11 0.0000000000000000E+00 + 2 1 1.3062238115075299E-12 1.1689779521201799E-09 9.9675499999999999E-12 1.0340750000000000E-11 + 2 2 3.4673141521596401E-05 7.5683209135304897E-10 1.0325200000000000E-11 1.1211550000000001E-11 + 3 0 -3.1973502105869101E-06 0.0000000000000000E+00 3.2032999999999999E-12 0.0000000000000000E+00 + 3 1 2.6367971329793599E-05 5.4545234758460603E-06 3.4987499999999998E-12 3.8408500000000001E-12 + 3 2 1.4171462398237300E-05 4.8780422067827496E-06 4.7583000000000000E-12 5.0070999999999995E-12 + 3 3 1.2275110410764001E-05 -1.7743616719102799E-06 7.4795500000000000E-12 8.0859999999999993E-12 + 4 0 3.2347760491901400E-06 0.0000000000000000E+00 9.5010500000000002E-13 0.0000000000000000E+00 + 4 1 -6.0134827991045798E-06 1.6643224587048200E-06 9.4699500000000005E-13 1.0169700000000001E-12 + 4 2 -7.1161591609509197E-06 -6.7770575259451700E-06 1.2751000000000001E-12 1.3233049999999999E-12 + 4 3 -1.3498926164216201E-06 -1.3444987335842399E-05 1.7726999999999999E-12 1.9281999999999998E-12 + 4 4 -6.0069538669876603E-06 3.9263792903879803E-06 3.4831999999999999E-12 2.7990000000000001E-12 + 5 0 -2.2378588607372900E-07 0.0000000000000000E+00 3.6386999999999999E-13 0.0000000000000000E+00 + 5 1 -1.0115917201824100E-06 -4.1189129582567597E-06 3.7164499999999998E-13 4.0274500000000002E-13 + 5 2 4.3995192763167700E-06 1.0571405564135299E-06 4.5561499999999997E-13 4.6805499999999997E-13 + 5 3 4.6613359601990599E-07 8.6988718727567904E-06 6.6864999999999998E-13 7.0130500000000001E-13 + 5 4 2.7542657233402899E-06 6.7696050150517194E-08 1.3450750000000001E-12 1.0200800000000000E-12 + 5 5 3.1105527966439498E-06 corrupted-data 1.6638500000000000E-12 2.3480499999999999E-12 diff --git a/src/test/resources/potential/sha-format/corrupted-4_sha.grgm1200b_sigma_truncated_5x5 b/src/test/resources/potential/sha-format/corrupted-4_sha.grgm1200b_sigma_truncated_5x5 new file mode 100644 index 0000000000..a57c78977f --- /dev/null +++ b/src/test/resources/potential/sha-format/corrupted-4_sha.grgm1200b_sigma_truncated_5x5 @@ -0,0 +1,4 @@ + 1.7380000000000000E+06 2 2 + 2 0 -9.0882399518633699E-05 0.0000000000000000E+00 3.5454000000000000E-11 0.0000000000000000E+00 + 2 1 1.3062238115075299E-12 1.1689779521201799E-09 9.9675499999999999E-12 1.0340750000000000E-11 + 2 2 3.4673141521596401E-05 7.5683209135304897E-10 1.0325200000000000E-11 1.1211550000000001E-11 diff --git a/src/test/resources/potential/sha-format/sha.grgm1200b_sigma_truncated_5x5 b/src/test/resources/potential/sha-format/sha.grgm1200b_sigma_truncated_5x5 new file mode 100644 index 0000000000..db38c8f720 --- /dev/null +++ b/src/test/resources/potential/sha-format/sha.grgm1200b_sigma_truncated_5x5 @@ -0,0 +1,19 @@ + 4.9028001224452998E+12 1.7380000000000000E+06 5 5 + 2 0 -9.0882399518633699E-05 0.0000000000000000E+00 3.5454000000000000E-11 0.0000000000000000E+00 + 2 1 1.3062238115075299E-12 1.1689779521201799E-09 9.9675499999999999E-12 1.0340750000000000E-11 + 2 2 3.4673141521596401E-05 7.5683209135304897E-10 1.0325200000000000E-11 1.1211550000000001E-11 + 3 0 -3.1973502105869101E-06 0.0000000000000000E+00 3.2032999999999999E-12 0.0000000000000000E+00 + 3 1 2.6367971329793599E-05 5.4545234758460603E-06 3.4987499999999998E-12 3.8408500000000001E-12 + 3 2 1.4171462398237300E-05 4.8780422067827496E-06 4.7583000000000000E-12 5.0070999999999995E-12 + 3 3 1.2275110410764001E-05 -1.7743616719102799E-06 7.4795500000000000E-12 8.0859999999999993E-12 + 4 0 3.2347760491901400E-06 0.0000000000000000E+00 9.5010500000000002E-13 0.0000000000000000E+00 + 4 1 -6.0134827991045798E-06 1.6643224587048200E-06 9.4699500000000005E-13 1.0169700000000001E-12 + 4 2 -7.1161591609509197E-06 -6.7770575259451700E-06 1.2751000000000001E-12 1.3233049999999999E-12 + 4 3 -1.3498926164216201E-06 -1.3444987335842399E-05 1.7726999999999999E-12 1.9281999999999998E-12 + 4 4 -6.0069538669876603E-06 3.9263792903879803E-06 3.4831999999999999E-12 2.7990000000000001E-12 + 5 0 -2.2378588607372900E-07 0.0000000000000000E+00 3.6386999999999999E-13 0.0000000000000000E+00 + 5 1 -1.0115917201824100E-06 -4.1189129582567597E-06 3.7164499999999998E-13 4.0274500000000002E-13 + 5 2 4.3995192763167700E-06 1.0571405564135299E-06 4.5561499999999997E-13 4.6805499999999997E-13 + 5 3 4.6613359601990599E-07 8.6988718727567904E-06 6.6864999999999998E-13 7.0130500000000001E-13 + 5 4 2.7542657233402899E-06 6.7696050150517194E-08 1.3450750000000001E-12 1.0200800000000000E-12 + 5 5 3.1105527966439498E-06 -2.7544142076146700E-06 1.6638500000000000E-12 2.3480499999999999E-12 diff --git a/src/test/resources/sp3/three-hours.sp3 b/src/test/resources/sp3/three-hours.sp3 new file mode 100644 index 0000000000..2b52519394 --- /dev/null +++ b/src/test/resources/sp3/three-hours.sp3 @@ -0,0 +1,131 @@ +#cP2015 5 5 0 0 0.00000000 36 u+U UNDEF FIT GFZ +## 1843 172800.00000000 300.00000000 57147 0.0000000000000 ++ 2 C01C02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ++ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ++ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ++ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ++ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +++ 10 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c M cc GPS ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 1.2500000 1.025000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* PCV:IGS08_1842 OL/AL:FES2004 NONE YN CLK:CoN ORB:CoN +/* GeoForschungsZentrum Potsdam +/* +/* +* 2015 5 5 0 0 0.00000000 +PC01 -32323.399959 27093.052654 -172.802215 -434.415658 +PC02 7290.092380 41532.108033 -96.440310 -916.586363 +* 2015 5 5 0 5 0.00000000 +PC01 -32322.871806 27093.768081 -146.920918 -434.406292 +PC02 7290.191776 41531.965688 -95.932918 -916.579690 +* 2015 5 5 0 10 0.00000000 +PC01 -32322.326189 27094.470619 -120.969101 -434.396780 +PC02 7290.285726 41531.822015 -95.379664 -916.573060 +* 2015 5 5 0 15 0.00000000 +PC01 -32321.763652 27095.159490 -94.959171 999999.999999 +PC02 7290.374117 41531.677069 -94.780803 -916.566535 +* 2015 5 5 0 20 0.00000000 +PC01 -32321.184765 27095.833931 -68.903562 999999.999999 +PC02 7290.456833 41531.530909 -94.136615 -916.559960 +* 2015 5 5 0 25 0.00000000 +PC01 -32320.590125 27096.493204 -42.814729 -434.368575 +PC02 7290.533764 41531.383595 -93.447398 -916.553461 +* 2015 5 5 0 30 0.00000000 +PC01 -32319.980350 27097.136591 -16.705143 -434.359035 +PC02 7290.604799 41531.235192 -92.713476 -916.546674 +* 2015 5 5 0 35 0.00000000 +PC01 -32319.356087 27097.763397 9.412713 -434.349600 +PC02 7290.669830 41531.085766 -91.935189 -916.539877 +* 2015 5 5 0 40 0.00000000 +PC01 -32318.718002 27098.372951 35.526353 -434.340167 +PC02 7290.728753 41530.935388 -91.112904 -916.533360 +* 2015 5 5 0 45 0.00000000 +PC01 -32318.066785 27098.964607 61.623293 -434.330669 +PC02 7290.781466 41530.784129 -90.247005 -916.526717 +* 2015 5 5 0 50 0.00000000 +PC01 -32317.403147 27099.537747 87.691057 -434.321257 +PC02 7290.827869 41530.632066 -89.337899 -916.520027 +* 2015 5 5 0 55 0.00000000 +PC01 -32316.727816 27100.091776 113.717182 -434.311875 +PC02 7290.867865 41530.479275 -88.386011 -916.513448 +* 2015 5 5 1 0 0.00000000 +PC01 -32316.041541 27100.626132 139.689223 -434.302478 +PC02 7290.901362 41530.325837 -87.391791 -916.506683 +* 2015 5 5 1 5 0.00000000 +PC01 -32315.345090 27101.140278 165.594764 -434.292891 +PC02 7290.928271 41530.171836 -86.355705 -916.500057 +* 2015 5 5 1 10 0.00000000 +PC01 -32314.639243 27101.633708 191.421419 -434.283567 +PC02 7290.948504 41530.017357 -85.278243 -916.493471 +* 2015 5 5 1 15 0.00000000 +PC01 -32313.924800 27102.105946 217.156837 -434.274283 +PC02 7290.961980 41529.862488 -84.159910 -916.486717 +* 2015 5 5 1 20 0.00000000 +PC01 -32313.202570 27102.556546 242.788715 -434.265065 +PC02 7290.968620 41529.707319 -83.001234 -916.480057 +* 2015 5 5 1 25 0.00000000 +PC01 -32312.473379 27102.985095 268.304794 -434.255572 +PC02 7290.968350 41529.551942 -81.802764 -916.473311 +* 2015 5 5 1 30 0.00000000 +PC01 -32311.738062 27103.391212 293.692875 -434.246132 +PC02 7290.961101 41529.396453 -80.565063 -916.466558 +* 2015 5 5 1 35 0.00000000 +PC01 -32310.997463 27103.774548 318.940815 -434.236693 +PC02 7290.946805 41529.240947 -79.288717 -916.459884 +* 2015 5 5 1 40 0.00000000 +PC01 -32310.252438 27104.134788 344.036542 -434.227293 +PC02 7290.925403 41529.085524 -77.974329 -916.453067 +* 2015 5 5 1 45 0.00000000 +PC01 -32309.503848 27104.471649 368.968053 -434.218028 +PC02 7290.896837 41528.930284 -76.622521 -916.446298 +* 2015 5 5 1 50 0.00000000 +PC01 -32308.752560 27104.784884 393.723426 -434.208794 +PC02 7290.861057 41528.775328 -75.233931 -916.439522 +* 2015 5 5 1 55 0.00000000 +PC01 -32307.999445 27105.074279 418.290819 -434.199341 +PC02 7290.818016 41528.620761 -73.809218 -916.432777 +* 2015 5 5 2 0 0.00000000 +PC01 -32307.245381 27105.339654 442.658485 -434.190045 +PC02 7290.767671 41528.466688 -72.349056 -916.426050 +* 2015 5 5 2 5 0.00000000 +PC01 -32306.491242 27105.580863 466.814766 -434.180555 +PC02 7290.709987 41528.313215 -70.854136 -916.419266 +* 2015 5 5 2 10 0.00000000 +PC01 -32305.737907 27105.797798 490.748109 -434.171120 +PC02 7290.644934 41528.160451 -69.325166 -916.412573 +* 2015 5 5 2 15 0.00000000 +PC01 -32304.986253 27105.990382 514.447065 -434.161810 +PC02 7290.572484 41528.008504 -67.762873 -916.405748 +* 2015 5 5 2 20 0.00000000 +PC01 -32304.237153 27106.158575 537.900299 -434.152478 +PC02 7290.492619 41527.857485 -66.167995 -916.398968 +* 2015 5 5 2 25 0.00000000 +PC01 -32303.491478 27106.302370 561.096589 -434.143172 +PC02 7290.405323 41527.707504 -64.541290 -916.392347 +* 2015 5 5 2 30 0.00000000 +PC01 -32302.750093 27106.421796 584.024840 -434.133936 +PC02 7290.310588 41527.558673 -62.883529 -916.385493 +* 2015 5 5 2 35 0.00000000 +PC01 -32302.013857 27106.516915 606.674082 -434.124577 +PC02 7290.208412 41527.411105 -61.195500 -916.378724 +* 2015 5 5 2 40 0.00000000 +PC01 -32301.283621 27106.587824 629.033479 -434.115199 +PC02 7290.098796 41527.264912 -59.478003 -916.372128 +* 2015 5 5 2 45 0.00000000 +PC01 -32300.560226 27106.634654 651.092332 -434.105784 +PC02 7289.981750 41527.120209 -57.731854 -916.365388 +* 2015 5 5 2 50 0.00000000 +PC01 -32299.844504 27106.657568 672.840087 -434.096354 +PC02 7289.857289 41526.977107 -55.957883 -916.358632 +* 2015 5 5 2 55 0.00000000 +PC01 -32299.137273 27106.656762 694.266336 -434.087019 +PC02 7289.725434 41526.835721 -54.156932 -916.351909 +EOF